]> granicus.if.org Git - clang/commitdiff
Improvements to vexing-parse warnings. Make the no-parameters case more
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 30 Jul 2012 21:30:52 +0000 (21:30 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 30 Jul 2012 21:30:52 +0000 (21:30 +0000)
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'.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160998 91177308-0d34-0410-b5e6-96231b3b80d8

18 files changed:
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/DeclSpec.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseTentative.cpp
lib/Sema/DeclSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaType.cpp
test/FixIt/fixit-vexing-parse.cpp
test/Parser/objcxx11-attributes.mm
test/SemaCXX/condition.cpp
test/SemaCXX/dcl_ambig_res.cpp
test/SemaCXX/decl-expr-ambiguity.cpp
test/SemaObjCXX/protocol-lookup.mm
test/SemaTemplate/typename-specifier.cpp

index 31b8077ef3a57fe415d0e68eb92af37ad903e45f..8cb82fd4a9149b863c34357ca95ee805e397809d 100644 (file)
@@ -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<VexingParse>;
 def warn_dangling_else : Warning<
   "add explicit braces to avoid dangling else">,
   InGroup<DanglingElse>;
index 263a4aef946eb339c30c1365f0ecf65b152c8d70..4df26cfa55fec3a1b4100e9b0fff31540d0cb195 100644 (file)
@@ -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<VexingParse>;
+def warn_parens_disambiguated_as_function_declaration : Warning<
+  "parentheses were disambiguated as a function declaration">,
+  InGroup<VexingParse>;
+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<
index ca380463fb601c6c0fee9359965955f9a478afbb..e874e382ced5d738a1089202ec0157777eb9a90e 100644 (file)
@@ -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(
index 00d42a814a75d960ffc42ae7d9482ae831e51db9..48f7d80167ba2a37248c27ed75c719d272343987 100644 (file)
@@ -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, 
index ea514d3826821feac7c8762c111f5965cee8ce5f..328345413f2b1cd9b1a81ff2283efcfe867af4bc 100644 (file)
@@ -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,
index 7f268b523df26f2b774b2b9bbbe011ab9327bf29..8d4668b95481e72dabbff7462861682c4c2433cc 100644 (file)
@@ -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(),
index 39ef38fdc0c2765ffa07ed33d619defa4de9e35c..b393d1ff67ce34f485edbb6e7355096639c84662 100644 (file)
@@ -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,
index 8cf1c29078c8d951ed68fffdc73c9defe31560ea..1a4df4791b5f9e813d1bfd300df2946fe6113fc4 100644 (file)
@@ -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();
 
-  // 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;
-  }
+  if (IsAmbiguous && TPR == TPResult::Ambiguous())
+    *IsAmbiguous = true;
 
-  return TPR == TPResult::True();
+  // In case of an error, let the declaration parsing code handle it.
+  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 ',' '...'
index f3ec5656ea33953dc0b7d56f0806eb0bb13987d9..d12ca78390955995a21af7713fd4ea19e0fda4b7 100644 (file)
@@ -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;
index 6dfd796f40bf88f177e6ee649f4ee91eb5eebae5..e511157366d8de4b464c5df3cc49fef0e92775d7 100644 (file)
@@ -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<FunctionType>()->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(),
index 84e8a7944d97d19361cf3fb304de2e19d655d529..fcea5a75a9a36c8041d55e7cd14642f872e60a5e 100644 (file)
@@ -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<FunctionProtoType>());
 }
 
+/// 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);
index 8450590c67f9ceacc1294e70365b11583dd1b338..dd0f87304a5379083bb44198a9f922705c6289c4 100644 (file)
@@ -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}}
   }
 }
index 0c91392978af44f826b30d64f223ba19ccb94a81..1b9bf66fad9ae8bbf00a74a6baa7091498ca392b 100644 (file)
@@ -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.
index 993f8e1d77613b836e9d58be15d4e87c4cc95b74..ec5eb17b08f3c2511cec79ae383e146015283e8c 100644 (file)
@@ -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'}}
index fa71b11ba160e7dc6992f76df50d4bf91738132b..08867c0ea2f5f80a3c184d9f7bf359a822b8af5c 100644 (file)
@@ -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<typename T>
index ab1a2b5b9583fb6c5919cb6ad1b3cc6ebde6f79d..7ee699c1a8bf1e4d3a13054fadb23af07d56bcb3 100644 (file)
@@ -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();
index bd8444c0ad6826305c27f3dffcc6bbb1dc038343..e8abf6c1a9b766bb74edda3e0b421b083bf3bbeb 100644 (file)
@@ -52,5 +52,5 @@
 void rdar8575095(id a) {
   [id<NSObject>(a) retain];
   id<NSObject> x(id<NSObject>(0));
-  id<NSObject> x2(id<NSObject>(y)); // expected-warning{{parentheses were disambiguated as a function declarator}}
+  id<NSObject> x2(id<NSObject>(y)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}}
 }
index 15c13e3bef62f90cbcdbd95bf808d8e0fcb8db02..733dc7fa184ed80b9c33412c2925e4b50257eb9c 100644 (file)
@@ -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;