From 55cf4bd859f8f8fdc8f1b7bbd07e7d3c12c284a2 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 13 Apr 2016 20:59:07 +0000 Subject: [PATCH] [SemaObjC] Properly handle mix between type arguments and protocols. Under certain conditions clang currently fails to properly diagnostic ObjectC parameter list when type args and protocols are mixed in the same list. This happens when the first item in the parameter list is a (1) protocol, (2) unknown type or (3) a list of protocols/unknown types up to the first type argument. Fix the problem to report the proper error, example: NSArray>> *foo = @[@"a"]; NSNumber *bar = foo[0]; NSLog(@"%@", bar); $ clang ... x.m:7:13: error: angle brackets contain both a type ('NSValue') and a protocol ('M') NSArray>> *foo = @[@"a"]; ~ ^ Differential Revision: http://reviews.llvm.org/D18997 rdar://problem/22204367 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266245 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 6 ++++ lib/Parse/ParseObjc.cpp | 47 ++++++++++++++++++++++++--- lib/Sema/SemaDeclObjC.cpp | 18 +++++++--- test/SemaObjC/parameterized_classes.m | 4 +++ 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d3e729b81d..abe18afb43 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7324,6 +7324,12 @@ public: 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. diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index c30ec1d7b7..708e2a7edc 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1693,11 +1693,18 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( 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 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( // 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 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( 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, diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index a9cc0d61be..58ee123688 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1303,6 +1303,16 @@ class ObjCTypeArgOrProtocolValidatorCCC : public CorrectionCandidateCallback { }; } // 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 @@ void Sema::actOnObjCTypeArgsOrProtocolQualifiers( // 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(); diff --git a/test/SemaObjC/parameterized_classes.m b/test/SemaObjC/parameterized_classes.m index 7f380a1054..1004fd3c4b 100644 --- a/test/SemaObjC/parameterized_classes.m +++ b/test/SemaObjC/parameterized_classes.m @@ -240,6 +240,10 @@ typedef PC4 typeArgs5; // expected-error{{unk // 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; -- 2.40.0