C++: better locations for bogus initializations (PR c++/88375)
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 19 Dec 2018 15:22:27 +0000 (15:22 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Wed, 19 Dec 2018 15:22:27 +0000 (15:22 +0000)
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

gcc/cp/ChangeLog
gcc/cp/typeck.c
gcc/cp/typeck2.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/init/pr88375-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/pr88375.C [new file with mode: 0644]

index beae2659c7442dbd7f02d18d339671f0c3b7da88..7b794612c8be166ffb94f04872ff8d3dae0ec0b5 100644 (file)
@@ -1,3 +1,13 @@
+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
index 94a33d41dbafefaec6d363e847e9c020649fd846..ef317f5cc7dce71bf1a70cd80cfceccd303575a8 100644 (file)
@@ -8852,6 +8852,7 @@ convert_for_assignment (tree type, tree rhs,
   enum tree_code coder;
 
   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,
      but preserve location wrappers.  */
   if (TREE_CODE (rhs) == NON_LVALUE_EXPR
@@ -8892,7 +8893,7 @@ convert_for_assignment (tree type, tree rhs,
   if (coder == VOID_TYPE)
     {
       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;
     }
 
@@ -8964,35 +8965,43 @@ convert_for_assignment (tree type, tree rhs,
                                             rhstype, type,
                                             fndecl, parmnum);
              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:
-                     error ("cannot convert %qH to %qI in default argument",
-                            rhstype, type);
+                     error_at (&richloc,
+                               "cannot convert %qH to %qI in default argument",
+                               rhstype, type);
                      break;
                    case ICR_ARGPASS:
-                     error ("cannot convert %qH to %qI in argument passing",
-                            rhstype, type);
+                     error_at (&richloc,
+                               "cannot convert %qH to %qI in argument passing",
+                               rhstype, type);
                      break;
                    case ICR_CONVERTING:
-                     error ("cannot convert %qH to %qI",
-                            rhstype, type);
+                     error_at (&richloc, "cannot convert %qH to %qI",
+                               rhstype, type);
                      break;
                    case ICR_INIT:
-                     error ("cannot convert %qH to %qI in initialization",
-                            rhstype, type);
+                     error_at (&richloc,
+                               "cannot convert %qH to %qI in initialization",
+                               rhstype, type);
                      break;
                    case ICR_RETURN:
-                     error ("cannot convert %qH to %qI in return",
-                            rhstype, type);
+                     error_at (&richloc, "cannot convert %qH to %qI in return",
+                               rhstype, type);
                      break;
                    case ICR_ASSIGN:
-                     error ("cannot convert %qH to %qI in assignment",
-                            rhstype, type);
+                     error_at (&richloc,
+                               "cannot convert %qH to %qI in assignment",
+                               rhstype, type);
                      break;
                    default:
                      gcc_unreachable();
                  }
+               }
              if (TYPE_PTR_P (rhstype)
                  && TYPE_PTR_P (type)
                  && CLASS_TYPE_P (TREE_TYPE (rhstype))
@@ -9059,9 +9068,7 @@ convert_for_assignment (tree type, tree rhs,
       && TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE
       && (complain & tf_warning))
     {
-      location_t loc = cp_expr_loc_or_loc (rhs, input_location);
-
-      warning_at (loc, OPT_Wparentheses,
+      warning_at (rhs_loc, OPT_Wparentheses,
                  "suggest parentheses around assignment used as truth value");
       TREE_NO_WARNING (rhs) = 1;
     }
index c1fa4a94843de3f879c1a73444bae7f84e4c7127..209832b19ea9eda4745d3edf461c39b09d2757d1 100644 (file)
@@ -1050,14 +1050,16 @@ digest_init_r (tree type, tree init, int nested, int flags,
                                        complain))
     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
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
-    init = TREE_OPERAND (init, 0);
-
-  location_t loc = cp_expr_loc_or_loc (init, input_location);
+    stripped_init = TREE_OPERAND (init, 0);
 
-  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
      can be optionally enclosed in braces, but reshape_init has already removed
index 4a613382a07143d51b68a5eebd9fb5576f33bf7a..b996242f00b07b6adaa1e3cfb3e963f874028e41 100644 (file)
@@ -1,3 +1,9 @@
+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.
diff --git a/gcc/testsuite/g++.dg/init/pr88375-2.C b/gcc/testsuite/g++.dg/init/pr88375-2.C
new file mode 100644 (file)
index 0000000..97ed72e
--- /dev/null
@@ -0,0 +1,41 @@
+// { 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 "" } */
+};
diff --git a/gcc/testsuite/g++.dg/init/pr88375.C b/gcc/testsuite/g++.dg/init/pr88375.C
new file mode 100644 (file)
index 0000000..21dd6c1
--- /dev/null
@@ -0,0 +1,26 @@
+// { 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'" }
+};