[PATCH v2] c++: Implement -Wvexing-parse [PR25814]

Jason Merrill jason@redhat.com
Fri Oct 30 20:33:48 GMT 2020


On 10/29/20 11:00 PM, Marek Polacek wrote:
> On Thu, Oct 29, 2020 at 02:25:33PM -0400, Jason Merrill via Gcc-patches wrote:
>> On 10/29/20 2:11 PM, Marek Polacek wrote:
>>> On Thu, Oct 29, 2020 at 11:17:37AM -0400, Jason Merrill via Gcc-patches wrote:
>>>> On 10/28/20 7:40 PM, Marek Polacek wrote:
>>>>> On Wed, Oct 28, 2020 at 03:09:08PM -0400, Jason Merrill wrote:
>>>>>> On 10/28/20 1:58 PM, Marek Polacek wrote:
>>>>>>> On Wed, Oct 28, 2020 at 01:26:53AM -0400, Jason Merrill via Gcc-patches wrote:
>>>>>>>> On 10/24/20 7:40 PM, Marek Polacek wrote:
>>>>>>>>> On Fri, Oct 23, 2020 at 09:33:38PM -0400, Jason Merrill via Gcc-patches wrote:
>>>>>>>>>> On 10/23/20 3:01 PM, Marek Polacek wrote:
>>>>>>>>>>> This patch implements the -Wvexing-parse warning to warn about the
>>>>>>>>>>> sneaky most vexing parse rule in C++: the cases when a declaration
>>>>>>>>>>> looks like a variable definition, but the C++ language requires it
>>>>>>>>>>> to be interpreted as a function declaration.  This warning is on by
>>>>>>>>>>> default (like clang++).  From the docs:
>>>>>>>>>>>
>>>>>>>>>>>         void f(double a) {
>>>>>>>>>>>           int i();        // extern int i (void);
>>>>>>>>>>>           int n(int(a));  // extern int n (int);
>>>>>>>>>>>         }
>>>>>>>>>>>
>>>>>>>>>>>         Another example:
>>>>>>>>>>>
>>>>>>>>>>>         struct S { S(int); };
>>>>>>>>>>>         void f(double a) {
>>>>>>>>>>>           S x(int(a));   // extern struct S x (int);
>>>>>>>>>>>           S y(int());    // extern struct S y (int (*) (void));
>>>>>>>>>>>           S z();         // extern struct S z (void);
>>>>>>>>>>>         }
>>>>>>>>>>>
>>>>>>>>>>> You can find more on this in [dcl.ambig.res].
>>>>>>>>>>>
>>>>>>>>>>> I spent a fair amount of time on fix-it hints so that GCC can recommend
>>>>>>>>>>> various ways to resolve such an ambiguity.  Sometimes that's tricky.
>>>>>>>>>>> E.g., suggesting default-initialization when the class doesn't have
>>>>>>>>>>> a default constructor would not be optimal.  Suggesting {}-init is also
>>>>>>>>>>> not trivial because it can use an initializer-list constructor if no
>>>>>>>>>>> default constructor is available (which ()-init wouldn't do).  And of
>>>>>>>>>>> course, pre-C++11, we shouldn't be recommending {}-init at all.
>>>>>>>>>>
>>>>>>>>>> What do you think of, instead of passing the type down into the declarator
>>>>>>>>>> parse, adding the paren locations to cp_declarator::function and giving the
>>>>>>>>>> diagnostic from cp_parser_init_declarator instead?
>>>>>>>>
>>>>>>>> Oops, now I see there's already cp_declarator::parenthesized; might as well
>>>>>>>> reuse that.  And maybe change it to a range, while we're at it.
>>>>>>>
>>>>>>> I'm afraid I can't reuse it because grokdeclarator uses it to warn about
>>>>>>> "unnecessary parentheses in declaration".  So when we have:
>>>>>>>
>>>>>>>       int (x());
>>>>>>>
>>>>>>> declarator->parenthesized points to the outer parens (if any), whereas
>>>>>>> declarator->u.function.parens_loc should point to the inner ones.  We also
>>>>>>> have declarator->id_loc but I think we should only use it for declarator-ids.
>>>>>>
>>>>>> Makes sense.
>>>>>>
>>>>>>> (We should still adjust ->parenthesized to be a range to generate a better
>>>>>>> diagnostic; I shall send a patch soon.)
>>>>>>>
>>>>>>>> Hmm, I wonder why we have the parenthesized_p parameter to some of these
>>>>>>>> functions, since we can look at the declarator to find that information...
>>>>>>>
>>>>>>> That would be a nice cleanup.
>>>>>>>
>>>>>>>>> Interesting idea.  I suppose it's better, and makes the implementation
>>>>>>>>> more localized.  The approach here is that if the .function.parens_loc
>>>>>>>>> is UNKNOWN_LOCATION, we've not seen a vexing parse.
>>>>>>>>
>>>>>>>> I'd rather always set the parens location, and then analyze the
>>>>>>>> cp_declarator in warn_about_ambiguous_parse to see if it's a vexing parse;
>>>>>>>> we should have all the information we need.
>>>>>>>
>>>>>>> I could always set .parens_loc, but then I'd still need another flag telling
>>>>>>> me whether we had an ambiguity.  Otherwise I don't know how I would tell
>>>>>>> apart e.g. "int f()" (warn) v. "int f(void)" (don't warn), etc.
>>>>>>
>>>>>> Ah, I was thinking that we still had the parameter declarators, but now I
>>>>>> see that cp_parser_parameter_declaration_list groks them and returns a
>>>>>> TREE_LIST.  We could set a TREE_LANG_FLAG on each TREE_LIST if its parameter
>>>>>> declarator was parenthesized?
>>>>>
>>>>> I think so, looks like we have a bunch of free TREE_LANG_FLAG slots on
>>>>> a TREE_LIST.  But cp_parser_parameter_declaration_clause can return
>>>>> a void_list_node, so I assume I'd have to copy_node it before setting
>>>>> some new flag in it.  Do you think that'd be fine?
>>>>
>>>> There's no declarator in a void_list_node, so we shouldn't need to set a
>>>> "declarator is parenthesized" flag on it.
>>>
>>> I guess I'm still not clear on how I would distinguish between
>>> int f() and int f(void).  When I look at the cdk_function declarator,
>>> all I can see is the .parameters TREE_LIST, which for both cases will
>>> be the same void_list_node, but we should only warn for the former.
>>>
>>> What am I missing?
>>
>> I'm just being dense.  You're right that we would need to distinguish those
>> two.  Perhaps an explicit_void_parms_node or something like that for during
>> parsing; it looks like grokparms will turn it into void_list_node as other
>> code expects.
> 
> Gotcha.  Now we do most of the work in warn_about_ambiguous_parse.

Thanks, just a few tweaks left.

> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> -- >8 --
> This patch implements the -Wvexing-parse warning to warn about the
> sneaky most vexing parse rule in C++: the cases when a declaration
> looks like a variable definition, but the C++ language requires it
> to be interpreted as a function declaration.  This warning is on by
> default (like clang++).  From the docs:
> 
>    void f(double a) {
>      int i();        // extern int i (void);
>      int n(int(a));  // extern int n (int);
>    }
> 
>    Another example:
> 
>    struct S { S(int); };
>    void f(double a) {
>      S x(int(a));   // extern struct S x (int);
>      S y(int());    // extern struct S y (int (*) (void));
>      S z();         // extern struct S z (void);
>    }
> 
> You can find more on this in [dcl.ambig.res].
> 
> I spent a fair amount of time on fix-it hints so that GCC can recommend
> various ways to resolve such an ambiguity.  Sometimes that's tricky.
> E.g., suggesting default-initialization when the class doesn't have
> a default constructor would not be optimal.  Suggesting {}-init is also
> not trivial because it can use an initializer-list constructor if no
> default constructor is available (which ()-init wouldn't do).  And of
> course, pre-C++11, we shouldn't be recommending {}-init at all.
> 
> I also uncovered a bug in cp_parser_declarator, where we were setting
> *parenthesized_p to true despite the comment saying the exact opposite.
> 
> gcc/c-family/ChangeLog:
> 
> 	PR c++/25814
> 	* c.opt (Wvexing-parse): New option.
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/25814
> 	* cp-tree.h (enum cp_tree_index): Add CPTI_EXPLICIT_VOID_LIST.
> 	(explicit_void_list_node): Define.
> 	(PARENTHESIZED_LIST_P): New macro.
> 	(struct cp_declarator): Add function::parens_loc.
> 	* decl.c (cxx_init_decl_processing): Initialize explicit_void_list_node.
> 	(grokparms): Also break when explicit_void_list_node.
> 	* parser.c (make_call_declarator): New location_t parameter.  Use it
> 	to set declarator->u.function.parens_loc.
> 	(cp_parser_lambda_declarator_opt): Pass UNKNOWN_LOCATION to
> 	make_call_declarator.
> 	(warn_about_ambiguous_parse): New function.
> 	(cp_parser_init_declarator): Call warn_about_ambiguous_parse.
> 	(cp_parser_declarator): Set *parenthesized_p to false rather than to
> 	true.
> 	(cp_parser_direct_declarator): Create a location for the function's
> 	parentheses and pass it to make_call_declarator.
> 	(cp_parser_parameter_declaration_clause): Return explicit_void_list_node
> 	for (void).
> 	(cp_parser_parameter_declaration_list): Set PARENTHESIZED_LIST_P
> 	in the parameters tree.
> 
> gcc/ChangeLog:
> 
> 	PR c++/25814
> 	* doc/invoke.texi: Document -Wvexing-parse.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/25814
> 	* g++.dg/cpp2a/fn-template16.C: Add a dg-warning.
> 	* g++.dg/cpp2a/fn-template7.C: Likewise.
> 	* g++.dg/lookup/pr80891-5.C: Likewise.
> 	* g++.dg/lto/pr79050_0.C: Add extern.
> 	* g++.dg/lto/pr84805_0.C: Likewise.
> 	* g++.dg/parse/pr58898.C: Add a dg-warning.
> 	* g++.dg/template/scope5.C: Likewise.
> 	* g++.old-deja/g++.brendan/recurse.C: Likewise.
> 	* g++.old-deja/g++.jason/template4.C: Likewise.
> 	* g++.old-deja/g++.law/arm4.C: Likewise.
> 	* g++.old-deja/g++.mike/for2.C: Likewise.
> 	* g++.old-deja/g++.other/local4.C: Likewise.
> 	* g++.old-deja/g++.pt/crash3.C: Likewise.
> 	* g++.dg/warn/Wvexing-parse.C: New test.
> 	* g++.dg/warn/Wvexing-parse2.C: New test.
> 	* g++.dg/warn/Wvexing-parse3.C: New test.
> 	* g++.dg/warn/Wvexing-parse4.C: New test.
> 	* g++.dg/warn/Wvexing-parse5.C: New test.
> 	* g++.dg/warn/Wvexing-parse6.C: New test.
> 
> libstdc++-v3/ChangeLog:
> 
> 	PR c++/25814
> 	* testsuite/20_util/reference_wrapper/lwg2993.cc: Add a dg-warning.
> 	* testsuite/25_algorithms/generate_n/87982_neg.cc: Likewise.
> ---
>   gcc/c-family/c.opt                            |   4 +
>   gcc/cp/cp-tree.h                              |   8 +
>   gcc/cp/decl.c                                 |   5 +-
>   gcc/cp/parser.c                               | 144 +++++++++++++++++-
>   gcc/doc/invoke.texi                           |  34 ++++-
>   gcc/testsuite/g++.dg/cpp2a/fn-template16.C    |   2 +-
>   gcc/testsuite/g++.dg/cpp2a/fn-template7.C     |   2 +-
>   gcc/testsuite/g++.dg/lookup/pr80891-5.C       |   2 +-
>   gcc/testsuite/g++.dg/lto/pr79050_0.C          |   2 +-
>   gcc/testsuite/g++.dg/lto/pr84805_0.C          |   2 +-
>   gcc/testsuite/g++.dg/parse/pr58898.C          |   4 +-
>   gcc/testsuite/g++.dg/template/scope5.C        |   2 +-
>   gcc/testsuite/g++.dg/warn/Wvexing-parse.C     | 110 +++++++++++++
>   gcc/testsuite/g++.dg/warn/Wvexing-parse2.C    |  24 +++
>   gcc/testsuite/g++.dg/warn/Wvexing-parse3.C    | 129 ++++++++++++++++
>   gcc/testsuite/g++.dg/warn/Wvexing-parse4.C    |  74 +++++++++
>   gcc/testsuite/g++.dg/warn/Wvexing-parse5.C    |  14 ++
>   gcc/testsuite/g++.dg/warn/Wvexing-parse6.C    |  24 +++
>   .../g++.old-deja/g++.brendan/recurse.C        |   2 +-
>   .../g++.old-deja/g++.jason/template4.C        |   2 +-
>   gcc/testsuite/g++.old-deja/g++.law/arm4.C     |   2 +-
>   gcc/testsuite/g++.old-deja/g++.mike/for2.C    |   2 +-
>   gcc/testsuite/g++.old-deja/g++.other/local4.C |   2 +-
>   gcc/testsuite/g++.old-deja/g++.pt/crash3.C    |   2 +
>   .../20_util/reference_wrapper/lwg2993.cc      |   2 +-
>   .../25_algorithms/generate_n/87982_neg.cc     |   2 +-
>   26 files changed, 578 insertions(+), 24 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse2.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse3.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse4.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse5.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse6.C
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 10e53ea67c9..0991094e34d 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1274,6 +1274,10 @@ Wvarargs
>   C ObjC C++ ObjC++ Warning Var(warn_varargs) Init(1)
>   Warn about questionable usage of the macros used to retrieve variable arguments.
>   
> +Wvexing-parse
> +C++ ObjC++ Warning Var(warn_vexing_parse) Init(1)
> +Warn about the most vexing parse syntactic ambiguity.
> +
>   Wvla
>   C ObjC C++ ObjC++ Var(warn_vla) Init(-1) Warning
>   Warn if a variable length array is used.
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index fdb8ee57f0b..b044098277c 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -125,6 +125,7 @@ enum cp_tree_index
>       CPTI_CLASS_TYPE,
>       CPTI_UNKNOWN_TYPE,
>       CPTI_INIT_LIST_TYPE,
> +    CPTI_EXPLICIT_VOID_LIST,
>       CPTI_VTBL_TYPE,
>       CPTI_VTBL_PTR_TYPE,
>       CPTI_STD,
> @@ -232,6 +233,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
>   #define class_type_node			cp_global_trees[CPTI_CLASS_TYPE]
>   #define unknown_type_node		cp_global_trees[CPTI_UNKNOWN_TYPE]
>   #define init_list_type_node		cp_global_trees[CPTI_INIT_LIST_TYPE]
> +#define explicit_void_list_node		cp_global_trees[CPTI_EXPLICIT_VOID_LIST]
>   #define vtbl_type_node			cp_global_trees[CPTI_VTBL_TYPE]
>   #define vtbl_ptr_type_node		cp_global_trees[CPTI_VTBL_PTR_TYPE]
>   #define std_node			cp_global_trees[CPTI_STD]
> @@ -413,6 +415,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
>         ATTR_IS_DEPENDENT (in the TREE_LIST for an attribute)
>         ABI_TAG_IMPLICIT (in the TREE_LIST for the argument of abi_tag)
>         LAMBDA_CAPTURE_EXPLICIT_P (in a TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST)
> +      PARENTHESIZED_LIST_P (in the TREE_LIST for a parameter-declaration-list)
>         CONSTRUCTOR_IS_DIRECT_INIT (in CONSTRUCTOR)
>         LAMBDA_EXPR_CAPTURES_THIS_P (in LAMBDA_EXPR)
>         DECLTYPE_FOR_LAMBDA_CAPTURE (in DECLTYPE_TYPE)
> @@ -3395,6 +3398,10 @@ struct GTY(()) lang_decl {
>      was inherited from a template parameter, not explicitly indicated.  */
>   #define ABI_TAG_IMPLICIT(NODE) TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
>   
> +/* In a TREE_LIST for a parameter-declaration-list, indicates that this
> +   list was enclosed in ().  */
> +#define PARENTHESIZED_LIST_P(NODE) TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
> +
>   /* Non zero if this is a using decl for a dependent scope. */
>   #define DECL_DEPENDENT_P(NODE) DECL_LANG_FLAG_0 (USING_DECL_CHECK (NODE))
>   
> @@ -6051,6 +6058,7 @@ struct cp_declarator {
>         tree late_return_type;
>         /* The trailing requires-clause, if any. */
>         tree requires_clause;
> +      location_t parens_loc;
>       } function;
>       /* For arrays.  */
>       struct {
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 39f56b81275..81666fecfcb 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -4378,6 +4378,9 @@ cxx_init_decl_processing (void)
>     init_list_type_node = make_node (LANG_TYPE);
>     record_unknown_type (init_list_type_node, "init list");
>   
> +  /* Used when parsing to distinguish parameter-lists () and (void).  */
> +  explicit_void_list_node = build_void_list_node ();
> +
>     {
>       /* Make sure we get a unique function type, so we can give
>          its pointer type a name.  (This wins for gdb.) */
> @@ -14033,7 +14036,7 @@ grokparms (tree parmlist, tree *parms)
>         tree init = TREE_PURPOSE (parm);
>         tree decl = TREE_VALUE (parm);
>   
> -      if (parm == void_list_node)
> +      if (parm == void_list_node || parm == explicit_void_list_node)
>   	break;

Is this hunk needed?  I thought explicit_void_type_node would be handled 
by the if (VOID_TYPE_P) block below.

>         if (! decl || TREE_TYPE (decl) == error_mark_node)
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index bd8c241dd82..00a419032af 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -1438,7 +1438,8 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs)
>      VAR_DECLs or FUNCTION_DECLs) should do that directly.  */
>   
>   static cp_declarator *make_call_declarator
> -  (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree, tree);
> +  (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier,
> +   tree, tree, tree, tree, location_t);
>   static cp_declarator *make_array_declarator
>     (cp_declarator *, tree);
>   static cp_declarator *make_pointer_declarator
> @@ -1621,7 +1622,8 @@ make_call_declarator (cp_declarator *target,
>   		      tree tx_qualifier,
>   		      tree exception_specification,
>   		      tree late_return_type,
> -		      tree requires_clause)
> +		      tree requires_clause,
> +		      location_t parens_loc)
>   {
>     cp_declarator *declarator;
>   
> @@ -1635,6 +1637,7 @@ make_call_declarator (cp_declarator *target,
>     declarator->u.function.exception_specification = exception_specification;
>     declarator->u.function.late_return_type = late_return_type;
>     declarator->u.function.requires_clause = requires_clause;
> +  declarator->u.function.parens_loc = parens_loc;
>     if (target)
>       {
>         declarator->id_loc = target->id_loc;
> @@ -11246,7 +11249,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
>   				       tx_qual,
>   				       exception_spec,
>                                          return_type,
> -				       trailing_requires_clause);
> +				       trailing_requires_clause,
> +				       UNKNOWN_LOCATION);
>       declarator->std_attributes = std_attrs;
>   
>       fco = grokmethod (&return_type_specs,
> @@ -20613,6 +20617,112 @@ strip_declarator_types (tree type, cp_declarator *declarator)
>     return type;
>   }
>   
> +/* Warn about the most vexing parse syntactic ambiguity, i.e., warn when
> +   a construct looks like a variable definition but is actually a function
> +   declaration.  TYPE is the return type of such a function; DECLARATOR is
> +   the declarator for this function declaration.  */
> +
> +static void
> +warn_about_ambiguous_parse (tree type, const cp_declarator *declarator)
> +{
> +  if (declarator->kind != cdk_function
> +      || !declarator->declarator
> +      || declarator->declarator->kind != cdk_id
> +      || !identifier_p (get_unqualified_id
> +			(const_cast<cp_declarator *>(declarator))))
> +    return;
> +
> +  /* Don't warn when the whole declarator (not just the declarator-id!)
> +     was parenthesized.  That is, don't warn for int(n()) but do warn
> +     for int(f)().  */
> +  if (declarator->parenthesized != UNKNOWN_LOCATION)
> +    return;
> +
> +  location_t loc = declarator->u.function.parens_loc;
> +  if (loc == UNKNOWN_LOCATION)
> +    return;

Is this still possible?

> +  if (TREE_CODE (type) == TYPE_DECL)
> +   type = TREE_TYPE (type);
> +
> +  /* If the return type is void there is no ambiguity.  */
> +  if (same_type_p (type, void_type_node))
> +    return;
> +
> +  auto_diagnostic_group d;
> +  tree params = declarator->u.function.parameters;
> +  const bool has_list_ctor_p = CLASS_TYPE_P (type) && TYPE_HAS_LIST_CTOR (type);
> +
> +  /* The T t() case.  */
> +  if (params == void_list_node)
> +    {
> +      if (warning_at (loc, OPT_Wvexing_parse,
> +		      "empty parentheses were disambiguated as a function "
> +		      "declaration"))
> +	{
> +	  /* () means value-initialization (C++03 and up); {} (C++11 and up)
> +	     means value-initialization or aggregate--initialization, nothing
> +	     means default-initialization.  We can only suggest removing the
> +	     parentheses/adding {} if T has a default constructor.  */
> +	  if (!CLASS_TYPE_P (type) || TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
> +	    {
> +	      gcc_rich_location iloc (loc);
> +	      iloc.add_fixit_remove ();
> +	      inform (&iloc, "remove parentheses to default-initialize "
> +		      "a variable");
> +	      if (cxx_dialect >= cxx11 && !has_list_ctor_p)
> +		{
> +		  if (CP_AGGREGATE_TYPE_P (type))
> +		    inform (loc, "or replace parentheses with braces to "
> +			    "aggregate-initialize a variable");
> +		  else
> +		    inform (loc, "or replace parentheses with braces to "
> +			    "value-initialize a variable");
> +		}
> +	    }
> +	}
> +      return;
> +    }
> +
> +  /* If we had (...) or the parameter-list wasn't parenthesized,
> +     we're done.  */
> +  if (params == NULL_TREE || !PARENTHESIZED_LIST_P (params))
> +    return;

This needs to be a loop so we check all the elements of the list.

> +  /* The T t(X()) case.  */
> +  if (list_length (params) == 2)
> +    {
> +      if (warning_at (loc, OPT_Wvexing_parse,
> +		      "parentheses were disambiguated as a function "
> +		      "declaration"))
> +	{
> +	  gcc_rich_location iloc (loc);
> +	  /* {}-initialization means that we can use an initializer-list
> +	     constructor if no default constructor is available, so don't
> +	     suggest using {} for classes that have an initializer_list
> +	     constructor.  */
> +	  if (cxx_dialect >= cxx11 && !has_list_ctor_p)
> +	    {
> +	      iloc.add_fixit_replace (get_start (loc), "{");
> +	      iloc.add_fixit_replace (get_finish (loc), "}");
> +	      inform (&iloc, "replace parentheses with braces to declare a "
> +		      "variable");
> +	    }
> +	  else
> +	    {
> +	      iloc.add_fixit_insert_after (get_start (loc), "(");
> +	      iloc.add_fixit_insert_before (get_finish (loc), ")");
> +	      inform (&iloc, "add parentheses to declare a variable");
  > +	    }
> +	}
> +    }
> +  /* The T t(X(), X()) case.  */
> +  else
> +    warning_at (loc, OPT_Wvexing_parse,
> +		"parentheses were disambiguated as a function "
> +		"declaration");
> +}

Why not suggest braces for this case?

> +
>   /* Declarators [gram.dcl.decl] */
>   
>   /* Parse an init-declarator.
> @@ -20807,6 +20917,13 @@ cp_parser_init_declarator (cp_parser* parser,
>   	    }
>   	}
>   
> +      if (decl_specifiers->storage_class == sc_none
> +	  && at_function_scope_p ()
> +	  && !member_p
> +	  && !decl_spec_seq_has_spec_p (decl_specifiers, ds_typedef)
> +	  && !cp_parser_error_occurred (parser))
> +	warn_about_ambiguous_parse (decl_specifiers->type, declarator);

Maybe pass decl_specifiers into warn_about_ambiguous_parse and move the 
specifiers checks into the function?

>         /* Check to see if the token indicates the start of a
>   	 function-definition.  */
>         if (cp_parser_token_starts_function_definition_p (token))
> @@ -21201,7 +21318,7 @@ cp_parser_declarator (cp_parser* parser,
>         /* If a ptr-operator was found, then this declarator was not
>   	 parenthesized.  */
>         if (parenthesized_p)
> -	*parenthesized_p = true;
> +	*parenthesized_p = false;
>         /* The dependent declarator is optional if we are parsing an
>   	 abstract-declarator.  */
>         if (dcl_kind != CP_PARSER_DECLARATOR_NAMED)
> @@ -21348,6 +21465,7 @@ cp_parser_direct_declarator (cp_parser* parser,
>   		cp_parser_parse_tentatively (parser);
>   
>   	      /* Consume the `('.  */
> +	      const location_t parens_start = token->location;
>   	      matching_parens parens;
>   	      parens.consume_open (parser);
>   	      if (first)
> @@ -21367,6 +21485,8 @@ cp_parser_direct_declarator (cp_parser* parser,
>   	      /* Parse the parameter-declaration-clause.  */
>   	      params
>   		= cp_parser_parameter_declaration_clause (parser, flags);
> +	      const location_t parens_end
> +		= cp_lexer_peek_token (parser->lexer)->location;
>   
>   	      /* Consume the `)'.  */
>   	      parens.require_close (parser);
> @@ -21431,6 +21551,9 @@ cp_parser_direct_declarator (cp_parser* parser,
>   		  /* Parse the virt-specifier-seq.  */
>   		  virt_specifiers = cp_parser_virt_specifier_seq_opt (parser);
>   
> +		  location_t parens_loc = make_location (parens_start,
> +							 parens_start,
> +							 parens_end);
>   		  /* Create the function-declarator.  */
>   		  declarator = make_call_declarator (declarator,
>   						     params,
> @@ -21440,7 +21563,8 @@ cp_parser_direct_declarator (cp_parser* parser,
>   						     tx_qual,
>   						     exception_specification,
>   						     late_return,
> -						     requires_clause);
> +						     requires_clause,
> +						     parens_loc);
>   		  declarator->std_attributes = attrs;
>   		  declarator->attributes = gnu_attrs;
>   		  /* Any subsequent parameter lists are to do with
> @@ -22707,7 +22831,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser,
>         /* Consume the `void' token.  */
>         cp_lexer_consume_token (parser->lexer);
>         /* There are no parameters.  */
> -      return void_list_node;
> +      return explicit_void_list_node;
>       }
>   
>     /* Parse the parameter-declaration-list.  */
> @@ -22838,7 +22962,13 @@ cp_parser_parameter_declaration_list (cp_parser* parser, cp_parser_flags flags)
>   	  || cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
>   	  || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
>   	/* The parameter-declaration-list is complete.  */
> -	break;
> +	{
> +	  /* If the parameters were parenthesized, it's the case of
> +	     T foo(X(x)) which looks like a variable definition but
> +	     is a function declaration.  */
> +	  PARENTHESIZED_LIST_P (parameters) = parenthesized_p;
> +	  break;
> +	}
>         else if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
>   	{
>   	  cp_token *token;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 79d479c72b7..bab6c2c4454 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -253,7 +253,8 @@ in the following sections.
>   -Woverloaded-virtual  -Wno-pmf-conversions -Wsign-promo @gol
>   -Wsized-deallocation  -Wsuggest-final-methods @gol
>   -Wsuggest-final-types  -Wsuggest-override  @gol
> --Wno-terminate  -Wuseless-cast  -Wvirtual-inheritance  @gol
> +-Wno-terminate  -Wuseless-cast  -Wno-vexing-parse  @gol
> +-Wvirtual-inheritance  @gol
>   -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
>   
>   @item Objective-C and Objective-C++ Language Options
> @@ -3886,6 +3887,37 @@ use the STL.  One may also use using directives and qualified names.
>   Disable the warning about a throw-expression that will immediately
>   result in a call to @code{terminate}.
>   
> +@item -Wno-vexing-parse @r{(C++ and Objective-C++ only)}
> +@opindex Wvexing-parse
> +@opindex Wno-vexing-parse
> +Warn about the most vexing parse syntactic ambiguity.  This warns about
> +the cases when a declaration looks like a variable definition, but the
> +C++ language requires it to be interpreted as a function declaration.
> +For instance:
> +
> +@smallexample
> +void f(double a) @{
> +  int i();        // extern int i (void);
> +  int n(int(a));  // extern int n (int);
> +@}
> +@end smallexample
> +
> +Another example:
> +
> +@smallexample
> +struct S @{ S(int); @};
> +void f(double a) @{
> +  S x(int(a));   // extern struct S x (int);
> +  S y(int());    // extern struct S y (int (*) (void));
> +  S z();         // extern struct S z (void);
> +@}
> +@end smallexample
> +
> +The warning will suggest options how to deal with such an ambiguity; e.g.,
> +it can suggest removing the parentheses or using braces instead.
> +
> +This warning is enabled by default.
> +
>   @item -Wno-class-conversion @r{(C++ and Objective-C++ only)}
>   @opindex Wno-class-conversion
>   @opindex Wclass-conversion
> diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template16.C b/gcc/testsuite/g++.dg/cpp2a/fn-template16.C
> index becaff1e3fb..8ee783ad577 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/fn-template16.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template16.C
> @@ -7,7 +7,7 @@ struct undeclared<int> { }; // { dg-error "not a class template" }
>   int
>   main ()
>   {
> -  int foo ();
> +  int foo (); // { dg-warning "empty parentheses" }
>     int foo (int);
>     int foo (int, int);
>     int a, b = 10;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/fn-template7.C b/gcc/testsuite/g++.dg/cpp2a/fn-template7.C
> index d048606c0d6..2c5ee120dcd 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/fn-template7.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/fn-template7.C
> @@ -7,7 +7,7 @@ struct undeclared<int> { }; // { dg-error "not a class template" }
>   int
>   main ()
>   {
> -  int foo ();
> +  int foo (); // { dg-warning "empty parentheses" }
>     int a, b = 10;
>     a = foo<; // { dg-error "invalid template-argument-list|invalid" }
>     a = foo < b; // { dg-error "invalid template-argument-list|invalid" }
> diff --git a/gcc/testsuite/g++.dg/lookup/pr80891-5.C b/gcc/testsuite/g++.dg/lookup/pr80891-5.C
> index e018922d68b..10d1ce3f3d5 100644
> --- a/gcc/testsuite/g++.dg/lookup/pr80891-5.C
> +++ b/gcc/testsuite/g++.dg/lookup/pr80891-5.C
> @@ -14,7 +14,7 @@ template <typename, typename, typename, typename,
>   struct B {
>     B(A, A, int, int, int, int);
>     void m_fn1(SubGraphIsoMapCallback p1) {
> -    __normal_iterator __trans_tmp_1();
> +    __normal_iterator __trans_tmp_1(); // { dg-warning "empty parentheses" }
>       p1(__trans_tmp_1, 0);
>     }
>   };
> diff --git a/gcc/testsuite/g++.dg/lto/pr79050_0.C b/gcc/testsuite/g++.dg/lto/pr79050_0.C
> index 1f31b5d0020..464f5594769 100644
> --- a/gcc/testsuite/g++.dg/lto/pr79050_0.C
> +++ b/gcc/testsuite/g++.dg/lto/pr79050_0.C
> @@ -3,5 +3,5 @@
>   
>   int main ()
>   {
> -  auto foo ();
> +  extern auto foo ();
>   }
> diff --git a/gcc/testsuite/g++.dg/lto/pr84805_0.C b/gcc/testsuite/g++.dg/lto/pr84805_0.C
> index 1509eae4845..668ba362aed 100644
> --- a/gcc/testsuite/g++.dg/lto/pr84805_0.C
> +++ b/gcc/testsuite/g++.dg/lto/pr84805_0.C
> @@ -149,5 +149,5 @@ public:
>   class XclImpRoot : XclRoot {};
>   class XclImpColRowSettings : XclImpRoot {};
>   void lcl_ExportExcelBiff() {
> -XclRootData aExpData();
> +extern XclRootData aExpData();
>   }
> diff --git a/gcc/testsuite/g++.dg/parse/pr58898.C b/gcc/testsuite/g++.dg/parse/pr58898.C
> index e788c913c66..e67011d2fe7 100644
> --- a/gcc/testsuite/g++.dg/parse/pr58898.C
> +++ b/gcc/testsuite/g++.dg/parse/pr58898.C
> @@ -5,12 +5,12 @@ struct Foo
>   {
>     Foo()
>     {
> -    int t(int()); // Error
> +    int t(int()); // { dg-warning "parentheses were disambiguated" }
>     }
>   };
>   
>   int main()
>   {
> -  int t(int()); // OK
> +  int t(int()); // { dg-warning "parentheses were disambiguated" }
>     Foo<> a; // Error
>   }
> diff --git a/gcc/testsuite/g++.dg/template/scope5.C b/gcc/testsuite/g++.dg/template/scope5.C
> index cf23a0837bd..b20d897b49f 100644
> --- a/gcc/testsuite/g++.dg/template/scope5.C
> +++ b/gcc/testsuite/g++.dg/template/scope5.C
> @@ -59,7 +59,7 @@ template <typename av> struct ac : ao<av> { typedef c::e<am::an> aq; };
>   template <typename aw, typename i, typename ax> void ay(aw, i, ax) {
>     // Not sure if this has been creduced from an initialization of a
>     // variable to a block-scope extern function decl
> -  au<c::e<ap<typename ak<i>::o>::f> > az2();
> +  au<c::e<ap<typename ak<i>::o>::f> > az2(); // { dg-warning "empty parentheses" }
>   }
>   void v() {
>     ad a;
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse.C
> new file mode 100644
> index 00000000000..b02e904fa83
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse.C
> @@ -0,0 +1,110 @@
> +// PR c++/25814
> +// { dg-do compile }
> +// Test -Wvexing-parse.
> +
> +struct T { };
> +
> +struct X {
> +  X();
> +};
> +
> +struct S {
> +  S(int);
> +  S foo (int (int));
> +  S(T);
> +  int m;
> +};
> +
> +struct W {
> +  W();
> +  W(X, X);
> +  int m;
> +};
> +
> +int g;
> +int g1(int(g));
> +int g2(int());
> +void fg(int);
> +
> +void
> +fn1 (double (a))
> +{
> +  extern int f0();
> +  extern int f1(int(a));
> +  int f2(int(a)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  int (*f3)(int(a));
> +  int f4(int a);
> +  int f5(int()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  int f6(...);
> +  int f7((int(a)));
> +  int (f8);
> +  int f9(S(s)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  int(f10) __attribute__(());
> +  int(f11(int()));
> +  if (int(a) = 1) { }
> +  int j, k, l(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  int m, f12(int(j)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +
> +  T t1(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  T t2(T()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  /* Declares a variable t3.  */
> +  T(t3);
> +  T t4(), // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +    t5(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +
> +  extern S s1(int(a));
> +  S s2(int(a)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  S s3(int a);
> +  S s4(int()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  S s5(int(int)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  S s6(...);
> +  S s7((int(a)));
> +  S s8((int)a);
> +  S s9 = int(a);
> +  S(T());
> +  S s10(S()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  S s11(T());
> +  S s12(X()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  S s13 = S(T());
> +  S(T()).foo(0);
> +  S (S::*foo)(int (int));
> +  S(*s14)(int(a));
> +  S s15(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  S s16(void);
> +
> +  /* Don't warn here.  */
> +  void fv1(int(a));
> +  void fv2(int());
> +  void (fv3)();
> +  void (fv4)(void);
> +  void (fv5)(int);
> +
> +  int n(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  int (n2)(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  int n3(void);
> +
> +  typedef int F(const char*);
> +  typedef int F2();
> +  typedef int F3() const;
> +  typedef int F4(int(a)) const;
> +
> +  W w(X(), X()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +}
> +
> +struct C1 {
> +  C1(int);
> +};
> +
> +struct C2 {
> +  C2(C1, int);
> +};
> +
> +template<int N> int value() { return N; }
> +
> +void
> +fn2 ()
> +{
> +  int i = 0;
> +  C2 c2(C1(int(i)), i);
> +  C1(value<0>());
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse2.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse2.C
> new file mode 100644
> index 00000000000..0dbeb7255cc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse2.C
> @@ -0,0 +1,24 @@
> +// PR c++/25814
> +// { dg-do compile { target c++11 } }
> +// Test -Wvexing-parse.  C++11 features.
> +
> +struct X { };
> +struct T {
> +  T(X);
> +};
> +
> +void
> +fn1 (double (a))
> +{
> +  auto l = [](){
> +    int f(int(a)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  };
> +
> +  [[noreturn]] int(e)(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +
> +  T t1{X()};
> +  T t2(X{});
> +  T t3{X{}};
> +
> +  using U = int();
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse3.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse3.C
> new file mode 100644
> index 00000000000..43fcdf29f61
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse3.C
> @@ -0,0 +1,129 @@
> +// PR c++/25814
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-fdiagnostics-show-caret" }
> +// Test -Wvexing-parse's fix-it hints in C++11.
> +
> +#include <initializer_list>
> +
> +struct X { };
> +
> +struct S {
> +  S(X);
> +  S(std::initializer_list<X>);
> +  int m;
> +};
> +
> +struct T {
> +  T(X);
> +  int m;
> +};
> +
> +struct W {
> +  W();
> +  W(std::initializer_list<X>);
> +  int m;
> +};
> +
> +struct U {
> +  U();
> +  int m;
> +};
> +
> +int
> +main ()
> +{
> +  /*
> +     Careful what we're suggesting:
> +     S a((X())) -> S(X)
> +     S a({X()}) -> (std::initializer_list<X>)
> +     S a{X()} -> (std::initializer_list<X>)
> +   */
> +  S a(X()); // { dg-warning "6:parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   S a(X());
> +      ^~~~~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:add parentheses to declare a variable" "" { target *-*-* } 41 }
> +  /* { dg-begin-multiline-output "" }
> +   S a(X());
> +      ^~~~~
> +       (  )
> +     { dg-end-multiline-output "" } */
> +
> +  T t(X()); // { dg-warning "6:parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   T t(X());
> +      ^~~~~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:replace parentheses with braces to declare a variable" "" { target *-*-* } 53 }
> +  /* { dg-begin-multiline-output "" }
> +   T t(X());
> +      ^~~~~
> +      -
> +      {   -
> +          }
> +     { dg-end-multiline-output "" } */
> +
> +  int n(   ); // { dg-warning "8:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   int n(   );
> +        ^~~~~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "8:remove parentheses to default-initialize a variable" "" { target *-*-* } 67 }
> +  /* { dg-begin-multiline-output "" }
> +   int n(   );
> +        ^~~~~
> +        -----
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "8:or replace parentheses with braces to value-initialize a variable" "" { target *-*-* } 67 }
> +
> +  S s(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   S s();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +
> +  X x(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   X x();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:remove parentheses to default-initialize a variable" "" { target *-*-* } 86 }
> +  /* { dg-begin-multiline-output "" }
> +   X x();
> +      ^~
> +      --
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:or replace parentheses with braces to aggregate-initialize a variable" "" { target *-*-* } 86 }
> +
> +  W w(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   W w();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:remove parentheses to default-initialize a variable" "" { target *-*-* } 99 }
> +  /* { dg-begin-multiline-output "" }
> +   W w();
> +      ^~
> +      --
> +     { dg-end-multiline-output "" } */
> +
> +  T t2(); // { dg-warning "7:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   T t2();
> +       ^~
> +     { dg-end-multiline-output "" } */
> +
> +  U u(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   U u();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:remove parentheses to default-initialize a variable" "" { target *-*-* } 117 }
> +  /* { dg-begin-multiline-output "" }
> +   U u();
> +      ^~
> +      --
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:or replace parentheses with braces to value-initialize a variable" "" { target *-*-* } 117 }
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse4.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse4.C
> new file mode 100644
> index 00000000000..3e010aaba3d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse4.C
> @@ -0,0 +1,74 @@
> +// PR c++/25814
> +// { dg-do compile { target c++98_only } }
> +// { dg-additional-options "-fdiagnostics-show-caret" }
> +// Test -Wvexing-parse's fix-it hints in C++98.
> +
> +struct X { };
> +
> +struct T {
> +  T(X);
> +  int m;
> +};
> +
> +struct U {
> +  U();
> +  int m;
> +};
> +
> +int
> +main ()
> +{
> +  T t(X()); // { dg-warning "6:parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   T t(X());
> +      ^~~~~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:add parentheses to declare a variable" "" { target *-*-* } 21 }
> +  /* { dg-begin-multiline-output "" }
> +   T t(X());
> +      ^~~~~
> +       (  )
> +     { dg-end-multiline-output "" } */
> +
> +  int n(   ); // { dg-warning "8:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   int n(   );
> +        ^~~~~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "8:remove parentheses to default-initialize a variable" "" { target *-*-* } 33 }
> +  /* { dg-begin-multiline-output "" }
> +   int n(   );
> +        ^~~~~
> +        -----
> +     { dg-end-multiline-output "" } */
> +
> +  T y(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   T y();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +
> +  X x(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   X x();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:remove parentheses to default-initialize a variable" "" { target *-*-* } 51 }
> +  /* { dg-begin-multiline-output "" }
> +   X x();
> +      ^~
> +      --
> +     { dg-end-multiline-output "" } */
> +
> +  U u(); // { dg-warning "6:empty parentheses were disambiguated as a function declaration" }
> +  /* { dg-begin-multiline-output "" }
> +   U u();
> +      ^~
> +     { dg-end-multiline-output "" } */
> +  // { dg-message "6:remove parentheses to default-initialize a variable" "" { target *-*-* } 63 }
> +  /* { dg-begin-multiline-output "" }
> +   U u();
> +      ^~
> +      --
> +     { dg-end-multiline-output "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse5.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse5.C
> new file mode 100644
> index 00000000000..3422e706160
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse5.C
> @@ -0,0 +1,14 @@
> +// PR c++/25814
> +// { dg-do compile }
> +// Test -Wvexing-parse in a template.
> +
> +struct X { };
> +
> +template<typename T>
> +void fn ()
> +{
> +  T t(); // { dg-warning "empty parentheses were disambiguated as a function declaration" }
> +  T a(X()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  X x(T()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  int i(T()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wvexing-parse6.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse6.C
> new file mode 100644
> index 00000000000..58fa725a2ee
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse6.C
> @@ -0,0 +1,24 @@
> +// PR c++/25814
> +// { dg-do compile }
> +// Test from Wikipedia.
> +
> +class Timer {
> + public:
> +  Timer();
> +};
> +
> +class TimeKeeper {
> + public:
> +  TimeKeeper(const Timer& t);
> +
> +  int get_time();
> +};
> +
> +void f(double adouble) {
> +  int i(int(adouble)); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +}
> +
> +int main() {
> +  TimeKeeper time_keeper(Timer()); // { dg-warning "parentheses were disambiguated as a function declaration" }
> +  return time_keeper.get_time(); // { dg-error "request for member" }
> +}
> diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/recurse.C b/gcc/testsuite/g++.old-deja/g++.brendan/recurse.C
> index de20a073221..0af1c147cd8 100644
> --- a/gcc/testsuite/g++.old-deja/g++.brendan/recurse.C
> +++ b/gcc/testsuite/g++.old-deja/g++.brendan/recurse.C
> @@ -73,7 +73,7 @@ public:
>   
>   int main()
>   {
> -  DBpathrec a(), b();
> +  DBpathrec a(), b(); // { dg-warning "empty parentheses" }
>   
>     a = b;// { dg-error "" }  non-lvalue in assignment.*
>   }
> diff --git a/gcc/testsuite/g++.old-deja/g++.jason/template4.C b/gcc/testsuite/g++.old-deja/g++.jason/template4.C
> index de7d3312a71..1cf5a614411 100644
> --- a/gcc/testsuite/g++.old-deja/g++.jason/template4.C
> +++ b/gcc/testsuite/g++.old-deja/g++.jason/template4.C
> @@ -17,5 +17,5 @@ template <class T>
>   ccList <T> cc_List<T>::copy (){}
>   
>   int main (int, char **) {
> -  ccList <int> size1();
> +  ccList <int> size1(); // { dg-warning "empty parentheses" }
>   }
> diff --git a/gcc/testsuite/g++.old-deja/g++.law/arm4.C b/gcc/testsuite/g++.old-deja/g++.law/arm4.C
> index bbcf7df2391..59492ca952c 100644
> --- a/gcc/testsuite/g++.old-deja/g++.law/arm4.C
> +++ b/gcc/testsuite/g++.old-deja/g++.law/arm4.C
> @@ -20,7 +20,7 @@ int main(void)
>   {
>     double a = 2.0;
>   
> -  S x(int (a));
> +  S x(int (a)); // { dg-warning "parentheses were disambiguated" }
>     if (count > 0)
>       { printf ("FAIL\n"); return 1; }
>     else
> diff --git a/gcc/testsuite/g++.old-deja/g++.mike/for2.C b/gcc/testsuite/g++.old-deja/g++.mike/for2.C
> index 6eb5d66675e..4a7c3042544 100644
> --- a/gcc/testsuite/g++.old-deja/g++.mike/for2.C
> +++ b/gcc/testsuite/g++.old-deja/g++.mike/for2.C
> @@ -14,7 +14,7 @@ void bar() {
>   
>   void bee () {
>     int i = 0;
> -  for (int fun() = 0; i != 2; ++i) {	// { dg-warning "extern" "extern" }
> +  for (int fun() = 0; i != 2; ++i) {	// { dg-warning "extern|empty parentheses" "extern" }
>     // { dg-error "initialized" "init" { target *-*-* } .-1 }
>     }
>   }
> diff --git a/gcc/testsuite/g++.old-deja/g++.other/local4.C b/gcc/testsuite/g++.old-deja/g++.other/local4.C
> index b5514a54b46..492ce2b7e70 100644
> --- a/gcc/testsuite/g++.old-deja/g++.other/local4.C
> +++ b/gcc/testsuite/g++.old-deja/g++.other/local4.C
> @@ -6,6 +6,6 @@ int f (int);
>   
>   int main ()
>   {
> -  int f ();
> +  int f (); // { dg-warning "empty parentheses" }
>     return f ();
>   }
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash3.C b/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
> index 52701b776ef..d1d9b12738c 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
> @@ -7,11 +7,13 @@ public:
>       {
>         // local-extern :)
>         CVector<int> v(); // { dg-message "old declaration" }
> +      // { dg-warning "empty parentheses" "" { target *-*-* } .-1 }
>          return v;		// { dg-error "convert" }
>       }
>       CVector<long> g() const
>       {
>         CVector<long> v(); // { dg-error "ambiguating new" }
> +      // { dg-warning "empty parentheses" "" { target *-*-* } .-1 }
>          return v;		// { dg-error "convert" }
>       }
>   };
> diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/lwg2993.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/lwg2993.cc
> index d1f0cafe688..c30511cab11 100644
> --- a/libstdc++-v3/testsuite/20_util/reference_wrapper/lwg2993.cc
> +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/lwg2993.cc
> @@ -43,7 +43,7 @@ test01()
>   void
>   test02()
>   {
> -  std::reference_wrapper<int> purr();
> +  std::reference_wrapper<int> purr(); // { dg-warning "empty parentheses" }
>   
>     // error, ambiguous: ICS exists from int prvalue to
>     // reference_wrapper<int> and from reference_wrapper<int> to int
> diff --git a/libstdc++-v3/testsuite/25_algorithms/generate_n/87982_neg.cc b/libstdc++-v3/testsuite/25_algorithms/generate_n/87982_neg.cc
> index 4236b5a820d..73934638ee3 100644
> --- a/libstdc++-v3/testsuite/25_algorithms/generate_n/87982_neg.cc
> +++ b/libstdc++-v3/testsuite/25_algorithms/generate_n/87982_neg.cc
> @@ -23,7 +23,7 @@
>   
>   void test01()
>   {
> -  int gen();
> +  int gen(); // { dg-warning "empty parentheses" }
>     int a[2];
>     std::generate_n(a, a+2, &gen);
>   }
> 
> base-commit: ffe6b4101501b5ada6f09a1fdf3822a23b68b5aa
> 



More information about the Gcc-patches mailing list