]> granicus.if.org Git - clang/commitdiff
Implement automatic bracket insertion for Objective-C class message
authorDouglas Gregor <dgregor@apple.com>
Thu, 16 Sep 2010 01:51:54 +0000 (01:51 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 16 Sep 2010 01:51:54 +0000 (01:51 +0000)
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

include/clang/Parse/Parser.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseObjc.cpp
lib/Parse/ParseTentative.cpp
lib/Sema/SemaExprObjC.cpp
test/FixIt/fixit-objc-message.m

index 479032c83f6984c78128cfb350ff2b1babb717eb..11f4be7cc34fe22b24af4bf22bde809180ea93b8 100644 (file)
@@ -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.
index 45487e0fd1228ffb841c0e76c332544d54ab0565..ef495e33d858ab0489a32eb7a4fb234a31139ce3 100644 (file)
@@ -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
index 33c7d67ce1458b765c4dd4e9bf99d152f698c669..be468d537d75e2dff871d9efc8ad3c0a3146c549 100644 (file)
@@ -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();
index 46870d952853d61f00061ae183c5e8888de68d8b..a9cab9ec1821bd94c65c809fb1b560abbb1b19f8 100644 (file)
@@ -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 ']'
 ///
index c22d99fa9d38aa6a2a622644bcc70a3f418930b1..085edb1281227330aed56d9059d36f213ad539e6 100644 (file)
@@ -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.
index caa2762d7fc03577ce6320d897cabdddfdb79f56..e9295ebb24454046dee51b19fcb5195baa1e3eef 100644 (file)
@@ -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, "[");
index 1969faab3b81fd0fbcfc72567a630771013b249f..1fef3cc56d5ed8a9bf6da04e760fcc2462e254a7 100644 (file)
@@ -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