From: Douglas Gregor Date: Thu, 16 Sep 2010 01:51:54 +0000 (+0000) Subject: Implement automatic bracket insertion for Objective-C class message X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9497a73ad0d54859edbf48beb93ebb19a7ae50c9;p=clang Implement automatic bracket insertion for Objective-C class message sends. These are far trickier than instance messages, because we typically have something like NSArray alloc] where it appears to be a declaration of a variable named "alloc" up until we see the ']' (or a ':'), and at that point we can't backtrace. So, we use a combination of syntactic and semantic disambiguation to treat this as a message send only when the type is an Objective-C type and it has the syntax of a class message send (which would otherwise be ill-formed). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@114057 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 479032c83f..11f4be7cc3 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1242,7 +1242,7 @@ private: void ParseStructDeclaration(DeclSpec &DS, FieldCallback &Callback); - bool isDeclarationSpecifier(); + bool isDeclarationSpecifier(bool DisambiguatingWithExpression = false); bool isTypeSpecifierQualifier(); bool isTypeQualifier() const; @@ -1257,7 +1257,7 @@ private: bool isDeclarationStatement() { if (getLang().CPlusPlus) return isCXXDeclarationStatement(); - return isDeclarationSpecifier(); + return isDeclarationSpecifier(true); } /// isSimpleDeclaration - Disambiguates between a declaration or an @@ -1267,9 +1267,13 @@ private: bool isSimpleDeclaration() { if (getLang().CPlusPlus) return isCXXSimpleDeclaration(); - return isDeclarationSpecifier(); + return isDeclarationSpecifier(true); } + /// \brief Determine whether we are currently at the start of an Objective-C + /// class message that appears to be missing the open bracket '['. + bool isStartOfObjCClassMessageMissingOpenBracket(); + /// \brief Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 45487e0fd1..ef495e33d8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2304,7 +2304,10 @@ bool Parser::isTypeSpecifierQualifier() { /// isDeclarationSpecifier() - Return true if the current token is part of a /// declaration specifier. -bool Parser::isDeclarationSpecifier() { +/// +/// \param DisambiguatingWithExpression True to indicate that the purpose of +/// this check is to disambiguate between an expression and a declaration. +bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { switch (Tok.getKind()) { default: return false; @@ -2322,6 +2325,16 @@ bool Parser::isDeclarationSpecifier() { return true; if (Tok.is(tok::identifier)) return false; + + // If we're in Objective-C and we have an Objective-C class type followed + // by an identifier and then either ':' or ']', in a place where an + // expression is permitted, then this is probably a class message send + // missing the initial '['. In this case, we won't consider this to be + // the start of a declaration. + if (DisambiguatingWithExpression && + isStartOfObjCClassMessageMissingOpenBracket()) + return false; + return isDeclarationSpecifier(); case tok::coloncolon: // ::foo::bar diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 33c7d67ce1..be468d537d 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -677,6 +677,35 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, break; } + // If we have an Objective-C class name followed by an identifier and + // either ':' or ']', this is an Objective-C class message send that's + // missing the opening '['. Recovery appropriately. + if (getLang().ObjC1 && Tok.is(tok::identifier)) { + const Token& Next = NextToken(); + if (Next.is(tok::colon) || Next.is(tok::r_square)) + if (ParsedType Type = Actions.getTypeName(II, ILoc, getCurScope())) + if (Type.get()->isObjCObjectOrInterfaceType()) { + // Fake up a Declarator to use with ActOnTypeName. + DeclSpec DS; + DS.SetRangeStart(ILoc); + DS.SetRangeEnd(ILoc); + const char *PrevSpec = 0; + unsigned DiagID; + DS.SetTypeSpecType(TST_typename, ILoc, PrevSpec, DiagID, Type); + + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + TypeResult Ty = Actions.ActOnTypeName(getCurScope(), + DeclaratorInfo); + if (Ty.isInvalid()) + break; + + Res = ParseObjCMessageExpressionBody(SourceLocation(), + SourceLocation(), + Ty.get(), 0); + break; + } + } + // Make sure to pass down the right value for isAddressOfOperand. if (isAddressOfOperand && isPostfixExpressionSuffixStart()) isAddressOfOperand = false; @@ -791,6 +820,32 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, Res = ParseCXXThis(); break; + case tok::annot_typename: + if (isStartOfObjCClassMessageMissingOpenBracket()) { + ParsedType Type = getTypeAnnotation(Tok); + + // Fake up a Declarator to use with ActOnTypeName. + DeclSpec DS; + DS.SetRangeStart(Tok.getLocation()); + DS.SetRangeEnd(Tok.getLastLoc()); + + const char *PrevSpec = 0; + unsigned DiagID; + DS.SetTypeSpecType(TST_typename, Tok.getLocation(), PrevSpec, DiagID, + Type); + + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); + if (Ty.isInvalid()) + break; + + ConsumeToken(); + Res = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), + Ty.get(), 0); + break; + } + // Fall through + case tok::kw_char: case tok::kw_wchar_t: case tok::kw_char16_t: @@ -806,8 +861,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw_void: case tok::kw_typename: case tok::kw_typeof: - case tok::kw___vector: - case tok::annot_typename: { + case tok::kw___vector: { if (!getLang().CPlusPlus) { Diag(Tok, diag::err_expected_expression); return ExprError(); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 46870d9528..a9cab9ec18 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1862,6 +1862,35 @@ bool Parser::isSimpleObjCMessageExpression() { GetLookAheadToken(2).is(tok::identifier); } +bool Parser::isStartOfObjCClassMessageMissingOpenBracket() { + if (!getLang().ObjC1 || !NextToken().is(tok::identifier) || + InMessageExpression) + return false; + + + ParsedType Type; + + if (Tok.is(tok::annot_typename)) + Type = getTypeAnnotation(Tok); + else if (Tok.is(tok::identifier)) + Type = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), + getCurScope()); + else + return false; + + if (!Type.get().isNull() && Type.get()->isObjCObjectOrInterfaceType()) { + const Token &AfterNext = GetLookAheadToken(2); + if (AfterNext.is(tok::colon) || AfterNext.is(tok::r_square)) { + if (Tok.is(tok::identifier)) + TryAnnotateTypeOrScopeToken(); + + return Tok.is(tok::annot_typename); + } + } + + return false; +} + /// objc-message-expr: /// '[' objc-receiver objc-message-args ']' /// diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index c22d99fa9d..085edb1281 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -821,6 +821,9 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { if (NextToken().is(tok::l_paren)) return TPResult::Ambiguous(); + if (isStartOfObjCClassMessageMissingOpenBracket()) + return TPResult::False(); + return TPResult::True(); // GNU typeof support. diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index caa2762d7f..e9295ebb24 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -710,7 +710,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, SourceLocation RBracLoc, MultiExprArg ArgsIn) { SourceLocation Loc = SuperLoc.isValid()? SuperLoc - : ReceiverTypeInfo->getTypeLoc().getLocalSourceRange().getBegin(); + : ReceiverTypeInfo->getTypeLoc().getSourceRange().getBegin(); if (LBracLoc.isInvalid()) { Diag(Loc, diag::err_missing_open_square_message_send) << FixItHint::CreateInsertion(Loc, "["); diff --git a/test/FixIt/fixit-objc-message.m b/test/FixIt/fixit-objc-message.m index 1969faab3b..1fef3cc56d 100644 --- a/test/FixIt/fixit-objc-message.m +++ b/test/FixIt/fixit-objc-message.m @@ -21,7 +21,9 @@ void f(A *a, int i, int j) { int array[17]; (void)array[a method1:5+2 second:+(3.14159)]]; - (A method2:5+2 second:3.14159]) + (A method2:5+2 second:3.14159]); + A method2:5+2 second:3.14159] + if (A method2:5+2 second:3.14159]) { } } @interface B : A