Handle 'instancetype' in ParseDeclarationSpecifiers.
authorDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 23:18:00 +0000 (23:18 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 23:18:00 +0000 (23:18 +0000)
...instead of as a special case in ParseObjCTypeName with lots of
duplicated logic. Besides being a nice refactoring, this also allows
"- (instancetype __nonnull)self" in addition to "- (nonnull instancetype)self".

rdar://problem/19924646

llvm-svn: 240188

clang/include/clang/Parse/Parser.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseObjc.cpp
clang/test/SemaObjC/nullability.m

index 1025dd7..7042a1e 100644 (file)
@@ -626,6 +626,16 @@ private:
                                 const char *&PrevSpec, unsigned &DiagID,
                                 bool &isInvalid);
 
+  /// Returns true if the current token is the identifier 'instancetype'.
+  ///
+  /// Should only be used in Objective-C language modes.
+  bool isObjCInstancetype() {
+    assert(getLangOpts().ObjC1);
+    if (!Ident_instancetype)
+      Ident_instancetype = PP.getIdentifierInfo("instancetype");
+    return Tok.getIdentifierInfo() == Ident_instancetype;
+  }
+
   /// TryKeywordIdentFallback - For compatibility with system headers using
   /// keywords as identifiers, attempt to convert the current token to an
   /// identifier and optionally disable the keyword for the remainder of the
@@ -1692,7 +1702,8 @@ private:
     DSC_trailing, // C++11 trailing-type-specifier in a trailing return type
     DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration
     DSC_top_level, // top-level/namespace declaration context
-    DSC_template_type_arg // template type argument context
+    DSC_template_type_arg, // template type argument context
+    DSC_objc_method_result, // ObjC method result context, enables 'instancetype'
   };
 
   /// Is this a context in which we are parsing just a type-specifier (or
@@ -1702,6 +1713,7 @@ private:
     case DSC_normal:
     case DSC_class:
     case DSC_top_level:
+    case DSC_objc_method_result:
       return false;
 
     case DSC_template_type_arg:
index 96555fc..1c52552 100644 (file)
@@ -2941,6 +2941,19 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       if (DS.isTypeAltiVecVector())
         goto DoneWithDeclSpec;
 
+      if (DSContext == DSC_objc_method_result && isObjCInstancetype()) {
+        ParsedType TypeRep = Actions.ActOnObjCInstanceType(Loc);
+        assert(TypeRep);
+        isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
+                                       DiagID, TypeRep, Policy);
+        if (isInvalid)
+          break;
+
+        DS.SetRangeEnd(Loc);
+        ConsumeToken();
+        continue;
+      }
+
       ParsedType TypeRep =
         Actions.getTypeName(*Tok.getIdentifierInfo(),
                             Tok.getLocation(), getCurScope());
index de34718..e4f7911 100644 (file)
@@ -1005,11 +1005,14 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
   ParseObjCTypeQualifierList(DS, context);
 
   ParsedType Ty;
-  if (isTypeSpecifierQualifier()) {
+  if (isTypeSpecifierQualifier() || isObjCInstancetype()) {
     // Parse an abstract declarator.
     DeclSpec declSpec(AttrFactory);
     declSpec.setObjCQualifiers(&DS);
-    ParseSpecifierQualifierList(declSpec);
+    DeclSpecContext dsContext = DSC_normal;
+    if (context == Declarator::ObjCResultContext)
+      dsContext = DSC_objc_method_result;
+    ParseSpecifierQualifierList(declSpec, AS_none, dsContext);
     declSpec.SetRangeEnd(Tok.getLocation());
     Declarator declarator(declSpec, context);
     ParseDeclarator(declarator);
@@ -1033,38 +1036,6 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
       if (context == Declarator::ObjCParameterContext)
         takeDeclAttributes(*paramAttrs, declarator);
     }
-  } else if (context == Declarator::ObjCResultContext &&
-             Tok.is(tok::identifier)) {
-    if (!Ident_instancetype)
-      Ident_instancetype = PP.getIdentifierInfo("instancetype");
-    
-    if (Tok.getIdentifierInfo() == Ident_instancetype) {
-      SourceLocation loc = ConsumeToken();
-      Ty = Actions.ActOnObjCInstanceType(loc);
-
-      // Synthesize an abstract declarator so we can use Sema::ActOnTypeName.
-      bool addedToDeclSpec = false;
-      const char *prevSpec;
-      unsigned diagID;
-      DeclSpec declSpec(AttrFactory);
-      declSpec.setObjCQualifiers(&DS);
-      declSpec.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
-                               Ty,
-                               Actions.getASTContext().getPrintingPolicy());
-      declSpec.SetRangeEnd(loc);
-      Declarator declarator(declSpec, context);
-
-      // Map a nullability specifier to a context-sensitive keyword attribute.
-      if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
-        addContextSensitiveTypeNullability(*this, declarator,
-                                           DS.getNullability(),
-                                           DS.getNullabilityLoc(),
-                                           addedToDeclSpec);
-
-      TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
-      if (!type.isInvalid())
-        Ty = type.get();
-    }
   }
 
   if (Tok.is(tok::r_paren))
index ca875b5..2cbdba1 100644 (file)
@@ -156,6 +156,10 @@ __attribute__((objc_root_class))
 - (nonnull instancetype)initWithBlah:(nonnull id)blah;
 - (nullable instancetype)returnMe;
 + (nullable instancetype)returnInstanceOfMe;
+
+- (nonnull instancetype __nullable)initWithBlah2:(nonnull id)blah; // expected-error {{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+- (instancetype __nullable)returnMe2;
++ (__nonnull instancetype)returnInstanceOfMe2;
 @end
 
 void test_instancetype(InitializableClass * __nonnull ic, id __nonnull object) {
@@ -163,6 +167,9 @@ void test_instancetype(InitializableClass * __nonnull ic, id __nonnull object) {
   ip = [InitializableClass returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
   ip = [InitializableClass returnInstanceOfMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nullable'}}
   ip = [object returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
+
+  ip = [ic returnMe2]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nullable'}}
+  ip = [InitializableClass returnInstanceOfMe2]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nonnull'}}
 }
 
 // Check null_resettable getters/setters.