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. 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; 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; + + 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; + + /* 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"); +} + /* 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); + /* 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 -- 2.28.0