From 943c4404555120ccf87d2f23807104e7cbf04439 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 30 Jul 2012 21:30:52 +0000 Subject: [PATCH] Improvements to vexing-parse warnings. Make the no-parameters case more accurate by asking the parser whether there was an ambiguity rather than trying to reverse-engineer it from the DeclSpec. Make the with-parameters case have better diagnostics by using semantic information to drive the warning, improving the diagnostics and adding a fixit. Patch by Nikola Smiljanic. Some minor changes by me to suppress diagnostics for declarations of the form 'T (*x)(...)', which seem to have a very high false positive rate, and to reduce indentation in 'warnAboutAmbiguousFunction'. llvm-svn: 160998 --- .../clang/Basic/DiagnosticParseKinds.td | 3 - .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Parse/Parser.h | 7 +- clang/include/clang/Sema/DeclSpec.h | 4 + clang/lib/Parse/ParseDecl.cpp | 18 ++- clang/lib/Parse/ParseExpr.cpp | 2 +- clang/lib/Parse/ParseExprCXX.cpp | 3 +- clang/lib/Parse/ParseTentative.cpp | 24 ++-- clang/lib/Sema/DeclSpec.cpp | 2 + clang/lib/Sema/SemaDecl.cpp | 58 +--------- clang/lib/Sema/SemaType.cpp | 103 +++++++++++++++++- clang/test/FixIt/fixit-vexing-parse.cpp | 51 ++++++--- clang/test/Parser/objcxx11-attributes.mm | 3 +- clang/test/SemaCXX/condition.cpp | 2 +- clang/test/SemaCXX/dcl_ambig_res.cpp | 6 +- clang/test/SemaCXX/decl-expr-ambiguity.cpp | 8 +- clang/test/SemaObjCXX/protocol-lookup.mm | 2 +- .../test/SemaTemplate/typename-specifier.cpp | 4 +- 18 files changed, 186 insertions(+), 119 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 31b8077ef3a5..8cb82fd4a914 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -415,9 +415,6 @@ def err_expected_init_in_condition_lparen : Error< "variable declaration in condition cannot have a parenthesized initializer">; def err_extraneous_rparen_in_condition : Error< "extraneous ')' after condition, expected a statement">; -def warn_parens_disambiguated_as_function_decl : Warning< - "parentheses were disambiguated as a function declarator">, - InGroup; def warn_dangling_else : Warning< "add explicit braces to avoid dangling else">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 263a4aef946e..4df26cfa55fe 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -158,6 +158,11 @@ def warn_redefinition_in_param_list : Warning< def warn_empty_parens_are_function_decl : Warning< "empty parentheses interpreted as a function declaration">, InGroup; +def warn_parens_disambiguated_as_function_declaration : Warning< + "parentheses were disambiguated as a function declaration">, + InGroup; +def note_additional_parens_for_variable_declaration : Note< + "add a pair of parentheses to declare a variable">; def note_empty_parens_function_call : Note< "change this ',' to a ';' to call %0">; def note_empty_parens_default_ctor : Note< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index ca380463fb60..e874e382ced5 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1643,11 +1643,11 @@ private: /// isCXXFunctionDeclarator - Disambiguates between a function declarator or /// a constructor-style initializer, when parsing declaration statements. /// Returns true for function declarator and false for constructor-style - /// initializer. If 'warnIfAmbiguous' is true a warning will be emitted to - /// indicate that the parens were disambiguated as function declarator. + /// initializer. Sets 'IsAmbiguous' to true to indicate that this declaration + /// might be a constructor-style initializer. /// If during the disambiguation process a parsing error is encountered, /// the function returns true to let the declaration parsing code handle it. - bool isCXXFunctionDeclarator(bool warnIfAmbiguous); + bool isCXXFunctionDeclarator(bool *IsAmbiguous = 0); /// isCXXConditionDeclaration - Disambiguates between a declaration or an /// expression for a condition of a if/switch/while/for statement. @@ -1903,6 +1903,7 @@ private: void ParseFunctionDeclarator(Declarator &D, ParsedAttributes &attrs, BalancedDelimiterTracker &Tracker, + bool IsAmbiguous, bool RequiresArg = false); bool isFunctionDeclaratorIdentifierList(); void ParseFunctionDeclaratorIdentifierList( diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 00d42a814a75..48f7d80167ba 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1104,6 +1104,9 @@ struct DeclaratorChunk { /// contains the location of the ellipsis. unsigned isVariadic : 1; + /// Can this declaration be a constructor-style initializer? + unsigned isAmbiguous : 1; + /// \brief Whether the ref-qualifier (if any) is an lvalue reference. /// Otherwise, it's an rvalue reference. unsigned RefQualifierIsLValueRef : 1; @@ -1356,6 +1359,7 @@ struct DeclaratorChunk { /// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function. /// "TheDeclarator" is the declarator that this will be added to. static DeclaratorChunk getFunction(bool hasProto, bool isVariadic, + bool isAmbiguous, SourceLocation EllipsisLoc, ParamInfo *ArgInfo, unsigned NumArgs, unsigned TypeQuals, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index ea514d382682..328345413f2b 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4318,17 +4318,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // The paren may be part of a C++ direct initializer, eg. "int x(1);". // In such a case, check if we actually have a function declarator; if it // is not, the declarator has been fully parsed. - if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) { - // When not in file scope, warn for ambiguous function declarators, just - // in case the author intended it as a variable definition. - bool warnIfAmbiguous = D.getContext() != Declarator::FileContext; - if (!isCXXFunctionDeclarator(warnIfAmbiguous)) - break; - } + bool IsAmbiguous = false; + if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit() && + !isCXXFunctionDeclarator(&IsAmbiguous)) + break; ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); - ParseFunctionDeclarator(D, attrs, T); + ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -4445,7 +4442,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope); - ParseFunctionDeclarator(D, attrs, T, RequiresArg); + ParseFunctionDeclarator(D, attrs, T, false, RequiresArg); PrototypeScope.Exit(); } @@ -4471,6 +4468,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { void Parser::ParseFunctionDeclarator(Declarator &D, ParsedAttributes &FirstArgAttrs, BalancedDelimiterTracker &Tracker, + bool IsAmbiguous, bool RequiresArg) { assert(getCurScope()->isFunctionPrototypeScope() && "Should call from a Function scope"); @@ -4588,7 +4586,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // Remember that we parsed a function type, and remember the attributes. D.AddTypeInfo(DeclaratorChunk::getFunction(HasProto, /*isVariadic=*/EllipsisLoc.isValid(), - EllipsisLoc, + IsAmbiguous, EllipsisLoc, ParamInfo.data(), ParamInfo.size(), DS.getTypeQualifiers(), RefQualifierIsLValueRef, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 7f268b523df2..8d4668b95481 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2415,7 +2415,7 @@ ExprResult Parser::ParseBlockLiteralExpression() { } else { // Otherwise, pretend we saw (void). ParsedAttributes attrs(AttrFactory); - ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, + ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, false, SourceLocation(), 0, 0, 0, true, SourceLocation(), diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 39ef38fdc0c2..b393d1ff67ce 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -804,7 +804,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true, /*isVariadic=*/EllipsisLoc.isValid(), - EllipsisLoc, + /*isAmbiguous=*/false, EllipsisLoc, ParamInfo.data(), ParamInfo.size(), DS.getTypeQualifiers(), /*RefQualifierIsLValueRef=*/true, @@ -849,6 +849,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( ParsedAttributes Attr(AttrFactory); D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true, /*isVariadic=*/false, + /*isAmbiguous=*/false, /*EllipsisLoc=*/SourceLocation(), /*Params=*/0, /*NumParams=*/0, /*TypeQuals=*/0, diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 8cf1c29078c8..1a4df4791b5f 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -671,7 +671,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, // initializer that follows the declarator. Note that ctor-style // initializers are not possible in contexts where abstract declarators // are allowed. - if (!mayBeAbstract && !isCXXFunctionDeclarator(false/*warnIfAmbiguous*/)) + if (!mayBeAbstract && !isCXXFunctionDeclarator()) break; // direct-declarator '(' parameter-declaration-clause ')' @@ -1267,7 +1267,7 @@ Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) { /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// -bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { +bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { // C++ 8.2p1: // The ambiguity arising from the similarity between a function-style cast and @@ -1304,23 +1304,13 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { } } - SourceLocation TPLoc = Tok.getLocation(); PA.Revert(); + if (IsAmbiguous && TPR == TPResult::Ambiguous()) + *IsAmbiguous = true; + // In case of an error, let the declaration parsing code handle it. - if (TPR == TPResult::Error()) - return true; - - if (TPR == TPResult::Ambiguous()) { - // Function declarator has precedence over constructor-style initializer. - // Emit a warning just in case the author intended a variable definition. - if (warnIfAmbiguous) - Diag(Tok, diag::warn_parens_disambiguated_as_function_decl) - << SourceRange(Tok.getLocation(), TPLoc); - return true; - } - - return TPR == TPResult::True(); + return TPR != TPResult::False(); } /// parameter-declaration-clause: @@ -1344,7 +1334,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { if (Tok.is(tok::r_paren)) - return TPResult::True(); + return TPResult::Ambiguous(); // parameter-declaration-list[opt] '...'[opt] // parameter-declaration-list ',' '...' diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index f3ec5656ea33..d12ca7839095 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -145,6 +145,7 @@ CXXScopeSpec::getWithLocInContext(ASTContext &Context) const { /// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function. /// "TheDeclarator" is the declarator that this will be added to. DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, + bool isAmbiguous, SourceLocation EllipsisLoc, ParamInfo *ArgInfo, unsigned NumArgs, @@ -173,6 +174,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, I.Fun.AttrList = 0; I.Fun.hasPrototype = hasProto; I.Fun.isVariadic = isVariadic; + I.Fun.isAmbiguous = isAmbiguous; I.Fun.EllipsisLoc = EllipsisLoc.getRawEncoding(); I.Fun.DeleteArgInfo = false; I.Fun.TypeQuals = TypeQuals; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6dfd796f40bf..e511157366d8 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5245,58 +5245,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, FunctionTemplate->setInvalidDecl(); } - // If we see "T var();" at block scope, where T is a class type, it is - // probably an attempt to initialize a variable, not a function declaration. - // We don't catch this case earlier, since there is no ambiguity here. - if (!FunctionTemplate && D.getFunctionDefinitionKind() == FDK_Declaration && - CurContext->isFunctionOrMethod() && - D.getNumTypeObjects() == 1 && D.isFunctionDeclarator() && - D.getDeclSpec().getStorageClassSpecAsWritten() - == DeclSpec::SCS_unspecified) { - QualType T = R->getAs()->getResultType(); - DeclaratorChunk &C = D.getTypeObject(0); - if (!T->isVoidType() && C.Fun.NumArgs == 0 && !C.Fun.isVariadic && - !C.Fun.hasTrailingReturnType() && - C.Fun.getExceptionSpecType() == EST_None) { - SourceRange ParenRange(C.Loc, C.EndLoc); - Diag(C.Loc, diag::warn_empty_parens_are_function_decl) << ParenRange; - - // If the declaration looks like: - // T var1, - // f(); - // and name lookup finds a function named 'f', then the ',' was - // probably intended to be a ';'. - if (!D.isFirstDeclarator() && D.getIdentifier()) { - FullSourceLoc Comma(D.getCommaLoc(), SourceMgr); - FullSourceLoc Name(D.getIdentifierLoc(), SourceMgr); - if (Comma.getFileID() != Name.getFileID() || - Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { - LookupResult Result(*this, D.getIdentifier(), SourceLocation(), - LookupOrdinaryName); - if (LookupName(Result, S)) - Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) - << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << NewFD; - } - } - const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); - // Empty parens mean value-initialization, and no parens mean default - // initialization. These are equivalent if the default constructor is - // user-provided, or if zero-initialization is a no-op. - if (RD && RD->hasDefinition() && - (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) - Diag(C.Loc, diag::note_empty_parens_default_ctor) - << FixItHint::CreateRemoval(ParenRange); - else { - std::string Init = getFixItZeroInitializerForType(T); - if (Init.empty() && LangOpts.CPlusPlus0x) - Init = "{}"; - if (!Init.empty()) - Diag(C.Loc, diag::note_empty_parens_zero_initialize) - << FixItHint::CreateReplacement(ParenRange, Init); - } - } - } - // C++ [dcl.fct.spec]p5: // The virtual specifier shall only be used in declarations of // nonstatic class member functions that appear within a @@ -7915,10 +7863,10 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, (void)Error; // Silence warning. assert(!Error && "Error setting up implicit decl!"); Declarator D(DS, Declarator::BlockContext); - D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, - 0, 0, true, SourceLocation(), + D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, false, + SourceLocation(), 0, 0, 0, true, + SourceLocation(), SourceLocation(), SourceLocation(), SourceLocation(), - SourceLocation(), EST_None, SourceLocation(), 0, 0, 0, 0, Loc, Loc, D), DS.getAttributes(), diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 84e8a7944d97..fcea5a75a9a3 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -554,7 +554,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, // ...and *prepend* it to the declarator. declarator.AddInnermostTypeInfo(DeclaratorChunk::getFunction( /*proto*/ true, - /*variadic*/ false, SourceLocation(), + /*variadic*/ false, + /*ambiguous*/ false, SourceLocation(), /*args*/ 0, 0, /*type quals*/ 0, /*ref-qualifier*/true, SourceLocation(), @@ -2040,6 +2041,101 @@ static void checkQualifiedFunction(Sema &S, QualType T, << getFunctionQualifiersAsString(T->castAs()); } +/// Produce an approprioate diagnostic for an ambiguity between a function +/// declarator and a C++ direct-initializer. +static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, + DeclaratorChunk &DeclType, QualType RT) { + const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun; + assert(FTI.isAmbiguous && "no direct-initializer / function ambiguity"); + + // If the return type is void there is no ambiguity. + if (RT->isVoidType()) + return; + + // An initializer for a non-class type can have at most one argument. + if (!RT->isRecordType() && FTI.NumArgs > 1) + return; + + // An initializer for a reference must have exactly one argument. + if (RT->isReferenceType() && FTI.NumArgs != 1) + return; + + // Only warn if this declarator is declaring a function at block scope, and + // doesn't have a storage class (such as 'extern') specified. + if (!D.isFunctionDeclarator() || + D.getFunctionDefinitionKind() != FDK_Declaration || + !S.CurContext->isFunctionOrMethod() || + D.getDeclSpec().getStorageClassSpecAsWritten() + != DeclSpec::SCS_unspecified) + return; + + // Inside a condition, a direct initializer is not permitted. We allow one to + // be parsed in order to give better diagnostics in condition parsing. + if (D.getContext() == Declarator::ConditionContext) + return; + + SourceRange ParenRange(DeclType.Loc, DeclType.EndLoc); + + // Declaration with parameters, eg. "T var(T());". + if (FTI.NumArgs > 0 && + D.getContext() != Declarator::ConditionContext) { + S.Diag(DeclType.Loc, + diag::warn_parens_disambiguated_as_function_declaration) + << ParenRange; + + SourceRange Range = FTI.ArgInfo[0].Param->getSourceRange(); + SourceLocation B = Range.getBegin(); + SourceLocation E = S.PP.getLocForEndOfToken(Range.getEnd()); + // FIXME: Maybe we should suggest adding braces instead of parens + // in C++11 for classes that don't have an initializer_list constructor. + S.Diag(B, diag::note_additional_parens_for_variable_declaration) + << FixItHint::CreateInsertion(B, "(") + << FixItHint::CreateInsertion(E, ")"); + } + + // Declaration without parameters, eg. "T var();". + if (FTI.NumArgs == 0) { + S.Diag(DeclType.Loc, diag::warn_empty_parens_are_function_decl) + << ParenRange; + + // If the declaration looks like: + // T var1, + // f(); + // and name lookup finds a function named 'f', then the ',' was + // probably intended to be a ';'. + if (!D.isFirstDeclarator() && D.getIdentifier()) { + FullSourceLoc Comma(D.getCommaLoc(), S.SourceMgr); + FullSourceLoc Name(D.getIdentifierLoc(), S.SourceMgr); + if (Comma.getFileID() != Name.getFileID() || + Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { + LookupResult Result(S, D.getIdentifier(), SourceLocation(), + Sema::LookupOrdinaryName); + if (S.LookupName(Result, S.getCurScope())) + S.Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) + << FixItHint::CreateReplacement(D.getCommaLoc(), ";") + << D.getIdentifier(); + } + } + const CXXRecordDecl *RD = RT->getAsCXXRecordDecl(); + // Empty parens mean value-initialization, and no parens mean + // default initialization. These are equivalent if the default + // constructor is user-provided or if zero-initialization is a + // no-op. + if (RD && RD->hasDefinition() && + (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) + S.Diag(DeclType.Loc, diag::note_empty_parens_default_ctor) + << FixItHint::CreateRemoval(ParenRange); + else { + std::string Init = S.getFixItZeroInitializerForType(RT); + if (Init.empty() && S.LangOpts.CPlusPlus0x) + Init = "{}"; + if (!Init.empty()) + S.Diag(DeclType.Loc, diag::note_empty_parens_zero_initialize) + << FixItHint::CreateReplacement(ParenRange, Init); + } + } +} + static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -2272,6 +2368,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, << (D.getContext() == Declarator::AliasDeclContext || D.getContext() == Declarator::AliasTemplateContext); + // If we see "T var();" or "T var(T());" at block scope, it is probably + // an attempt to initialize a variable, not a function declaration. + if (FTI.isAmbiguous) + warnAboutAmbiguousFunction(S, D, DeclType, T); + if (!FTI.NumArgs && !FTI.isVariadic && !LangOpts.CPlusPlus) { // Simple void foo(), where the incoming T is the result type. T = Context.getFunctionNoProtoType(T); diff --git a/clang/test/FixIt/fixit-vexing-parse.cpp b/clang/test/FixIt/fixit-vexing-parse.cpp index 8450590c67f9..dd0f87304a53 100644 --- a/clang/test/FixIt/fixit-vexing-parse.cpp +++ b/clang/test/FixIt/fixit-vexing-parse.cpp @@ -7,6 +7,7 @@ struct S { struct T { T(); + T(S, S); int n; }; @@ -30,26 +31,44 @@ S F2(); namespace N { void test() { - // CHECK: fix-it:"{{.*}}":{34:9-34:11}:" = {}" + // CHECK: fix-it:"{{.*}}":{35:9-35:11}:" = {}" S s1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{38:9-38:10}:";" - // CHECK: fix-it:"{{.*}}":{39:7-39:9}:" = {}" + // CHECK: fix-it:"{{.*}}":{39:9-39:10}:";" + // CHECK: fix-it:"{{.*}}":{40:7-40:9}:" = {}" S s2, // expected-note {{change this ',' to a ';' to call 'F2'}} F2(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{43:9-43:11}:"" // CHECK: fix-it:"{{.*}}":{44:9-44:11}:"" + // CHECK: fix-it:"{{.*}}":{45:9-45:11}:"" T t1(), // expected-warning {{function declaration}} expected-note {{remove parentheses}} t2(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} - // CHECK: fix-it:"{{.*}}":{47:8-47:10}:" = {}" + // Suggest parentheses only around the first argument. + // CHECK: fix-it:"{{.*}}":{50:10-50:10}:"(" + // CHECK: fix-it:"{{.*}}":{50:13-50:13}:")" + T t3(S(), S()); // expected-warning {{disambiguated as a function declaration}} expected-note {{add a pair of parentheses}} + + // Check fixit position for pathological case + // CHECK: fix-it:"{{.*}}":{56:11-56:11}:"(" + // CHECK: fix-it:"{{.*}}":{56:20-56:20}:")" + float k[1]; + int l(int(k[0])); // expected-warning {{disambiguated as a function declaration}} expected-note {{add a pair of parentheses}} + + // Don't emit warning and fixit because this must be a function declaration due to void return type. + typedef void VO; + VO m(int (*p)[4]); + + // Don't emit warning and fixit because direct initializer is not permitted here. + if (int n(int())){} // expected-error {{function type is not allowed here}} expected-error {{condition must have an initializer}} + + // CHECK: fix-it:"{{.*}}":{66:8-66:10}:" = {}" U u(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{50:8-50:10}:"" + // CHECK: fix-it:"{{.*}}":{69:8-69:10}:"" V v(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} - // CHECK: fix-it:"{{.*}}":{53:8-53:10}:"" + // CHECK: fix-it:"{{.*}}":{72:8-72:10}:"" W w(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} // TODO: Removing the parens here would not initialize U::n. @@ -57,33 +76,33 @@ namespace N { // Maybe suggest removing the parens anyway? X x(); // expected-warning {{function declaration}} - // CHECK: fix-it:"{{.*}}":{61:11-61:13}:" = 0" + // CHECK: fix-it:"{{.*}}":{80:11-80:13}:" = 0" int n1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{65:11-65:12}:";" - // CHECK: fix-it:"{{.*}}":{66:7-66:9}:" = 0" + // CHECK: fix-it:"{{.*}}":{84:11-84:12}:";" + // CHECK: fix-it:"{{.*}}":{85:7-85:9}:" = 0" int n2, // expected-note {{change this ',' to a ';' to call 'F1'}} F1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{69:13-69:15}:" = 0.0" + // CHECK: fix-it:"{{.*}}":{88:13-88:15}:" = 0.0" double d(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} typedef void *Ptr; - // CHECK: fix-it:"{{.*}}":{74:10-74:12}:" = 0" + // CHECK: fix-it:"{{.*}}":{93:10-93:12}:" = 0" Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} #define NULL 0 - // CHECK: fix-it:"{{.*}}":{78:10-78:12}:" = NULL" + // CHECK: fix-it:"{{.*}}":{97:10-97:12}:" = NULL" Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{81:11-81:13}:" = false" + // CHECK: fix-it:"{{.*}}":{100:11-100:13}:" = false" bool b(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{84:11-84:13}:" = '\\0'" + // CHECK: fix-it:"{{.*}}":{103:11-103:13}:" = '\\0'" char c(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} - // CHECK: fix-it:"{{.*}}":{87:15-87:17}:" = L'\\0'" + // CHECK: fix-it:"{{.*}}":{106:15-106:17}:" = L'\\0'" wchar_t wc(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} } } diff --git a/clang/test/Parser/objcxx11-attributes.mm b/clang/test/Parser/objcxx11-attributes.mm index 0c91392978af..1b9bf66fad9a 100644 --- a/clang/test/Parser/objcxx11-attributes.mm +++ b/clang/test/Parser/objcxx11-attributes.mm @@ -35,7 +35,8 @@ void f(X *noreturn) { [[class, test(foo 'x' bar),,,]]; [[bitand, noreturn]]; // expected-warning {{attribute noreturn cannot be specified on a statement}} - [[noreturn]]int(e)(); + // FIXME: Suppress vexing parse warning + [[noreturn]]int(e)(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} int e2(); // expected-warning {{interpreted as a function declaration}} expected-note{{}} // A function taking a noreturn function. diff --git a/clang/test/SemaCXX/condition.cpp b/clang/test/SemaCXX/condition.cpp index 993f8e1d7761..ec5eb17b08f3 100644 --- a/clang/test/SemaCXX/condition.cpp +++ b/clang/test/SemaCXX/condition.cpp @@ -7,7 +7,7 @@ void test() { typedef int arr[10]; while (arr x=0) ; // expected-error {{an array type is not allowed here}} expected-error {{array initializer must be an initializer list}} - while (int f()=0) ; // expected-warning {{interpreted as a function declaration}} expected-note {{initializer}} expected-error {{a function type is not allowed here}} + while (int f()=0) ; // expected-error {{a function type is not allowed here}} struct S {} s; if (s) ++x; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} diff --git a/clang/test/SemaCXX/dcl_ambig_res.cpp b/clang/test/SemaCXX/dcl_ambig_res.cpp index fa71b11ba160..08867c0ea2f5 100644 --- a/clang/test/SemaCXX/dcl_ambig_res.cpp +++ b/clang/test/SemaCXX/dcl_ambig_res.cpp @@ -10,9 +10,9 @@ int returns_an_int(); void foo(double a) { - S w(int(a)); // expected-warning{{disambiguated}} + S w(int(a)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} w(17); - S x1(int()); // expected-warning{{disambiguated}} + S x1(int()); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} x1(&returns_an_int); S y((int)a); y.bar(); @@ -69,7 +69,7 @@ struct S5 { static bool const value = false; }; int foo8() { - int v(int(S5::value)); // expected-warning{{disambiguated}} expected-error{{parameter declarator cannot be qualified}} + int v(int(S5::value)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} expected-error{{parameter declarator cannot be qualified}} } template diff --git a/clang/test/SemaCXX/decl-expr-ambiguity.cpp b/clang/test/SemaCXX/decl-expr-ambiguity.cpp index ab1a2b5b9583..7ee699c1a8bf 100644 --- a/clang/test/SemaCXX/decl-expr-ambiguity.cpp +++ b/clang/test/SemaCXX/decl-expr-ambiguity.cpp @@ -22,10 +22,10 @@ void f() { (int())1; // expected-error {{C-style cast from 'int' to 'int ()' is not allowed}} // Declarations. - int fd(T(a)); // expected-warning {{parentheses were disambiguated as a function declarator}} - T(*d)(int(p)); // expected-warning {{parentheses were disambiguated as a function declarator}} expected-note {{previous definition is here}} - typedef T(*td)(int(p)); - extern T(*tp)(int(p)); + int fd(T(a)); // expected-warning {{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} + T(*d)(int(p)); // expected-note {{previous}} + typedef T td(int(p)); + extern T tp(int(p)); T d3(); // expected-warning {{empty parentheses interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}} T d3v(void); typedef T d3t(); diff --git a/clang/test/SemaObjCXX/protocol-lookup.mm b/clang/test/SemaObjCXX/protocol-lookup.mm index bd8444c0ad68..e8abf6c1a9b7 100644 --- a/clang/test/SemaObjCXX/protocol-lookup.mm +++ b/clang/test/SemaObjCXX/protocol-lookup.mm @@ -52,5 +52,5 @@ void rdar8575095(id a) { [id(a) retain]; id x(id(0)); - id x2(id(y)); // expected-warning{{parentheses were disambiguated as a function declarator}} + id x2(id(y)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} } diff --git a/clang/test/SemaTemplate/typename-specifier.cpp b/clang/test/SemaTemplate/typename-specifier.cpp index 15c13e3bef62..733dc7fa184e 100644 --- a/clang/test/SemaTemplate/typename-specifier.cpp +++ b/clang/test/SemaTemplate/typename-specifier.cpp @@ -22,8 +22,8 @@ typename N::C::type *ip3 = &i; // expected-error{{typename specifier refers to n // expected-warning{{'typename' occurs outside of a template}} void test(double d) { - typename N::A::type f(typename N::A::type(a)); // expected-warning{{parentheses were disambiguated as a function declarator}} \ - // expected-warning 2{{'typename' occurs outside of a template}} + typename N::A::type f(typename N::A::type(a)); // expected-warning{{disambiguated as a function declaration}} \ + // expected-note{{add a pair of parentheses}} expected-warning 2{{'typename' occurs outside of a template}} int five = f(5); using namespace N;