Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -7324,6 +7324,12 @@ ArrayRef ProtocolId, SmallVectorImpl &Protocols); + void DiagnoseTypeArgsAndProtocols(IdentifierInfo *ProtocolId, + SourceLocation ProtocolLoc, + IdentifierInfo *TypeArgId, + SourceLocation TypeArgLoc, + bool SelectProtocolFirst = false); + /// Given a list of identifiers (and their locations), resolve the /// names to either Objective-C protocol qualifiers or type /// arguments, as appropriate. Index: lib/Parse/ParseObjc.cpp =================================================================== --- lib/Parse/ParseObjc.cpp +++ lib/Parse/ParseObjc.cpp @@ -1693,11 +1693,18 @@ return; } - // We syntactically matched a type argument, so commit to parsing - // type arguments. + // We parsed an identifier list but stumbled into non single identifiers, this + // means we might (a) check that what we already parsed is a legitimate type + // (not a protocol or unknown type) and (b) parse the remaining ones, which + // must all be type args. // Convert the identifiers into type arguments. bool invalid = false; + IdentifierInfo *foundProtocolId = nullptr, *foundValidTypeId = nullptr; + SourceLocation foundProtocolSrcLoc, foundValidTypeSrcLoc; + SmallVector unknownTypeArgs; + SmallVector unknownTypeArgsLoc; + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { ParsedType typeArg = Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope()); @@ -1711,17 +1718,32 @@ // Form a declarator to turn this into a type. Declarator D(DS, Declarator::TypeNameContext); TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D); - if (fullTypeArg.isUsable()) + if (fullTypeArg.isUsable()) { typeArgs.push_back(fullTypeArg.get()); - else + if (!foundValidTypeId) { + foundValidTypeId = identifiers[i]; + foundValidTypeSrcLoc = identifierLocs[i]; + } + } else { invalid = true; + unknownTypeArgs.push_back(identifiers[i]); + unknownTypeArgsLoc.push_back(identifierLocs[i]); + } } else { invalid = true; + if (!Actions.LookupProtocol(identifiers[i], identifierLocs[i])) { + unknownTypeArgs.push_back(identifiers[i]); + unknownTypeArgsLoc.push_back(identifierLocs[i]); + } else if (!foundProtocolId) { + foundProtocolId = identifiers[i]; + foundProtocolSrcLoc = identifierLocs[i]; + } } } // Continue parsing type-names. do { + Token CurTypeTok = Tok; TypeResult typeArg = ParseTypeName(); // Consume the '...' for a pack expansion. @@ -1733,11 +1755,28 @@ if (typeArg.isUsable()) { typeArgs.push_back(typeArg.get()); + if (!foundValidTypeId) { + foundValidTypeId = CurTypeTok.getIdentifierInfo(); + foundValidTypeSrcLoc = CurTypeTok.getLocation(); + } } else { invalid = true; } } while (TryConsumeToken(tok::comma)); + // Diagnose the mix between type args and protocols. + if (foundProtocolId && foundValidTypeId) + Actions.DiagnoseTypeArgsAndProtocols(foundProtocolId, foundProtocolSrcLoc, + foundValidTypeId, + foundValidTypeSrcLoc); + + // Diagnose unknown arg types. + ParsedType T; + if (unknownTypeArgs.size()) + for (unsigned i = 0, e = unknownTypeArgsLoc.size(); i < e; ++i) + Actions.DiagnoseUnknownTypeName(unknownTypeArgs[i], unknownTypeArgsLoc[i], + getCurScope(), nullptr, T); + // Parse the closing '>'. SourceLocation rAngleLoc; (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, Index: lib/Sema/SemaDeclObjC.cpp =================================================================== --- lib/Sema/SemaDeclObjC.cpp +++ lib/Sema/SemaDeclObjC.cpp @@ -1303,6 +1303,16 @@ }; } // end anonymous namespace +void Sema::DiagnoseTypeArgsAndProtocols(IdentifierInfo *ProtocolId, + SourceLocation ProtocolLoc, + IdentifierInfo *TypeArgId, + SourceLocation TypeArgLoc, + bool SelectProtocolFirst) { + Diag(TypeArgLoc, diag::err_objc_type_args_and_protocols) + << SelectProtocolFirst << TypeArgId << ProtocolId + << SourceRange(ProtocolLoc); +} + void Sema::actOnObjCTypeArgsOrProtocolQualifiers( Scope *S, ParsedType baseType, @@ -1570,11 +1580,9 @@ // We have a conflict: some names refer to protocols and others // refer to types. - Diag(identifierLocs[i], diag::err_objc_type_args_and_protocols) - << (protocols[i] != nullptr) - << identifiers[i] - << identifiers[0] - << SourceRange(identifierLocs[0]); + DiagnoseTypeArgsAndProtocols(identifiers[0], identifierLocs[0], + identifiers[i], identifierLocs[i], + protocols[i] != nullptr); protocols.clear(); typeArgs.clear(); Index: test/SemaObjC/parameterized_classes.m =================================================================== --- test/SemaObjC/parameterized_classes.m +++ test/SemaObjC/parameterized_classes.m @@ -240,6 +240,10 @@ // Type/protocol conflict. typedef PC4 typeArgsProtocolQualsConflict1; // expected-error{{angle brackets contain both a type ('ObjCStringRef') and a protocol ('NSCopying')}} +typedef PC4 typeArgsProtocolQualsConflict2; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} +typedef PC4 typeArgsProtocolQualsConflict3; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} expected-error{{unknown type name 'UnknownType'}} +typedef PC4 typeArgsProtocolQualsConflict4; // expected-error{{unknown type name 'UnknownType'}} +typedef PC4 typeArgsProtocolQualsConflict5; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} // Handling the '>>' in type argument lists. typedef PC4, NSObject *, id> typeArgs6;