Commit 15c40a3b by David Malcolm Committed by David Malcolm

C++: better locations for bogus initializations (PR c++/88375)

PR c++/88375 reports that errors relating to invalid conversions in
initializations are reported at unhelpfully vague locations, as in
e.g.:

enum struct a : int {
  one, two
};

struct foo {
  int e1, e2;
  a e3;
} arr[] = {
  { 1, 2, a::one },
  { 3, a::two },
  { 4, 5, a::two }
};

for which g++ trunk emits the vague:

pr88375.cc:12:1: error: cannot convert 'a' to 'int' in initialization
   12 | };
      | ^

with the error at the final closing brace.

This patch uses location information for the initializers, converting the
above to:

pr88375.cc:10:11: error: cannot convert 'a' to 'int' in initialization
   10 |   { 3, a::two },
      |        ~~~^~~
      |           |
      |           a

highlighting which subexpression is problematic, and its type.

Ideally we'd also issue a note showing the field decl being initialized,
but that turned out to be more invasive.

gcc/cp/ChangeLog:
	PR c++/88375
	* typeck.c (convert_for_assignment): Capture location of rhs
	before stripping, and if available.  Use the location when
	complaining about bad conversions, labelling it with the
	rhstype if the location was present.
	* typeck2.c (digest_init_r): Capture location of init before
	stripping.

gcc/testsuite/ChangeLog:
	PR c++/88375
	* g++.dg/init/pr88375-2.C: New test.
	* g++.dg/init/pr88375.C: New test.

