diff options
author | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:58:28 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:58:28 +0000 |
commit | 10dc9d80cbb47675be0c6ea8481e12ec197e1637 (patch) | |
tree | df1105b48af8c9755bf006c9c45ddcab262cbf6c /clang/lib/Sema | |
parent | 5054cb04673f883cf7f2f2432fc3d0000b69f6fe (diff) | |
download | bcm5719-llvm-10dc9d80cbb47675be0c6ea8481e12ec197e1637.tar.gz bcm5719-llvm-10dc9d80cbb47675be0c6ea8481e12ec197e1637.zip |
Warn when an intended Objective-C specialization was actually a useless protocol qualification.
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.
llvm-svn: 241547
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 48aa2467d24..74fbec23545 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -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; |