diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Parse/ParseObjc.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 63 |
2 files changed, 71 insertions, 3 deletions
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 3dbecd09008..0f90b684685 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -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); 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; |