From 5b2003105b35f8fe8e074c055a718c8f484d9d32 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Fri, 2 Oct 2020 09:46:30 -0400 Subject: [PATCH] c++: Implement -Wvexing-parse [PR25814] 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. * g++.dg/warn/Wvexing-parse7.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 | 155 +++++++++++++++++- 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 +++ gcc/testsuite/g++.dg/warn/Wvexing-parse7.C | 27 +++ .../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 +- 27 files changed, 617 insertions(+), 23 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 create mode 100644 gcc/testsuite/g++.dg/warn/Wvexing-parse7.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 426636be839..5bfe3a68f66 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 26852f6f2e3..fde9f63ec05 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) @@ -3382,6 +3385,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 all the + parameters in the list have declarators 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)) @@ -6038,6 +6045,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 9428fa05258..98c1a5decf2 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4384,6 +4384,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.) */ @@ -14037,7 +14040,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 b0d5c69f1d6..e7bfbf649a5 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,129 @@ 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. DECL_SPECIFIERS is the decl-specifier-seq and DECLARATOR + is the declarator for this function declaration. */ + +static void +warn_about_ambiguous_parse (const cp_decl_specifier_seq *decl_specifiers, + const cp_declarator *declarator) +{ + /* Only warn if we are declaring a function at block scope. */ + if (!at_function_scope_p ()) + return; + + /* And only if there is no storage class specified. */ + if (decl_specifiers->storage_class != sc_none + || decl_spec_seq_has_spec_p (decl_specifiers, ds_typedef)) + return; + + if (declarator->kind != cdk_function + || !declarator->declarator + || declarator->declarator->kind != cdk_id + || !identifier_p (get_unqualified_id + (const_cast(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; + + tree type = decl_specifiers->type; + 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; + location_t loc = declarator->u.function.parens_loc; + 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 if (warning_at (loc, OPT_Wvexing_parse, + "parentheses were disambiguated as a function " + "declaration")) + { + gcc_rich_location iloc (loc); + 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"); + } + } +} + /* Declarators [gram.dcl.decl] */ /* Parse an init-declarator. @@ -20808,6 +20935,9 @@ cp_parser_init_declarator (cp_parser* parser, } } + if (!member_p && !cp_parser_error_occurred (parser)) + warn_about_ambiguous_parse (decl_specifiers, declarator); + /* Check to see if the token indicates the start of a function-definition. */ if (cp_parser_token_starts_function_definition_p (token)) @@ -21202,7 +21332,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) @@ -21349,6 +21479,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) @@ -21368,6 +21499,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); @@ -21432,6 +21565,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, @@ -21441,7 +21577,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 @@ -22708,7 +22845,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. */ @@ -22832,6 +22969,12 @@ cp_parser_parameter_declaration_list (cp_parser* parser, cp_parser_flags flags) *tail = build_tree_list (parameter->default_argument, decl); tail = &TREE_CHAIN (*tail); + /* 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. */ + if (index == 1 || PARENTHESIZED_LIST_P (parameters)) + PARENTHESIZED_LIST_P (parameters) = parenthesized_p; + /* Peek at the next token. */ if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN) || cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 5320e6c1e1e..812fa46e906 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 { }; // { 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 { }; // { 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 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 struct ac : ao { typedef c::e aq; }; template 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::o>::f> > az2(); + au::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 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 + +struct X { }; + +struct S { + S(X); + S(std::initializer_list); + int m; +}; + +struct T { + T(X); + int m; +}; + +struct W { + W(); + W(std::initializer_list); + 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) + S a{X()} -> (std::initializer_list) + */ + 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 +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++.dg/warn/Wvexing-parse7.C b/gcc/testsuite/g++.dg/warn/Wvexing-parse7.C new file mode 100644 index 00000000000..9f4c7021cdc --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wvexing-parse7.C @@ -0,0 +1,27 @@ +// PR c++/25814 +// { dg-do compile } + +struct X { }; +struct W { + W(X, X); +}; + +void +fn () +{ + W w1(X(), X()); // { dg-warning "parentheses" } + W w2(X(a), X()); // { dg-warning "parentheses" } + W w3(X(), X(a)); // { dg-warning "parentheses" } + W w4(X(a), X(b)); // { dg-warning "parentheses" } + W w5(X, X); + W w6(X(a), X); + W w7(X, X(a)); + W w8(X(a), X()); // { dg-warning "parentheses" } + W w9(X, X()); + W w10(X, X()); + + // Not function declarations. + W z1(X(), (X())); + W z2((X()), X()); + W z3((X()), (X())); +} 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 ccList cc_List::copy (){} int main (int, char **) { - ccList size1(); + ccList 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 v(); // { dg-message "old declaration" } + // { dg-warning "empty parentheses" "" { target *-*-* } .-1 } return v; // { dg-error "convert" } } CVector g() const { CVector 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 purr(); + std::reference_wrapper purr(); // { dg-warning "empty parentheses" } // error, ambiguous: ICS exists from int prvalue to // reference_wrapper and from reference_wrapper 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); } -- 2.30.2