]> granicus.if.org Git - clang/commitdiff
Warn when an intended Objective-C specialization was actually a useless protocol...
authorDouglas Gregor <dgregor@apple.com>
Tue, 7 Jul 2015 03:58:28 +0000 (03:58 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 7 Jul 2015 03:58:28 +0000 (03:58 +0000)
Warn in cases where one has provided redundant protocol qualification
that might be a typo for a specialization, e.g., NSArray<NSObject>,
which is pointless (NSArray declares that it conforms to NSObject) and
is likely to be a typo for NSArray<NSObject *>, i.e., an array of
NSObject pointers. This warning is very narrow, only applying when the
base type being qualified is parameterized, has the same number of
parameters as their are protocols listed, all of the names can also
refer to types (including Objective-C class types, of course), and at
least one of those types is an Objective-C class (making this a typo
for a missing '*'). The limitations are partly for performance reasons
(we don't want to do redundant name lookup unless we really need to),
and because we want the warning to apply in very limited cases to
limit false positives.

Part of rdar://problem/6294649.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@241547 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Parse/ParseObjc.cpp
lib/Sema/SemaDeclObjC.cpp
test/SemaObjC/parameterized_classes_subst.m

index 85796cc0d32575cb163643651214ccf7eeceaf12..4ecd5d5e184256d898443e5928ab81f9df058ea0 100644 (file)
@@ -280,6 +280,7 @@ def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">;
 def BadFunctionCast : DiagGroup<"bad-function-cast">;
 def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
 def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
+def ObjCProtocolQualifiers : DiagGroup<"objc-protocol-qualifiers">;
 def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">;
 def ObjCDesignatedInit : DiagGroup<"objc-designated-initializers">;
 def ObjCRetainBlockProperty : DiagGroup<"objc-noncopy-retain-block-property">;
index 8032b0b8b4066bb2ccfb18471ed9b5664a4c192b..99400985d7841fd5bdf63a957eae4712e9a0288d 100644 (file)
@@ -7824,4 +7824,8 @@ def err_objc_type_arg_not_id_compatible : Error<
 def err_objc_type_arg_does_not_match_bound : Error<
   "type argument %0 does not satisfy the bound (%1) of type parameter %2">;
 
+def warn_objc_redundant_qualified_class_type : Warning<
+  "parameterized class %0 already conforms to the protocols listed; did you "
+  "forget a '*'?">, InGroup<ObjCProtocolQualifiers>;
+
 } // end of sema component.
index 8340739b649b36d9b4def815385f7d7f96d5a3f4..fb9eb8ff5af88e97f239c0f0eec9366a7762332d 100644 (file)
@@ -1274,6 +1274,7 @@ private:
   /// Objective-C object or object pointer type, which may be either
   /// type arguments or protocol qualifiers.
   void parseObjCTypeArgsOrProtocolQualifiers(
+         ParsedType baseType,
          SourceLocation &typeArgsLAngleLoc,
          SmallVectorImpl<ParsedType> &typeArgs,
          SourceLocation &typeArgsRAngleLoc,
@@ -1287,6 +1288,7 @@ private:
   /// Parse either Objective-C type arguments or protocol qualifiers; if the
   /// former, also parse protocol qualifiers afterward.
   void parseObjCTypeArgsAndProtocolQualifiers(
+         ParsedType baseType,
          SourceLocation &typeArgsLAngleLoc,
          SmallVectorImpl<ParsedType> &typeArgs,
          SourceLocation &typeArgsRAngleLoc,
index a2f1197f1559a4162892c02ce654d0f706c87526..edf47e233c5618be99d1e3f9528fe24d18cf99ba 100644 (file)
@@ -7194,6 +7194,7 @@ public:
   /// arguments, as appropriate.
   void actOnObjCTypeArgsOrProtocolQualifiers(
          Scope *S,
+         ParsedType baseType,
          SourceLocation lAngleLoc,
          ArrayRef<IdentifierInfo *> identifiers,
          ArrayRef<SourceLocation> identifierLocs,
index 3dbecd0900864dbb62233651e7858c719352a5ad..0f90b6846855194bd78478f9f39e0341808bcbd3 100644 (file)
@@ -319,7 +319,8 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
 
     // Type arguments for the superclass or protocol conformances.
     if (Tok.is(tok::less)) {
-      parseObjCTypeArgsOrProtocolQualifiers(typeArgsLAngleLoc,
+      parseObjCTypeArgsOrProtocolQualifiers(ParsedType(),
+                                            typeArgsLAngleLoc,
                                             typeArgs,
                                             typeArgsRAngleLoc,
                                             LAngleLoc,
@@ -1589,6 +1590,7 @@ TypeResult Parser::parseObjCProtocolQualifierType(SourceLocation &rAngleLoc) {
 ///     '<' type-name '...'[opt] (',' type-name '...'[opt])* '>'
 ///
 void Parser::parseObjCTypeArgsOrProtocolQualifiers(
+       ParsedType baseType,
        SourceLocation &typeArgsLAngleLoc,
        SmallVectorImpl<ParsedType> &typeArgs,
        SourceLocation &typeArgsRAngleLoc,
@@ -1648,6 +1650,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers(
 
     // Let Sema figure out what we parsed.
     Actions.actOnObjCTypeArgsOrProtocolQualifiers(getCurScope(),
+                                                  baseType,
                                                   lAngleLoc,
                                                   identifiers,
                                                   identifierLocs,
@@ -1723,6 +1726,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers(
 }
 
 void Parser::parseObjCTypeArgsAndProtocolQualifiers(
+       ParsedType baseType,
        SourceLocation &typeArgsLAngleLoc,
        SmallVectorImpl<ParsedType> &typeArgs,
        SourceLocation &typeArgsRAngleLoc,
@@ -1734,7 +1738,8 @@ void Parser::parseObjCTypeArgsAndProtocolQualifiers(
   assert(Tok.is(tok::less));
 
   // Parse the first angle-bracket-delimited clause.
-  parseObjCTypeArgsOrProtocolQualifiers(typeArgsLAngleLoc,
+  parseObjCTypeArgsOrProtocolQualifiers(baseType,
+                                        typeArgsLAngleLoc,
                                         typeArgs,
                                         typeArgsRAngleLoc,
                                         protocolLAngleLoc,
@@ -1786,7 +1791,7 @@ TypeResult Parser::parseObjCTypeArgsAndProtocolQualifiers(
   SourceLocation protocolRAngleLoc;
 
   // Parse type arguments and protocol qualifiers.
-  parseObjCTypeArgsAndProtocolQualifiers(typeArgsLAngleLoc, typeArgs,
+  parseObjCTypeArgsAndProtocolQualifiers(type, typeArgsLAngleLoc, typeArgs,
                                          typeArgsRAngleLoc, protocolLAngleLoc,
                                          protocols, protocolLocs,
                                          protocolRAngleLoc, consumeLastToken);
index 48aa2467d240bc7b317f83ce92b7da8baacff55e..74fbec23545c926492cea5cfe0d8cd92c41e8cb3 100644 (file)
@@ -1220,6 +1220,7 @@ class ObjCTypeArgOrProtocolValidatorCCC : public CorrectionCandidateCallback {
 
 void Sema::actOnObjCTypeArgsOrProtocolQualifiers(
        Scope *S,
+       ParsedType baseType,
        SourceLocation lAngleLoc,
        ArrayRef<IdentifierInfo *> identifiers,
        ArrayRef<SourceLocation> identifierLocs,
@@ -1237,6 +1238,27 @@ void Sema::actOnObjCTypeArgsOrProtocolQualifiers(
   auto resolvedAsProtocols = [&] {
     assert(numProtocolsResolved == identifiers.size() && "Unresolved protocols");
     
+    // Determine whether the base type is a parameterized class, in
+    // which case we want to warn about typos such as
+    // "NSArray<NSObject>" (that should be NSArray<NSObject *>).
+    ObjCInterfaceDecl *baseClass = nullptr;
+    QualType base = GetTypeFromParser(baseType, nullptr);
+    bool allAreTypeNames = false;
+    SourceLocation firstClassNameLoc;
+    if (!base.isNull()) {
+      if (const auto *objcObjectType = base->getAs<ObjCObjectType>()) {
+        baseClass = objcObjectType->getInterface();
+        if (baseClass) {
+          if (auto typeParams = baseClass->getTypeParamList()) {
+            if (typeParams->size() == numProtocolsResolved) {
+              // Note that we should be looking for type names, too.
+              allAreTypeNames = true;
+            }
+          }
+        }
+      }
+    }
+
     for (unsigned i = 0, n = protocols.size(); i != n; ++i) {
       ObjCProtocolDecl *&proto 
         = reinterpret_cast<ObjCProtocolDecl *&>(protocols[i]);
@@ -1261,6 +1283,47 @@ void Sema::actOnObjCTypeArgsOrProtocolQualifiers(
         Diag(forwardDecl->getLocation(), diag::note_protocol_decl_undefined)
           << forwardDecl;
       }
+
+      // If everything this far has been a type name (and we care
+      // about such things), check whether this name refers to a type
+      // as well.
+      if (allAreTypeNames) {
+        if (auto *decl = LookupSingleName(S, identifiers[i], identifierLocs[i],
+                                          LookupOrdinaryName)) {
+          if (isa<ObjCInterfaceDecl>(decl)) {
+            if (firstClassNameLoc.isInvalid())
+              firstClassNameLoc = identifierLocs[i];
+          } else if (!isa<TypeDecl>(decl)) {
+            // Not a type.
+            allAreTypeNames = false;
+          }
+        } else {
+          allAreTypeNames = false;
+        }
+      }
+    }
+    
+    // All of the protocols listed also have type names, and at least
+    // one is an Objective-C class name. Check whether all of the
+    // protocol conformances are declared by the base class itself, in
+    // which case we warn.
+    if (allAreTypeNames && firstClassNameLoc.isValid()) {
+      llvm::SmallPtrSet<ObjCProtocolDecl*, 8> knownProtocols;
+      Context.CollectInheritedProtocols(baseClass, knownProtocols);
+      bool allProtocolsDeclared = true;
+      for (auto proto : protocols) {
+        if (knownProtocols.count(static_cast<ObjCProtocolDecl *>(proto)) == 0) {
+          allProtocolsDeclared = false;
+          break;
+        }
+      }
+
+      if (allProtocolsDeclared) {
+        Diag(firstClassNameLoc, diag::warn_objc_redundant_qualified_class_type)
+          << baseClass->getDeclName() << SourceRange(lAngleLoc, rAngleLoc)
+          << FixItHint::CreateInsertion(
+               PP.getLocForEndOfToken(firstClassNameLoc), " *");
+      }
     }
 
     protocolLAngleLoc = lAngleLoc;
index 979053bfa787368915c30f6380c8e478f2af64b5..6804bdc7955f3691823fb3b3b7e8d52004e85422 100644 (file)
@@ -3,8 +3,11 @@
 // Test the substitution of type arguments for type parameters when
 // using parameterized classes in Objective-C.
 
+@protocol NSObject
+@end
+
 __attribute__((objc_root_class))
-@interface NSObject
+@interface NSObject <NSObject>
 + (instancetype)alloc;
 - (instancetype)init;
 @end
@@ -376,3 +379,8 @@ void test_ternary_operator(NSArray<NSString *> *stringArray,
   ip = [super array]; // expected-warning{{from 'NSArray<NSString *> *'}}
 }
 @end
+
+// --------------------------------------------------------------------------
+// warning about likely protocol/class name typos.
+// --------------------------------------------------------------------------
+typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}}