From-SVN: r267276
parent 4c187162
2018-12-19 David Malcolm <dmalcolm@redhat.com> 2018-12-19 David Malcolm <dmalcolm@redhat.com>
PR c++/88375
* typeck.c (convert_for_assignment): Capture location of rhs
before stripping, and if available. Use the location when
complaining about bad conversions, labelling it with the
rhstype if the location was present.
* typeck2.c (digest_init_r): Capture location of init before
stripping.
2018-12-19 David Malcolm <dmalcolm@redhat.com>
PR c++/87504 PR c++/87504
* call.c (op_error): Convert 1st param from location_t to * call.c (op_error): Convert 1st param from location_t to
const op_location_t &. Use binary_op_rich_location for binary const op_location_t &. Use binary_op_rich_location for binary
......
...@@ -8852,6 +8852,7 @@ convert_for_assignment (tree type, tree rhs, ...@@ -8852,6 +8852,7 @@ convert_for_assignment (tree type, tree rhs,
enum tree_code coder; enum tree_code coder;
location_t rhs_loc = EXPR_LOC_OR_LOC (rhs, input_location); location_t rhs_loc = EXPR_LOC_OR_LOC (rhs, input_location);
bool has_loc = EXPR_LOCATION (rhs) != UNKNOWN_LOCATION;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue, /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue,
but preserve location wrappers. */ but preserve location wrappers. */
if (TREE_CODE (rhs) == NON_LVALUE_EXPR if (TREE_CODE (rhs) == NON_LVALUE_EXPR
...@@ -8892,7 +8893,7 @@ convert_for_assignment (tree type, tree rhs, ...@@ -8892,7 +8893,7 @@ convert_for_assignment (tree type, tree rhs,
if (coder == VOID_TYPE) if (coder == VOID_TYPE)
{ {
if (complain & tf_error) if (complain & tf_error)
error ("void value not ignored as it ought to be"); error_at (rhs_loc, "void value not ignored as it ought to be");
return error_mark_node; return error_mark_node;
} }
...@@ -8964,35 +8965,43 @@ convert_for_assignment (tree type, tree rhs, ...@@ -8964,35 +8965,43 @@ convert_for_assignment (tree type, tree rhs,
rhstype, type, rhstype, type,
fndecl, parmnum); fndecl, parmnum);
else else
switch (errtype) {
{ range_label_for_type_mismatch label (rhstype, type);
gcc_rich_location richloc (rhs_loc, has_loc ? &label : NULL);
switch (errtype)
{
case ICR_DEFAULT_ARGUMENT: case ICR_DEFAULT_ARGUMENT:
error ("cannot convert %qH to %qI in default argument", error_at (&richloc,
rhstype, type); "cannot convert %qH to %qI in default argument",
rhstype, type);
break; break;
case ICR_ARGPASS: case ICR_ARGPASS:
error ("cannot convert %qH to %qI in argument passing", error_at (&richloc,
rhstype, type); "cannot convert %qH to %qI in argument passing",
rhstype, type);
break; break;
case ICR_CONVERTING: case ICR_CONVERTING:
error ("cannot convert %qH to %qI", error_at (&richloc, "cannot convert %qH to %qI",
rhstype, type); rhstype, type);
break; break;
case ICR_INIT: case ICR_INIT:
error ("cannot convert %qH to %qI in initialization", error_at (&richloc,
rhstype, type); "cannot convert %qH to %qI in initialization",
rhstype, type);
break; break;
case ICR_RETURN: case ICR_RETURN:
error ("cannot convert %qH to %qI in return", error_at (&richloc, "cannot convert %qH to %qI in return",
rhstype, type); rhstype, type);
break; break;
case ICR_ASSIGN: case ICR_ASSIGN:
error ("cannot convert %qH to %qI in assignment", error_at (&richloc,
rhstype, type); "cannot convert %qH to %qI in assignment",
rhstype, type);
break; break;
default: default:
gcc_unreachable(); gcc_unreachable();
} }
}
if (TYPE_PTR_P (rhstype) if (TYPE_PTR_P (rhstype)
&& TYPE_PTR_P (type) && TYPE_PTR_P (type)
&& CLASS_TYPE_P (TREE_TYPE (rhstype)) && CLASS_TYPE_P (TREE_TYPE (rhstype))
...@@ -9059,9 +9068,7 @@ convert_for_assignment (tree type, tree rhs, ...@@ -9059,9 +9068,7 @@ convert_for_assignment (tree type, tree rhs,
&& TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE && TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE
&& (complain & tf_warning)) && (complain & tf_warning))
{ {
location_t loc = cp_expr_loc_or_loc (rhs, input_location); warning_at (rhs_loc, OPT_Wparentheses,
warning_at (loc, OPT_Wparentheses,
"suggest parentheses around assignment used as truth value"); "suggest parentheses around assignment used as truth value");
TREE_NO_WARNING (rhs) = 1; TREE_NO_WARNING (rhs) = 1;
} }
......
...@@ -1050,14 +1050,16 @@ digest_init_r (tree type, tree init, int nested, int flags, ...@@ -1050,14 +1050,16 @@ digest_init_r (tree type, tree init, int nested, int flags,
complain)) complain))
return error_mark_node; return error_mark_node;
location_t loc = cp_expr_loc_or_loc (init, input_location);
tree stripped_init = init;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
(g++.old-deja/g++.law/casts2.C). */ (g++.old-deja/g++.law/casts2.C). */
if (TREE_CODE (init) == NON_LVALUE_EXPR) if (TREE_CODE (init) == NON_LVALUE_EXPR)
init = TREE_OPERAND (init, 0); stripped_init = TREE_OPERAND (init, 0);
location_t loc = cp_expr_loc_or_loc (init, input_location);
tree stripped_init = tree_strip_any_location_wrapper (init); stripped_init = tree_strip_any_location_wrapper (stripped_init);
/* Initialization of an array of chars from a string constant. The initializer /* Initialization of an array of chars from a string constant. The initializer
can be optionally enclosed in braces, but reshape_init has already removed can be optionally enclosed in braces, but reshape_init has already removed
......
2018-12-19 David Malcolm <dmalcolm@redhat.com> 2018-12-19 David Malcolm <dmalcolm@redhat.com>
PR c++/88375
* g++.dg/init/pr88375-2.C: New test.
* g++.dg/init/pr88375.C: New test.
2018-12-19 David Malcolm <dmalcolm@redhat.com>
* c-c++-common/Wtautological-compare-ranges.c: New test. * c-c++-common/Wtautological-compare-ranges.c: New test.
* g++.dg/cpp0x/pr51420.C: Add -fdiagnostics-show-caret and update * g++.dg/cpp0x/pr51420.C: Add -fdiagnostics-show-caret and update
expected output. expected output.
......
// { dg-do compile { target c++11 } }
// { dg-options "-fdiagnostics-show-caret" }
enum struct a : int {
one, two
};
constexpr int fn () { return 42; }
struct foo {
int e1, e2;
a e3;
} arr[] = {
{ 3, a::two }, // { dg-error "11: cannot convert 'a' to 'int' in initialization" }
/* { dg-begin-multiline-output "" }
{ 3, a::two },
~~~^~~
|
a
{ dg-end-multiline-output "" } */
{ 6, 7, fn() }, // { dg-error "13: cannot convert 'int' to 'a' in initialization" }
/* { dg-begin-multiline-output "" }
{ 6, 7, fn() },
~~^~
|
int
{ dg-end-multiline-output "" } */
};
struct bar {
const char *f1;
int f2;
} arr_2[] = {
{ 42 }, // { dg-error "5: invalid conversion from 'int' to 'const char\\*'" }
/* { dg-begin-multiline-output "" }
{ 42 },
^~
|
int
{ dg-end-multiline-output "" } */
};
// { dg-do compile { target c++11 } }
enum struct a : int {
one, two
};
constexpr int fn () { return 42; }
struct foo {
int e1, e2;
a e3;
} arr[] = {
{ 1, 2, a::one },
{ 3, a::two }, // { dg-error "11: cannot convert 'a' to 'int' in initialization" }
{ 6, 7, 8 }, // { dg-error "11: cannot convert 'int' to 'a' in initialization" }
{ 6, 7, fn() }, // { dg-error "13: cannot convert 'int' to 'a' in initialization" }
};
struct bar {
const char *f1;
int f2;
} arr_2[] = {
{ "hello world", 42 },
{ 42 }, // { dg-error "5: invalid conversion from 'int' to 'const char\\*'" }
{ "hello", "world" }, // { dg-error "14: invalid conversion from 'const char\\*' to 'int'" }
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment