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.
This commit is contained in:
Marek Polacek 2020-10-02 09:46:30 -04:00
parent 22984f3f09
commit 5b2003105b
27 changed files with 617 additions and 23 deletions

View File

@ -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.

View File

@ -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 {

View File

@ -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)

View File

@ -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<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;
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)

View File

@ -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

View File

@ -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;

View File

@ -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" }

View File

@ -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);
}
};

View File

@ -3,5 +3,5 @@
int main ()
{
auto foo ();
extern auto foo ();
}

View File

@ -149,5 +149,5 @@ public:
class XclImpRoot : XclRoot {};
class XclImpColRowSettings : XclImpRoot {};
void lcl_ExportExcelBiff() {
XclRootData aExpData();
extern XclRootData aExpData();
}

View File

@ -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
}

View File

@ -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;

View File

@ -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>());
}

View File

@ -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();
}

View File

@ -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 }
}

View File

@ -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 "" } */
}

View File

@ -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" }
}

View File

@ -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" }
}

View File

@ -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()));
}

View File

@ -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.*
}

View File

@ -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" }
}

View File

@ -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

View File

@ -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 }
}
}

View File

@ -6,6 +6,6 @@ int f (int);
int main ()
{
int f ();
int f (); // { dg-warning "empty parentheses" }
return f ();
}

View File

@ -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" }
}
};

View File

@ -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

View File

@ -23,7 +23,7 @@
void test01()
{
int gen();
int gen(); // { dg-warning "empty parentheses" }
int a[2];
std::generate_n(a, a+2, &gen);
}