c++: Implement -Wvexing-parse [PR25814]
authorMarek Polacek <polacek@redhat.com>
Fri, 2 Oct 2020 13:46:30 +0000 (09:46 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 5 Nov 2020 20:55:14 +0000 (15:55 -0500)
commit5b2003105b35f8fe8e074c055a718c8f484d9d32
tree864228a0058125059a0930ea0abc69aef281e8df
parent22984f3f090921b5ac80ec0057f6754ec458e97e
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.
27 files changed:
gcc/c-family/c.opt
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parser.c
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp2a/fn-template16.C
gcc/testsuite/g++.dg/cpp2a/fn-template7.C
gcc/testsuite/g++.dg/lookup/pr80891-5.C
gcc/testsuite/g++.dg/lto/pr79050_0.C
gcc/testsuite/g++.dg/lto/pr84805_0.C
gcc/testsuite/g++.dg/parse/pr58898.C
gcc/testsuite/g++.dg/template/scope5.C
gcc/testsuite/g++.dg/warn/Wvexing-parse.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wvexing-parse7.C [new file with mode: 0644]
gcc/testsuite/g++.old-deja/g++.brendan/recurse.C
gcc/testsuite/g++.old-deja/g++.jason/template4.C
gcc/testsuite/g++.old-deja/g++.law/arm4.C
gcc/testsuite/g++.old-deja/g++.mike/for2.C
gcc/testsuite/g++.old-deja/g++.other/local4.C
gcc/testsuite/g++.old-deja/g++.pt/crash3.C
libstdc++-v3/testsuite/20_util/reference_wrapper/lwg2993.cc
libstdc++-v3/testsuite/25_algorithms/generate_n/87982_neg.cc