Revisiting this patch from 2018 that didn't quite make it;
earlier versions were:
  v1: https://gcc.gnu.org/ml/gcc-patches/2018-11/msg00802.html
  v2: https://gcc.gnu.org/legacy-ml/gcc-patches/2018-11/msg01408.html
  v3: https://gcc.gnu.org/legacy-ml/gcc-patches/2018-11/msg01658.html
  v4: https://gcc.gnu.org/legacy-ml/gcc-patches/2018-11/msg02617.html

I believe the remaining point of discussion was about enum vs int:
  https://gcc.gnu.org/legacy-ml/gcc-patches/2018-12/msg00293.html
and that none of us had strong opinions on the matter.  I've added
some test coverage for that, and rebased it (e.g. for the .c to .cc
renaming of our sources).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
OK for trunk?


This patch adds a note with a fix-it hint to various
pointer-vs-non-pointer diagnostics, suggesting the addition of
a leading '&' or '*'.

For example, note the ampersand fix-it hint in the following:

demo.c: In function 'int main()':
demo.c:5:22: error: invalid conversion from 'pthread_key_t' {aka 'unsigned int'}
   to 'pthread_key_t*' {aka 'unsigned int*'} [-fpermissive]
    5 |   pthread_key_create(key, NULL);
      |                      ^~~
      |                      |
      |                      pthread_key_t {aka unsigned int}
demo.c:5:22: note: possible fix: take the address with '&'
    5 |   pthread_key_create(key, NULL);
      |                      ^~~
      |                      &
In file included from demo.c:1:
/usr/include/pthread.h:1122:47: note:   initializing argument 1 of
   'int pthread_key_create(pthread_key_t*, void (*)(void*))'
 1122 | extern int pthread_key_create (pthread_key_t *__key,
      |                                ~~~~~~~~~~~~~~~^~~~~

gcc/c-family/ChangeLog:
        PR c++/87850
        * c-common.cc: Include "gcc-rich-location.h".
        (maybe_emit_indirection_note): New function.
        * c-common.h (maybe_emit_indirection_note): New decl.
        (compatible_types_for_indirection_note_p): New decl.

gcc/c/ChangeLog:
        PR c++/87850
        * c-typeck.cc (compatible_types_for_indirection_note_p): New
        function.
        (convert_for_assignment): Call maybe_emit_indirection_note for
        pointer vs non-pointer diagnostics.

gcc/cp/ChangeLog:
        PR c++/87850
        * call.cc (convert_like_real): Call maybe_emit_indirection_note
        for "invalid conversion" diagnostic.
        * typeck.cc (compatible_types_for_indirection_note_p): New
        function.

gcc/testsuite/ChangeLog:
        PR c++/87850
        * c-c++-common/indirection-fixits.c: New test.
        * g++.dg/template/error60.C: Add fix-it hint to expected output.
        * g++.dg/template/error60a.C: Likewise.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/c-family/c-common.cc                      |  35 ++
 gcc/c-family/c-common.h                       |   4 +
 gcc/c/c-typeck.cc                             |  18 +-
 gcc/cp/call.cc                                |   2 +
 gcc/cp/typeck.cc                              |   8 +
 .../c-c++-common/indirection-fixits.c         | 317 ++++++++++++++++++
 gcc/testsuite/g++.dg/template/error60.C       |   5 +
 gcc/testsuite/g++.dg/template/error60a.C      |   5 +
 8 files changed, 392 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/indirection-fixits.c

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 1ffc63afbd3a..8cae3d1feb1f 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -56,6 +56,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "vec-perm-indices.h"
 #include "tree-pretty-print-markup.h"
 #include "gcc-urlifier.h"
+#include "gcc-rich-location.h"
 
 cpp_reader *parse_in;          /* Declared in c-pragma.h.  */
 
@@ -9640,6 +9641,40 @@ maybe_suggest_missing_token_insertion (rich_location 
*richloc,
     }
 }
 
+/* Potentially emit a note about likely missing '&' or '*',
+   depending on EXPR and EXPECTED_TYPE.  */
+
+void
+maybe_emit_indirection_note (location_t loc,
+                            tree expr, tree expected_type)
+{
+  gcc_assert (expr);
+  gcc_assert (expected_type);
+
+  tree actual_type = TREE_TYPE (expr);
+
+  /* Missing '&'.  */
+  if (TREE_CODE (expected_type) == POINTER_TYPE
+      && compatible_types_for_indirection_note_p (actual_type,
+                                                 TREE_TYPE (expected_type))
+      && lvalue_p (expr))
+    {
+      gcc_rich_location richloc (loc);
+      richloc.add_fixit_insert_before ("&");
+      inform (&richloc, "possible fix: take the address with %qs", "&");
+    }
+
+  /* Missing '*'.  */
+  if (TREE_CODE (actual_type) == POINTER_TYPE
+      && compatible_types_for_indirection_note_p (TREE_TYPE (actual_type),
+                                                 expected_type))
+    {
+      gcc_rich_location richloc (loc);
+      richloc.add_fixit_insert_before ("*");
+      inform (&richloc, "possible fix: dereference with %qs", "*");
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index a74530bafff6..a4c925278bb6 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1694,6 +1694,10 @@ extern void maybe_add_include_fixit (rich_location *, 
const char *, bool);
 extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
                                                   enum cpp_ttype token_type,
                                                   location_t prev_token_loc);
+extern void maybe_emit_indirection_note (location_t loc,
+                                        tree expr, tree expected_type);
+extern bool compatible_types_for_indirection_note_p (tree type1, tree type2);
+
 extern tree braced_lists_to_strings (tree, tree);
 
 #if CHECKING_P
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 8960c55aef3b..1624d3f96918 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1440,6 +1440,14 @@ struct comptypes_data {
   const struct tagged_tu_seen_cache* cache;
 };
 
+/* C implementation of compatible_types_for_indirection_note_p.  */
+
+bool
+compatible_types_for_indirection_note_p (tree type1, tree type2)
+{
+  return comptypes (type1, type2) == 1;
+}
+
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  Return 2 if they are compatible
    but a warning may be needed if you use them together.  */
@@ -8487,7 +8495,10 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
              if (permerror_opt (&richloc, OPT_Wint_conversion,
                                 "passing argument %d of %qE makes pointer "
                                 "from integer without a cast", parmnum, rname))
-               inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
+               {
+                 maybe_emit_indirection_note (expr_loc, rhs, type);
+                 inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
+               }
            }
            break;
          case ic_assign:
@@ -8527,7 +8538,10 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
            if (permerror_opt (&richloc, OPT_Wint_conversion,
                               "passing argument %d of %qE makes integer from "
                               "pointer without a cast", parmnum, rname))
-             inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
+             {
+               maybe_emit_indirection_note (expr_loc, rhs, type);
+               inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
+             }
          }
          break;
        case ic_assign:
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 3033be47c7d7..cc9761d68b40 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -8585,6 +8585,8 @@ convert_like_internal (conversion *convs, tree expr, tree 
fn, int argnum,
          complained = permerror (&richloc,
                                  "invalid conversion from %qH to %qI",
                                  TREE_TYPE (expr), totype);
+         if (complained)
+           maybe_emit_indirection_note (loc, expr, totype);
        }
       if (convs->kind == ck_ref_bind)
        expr = convert_to_reference (totype, expr, CONV_IMPLICIT,
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 4c15e26f692a..cf694555aac5 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1687,6 +1687,14 @@ structural_comptypes (tree t1, tree t2, int strict)
   return true;
 }
 
+/* C++ implementation of compatible_types_for_indirection_note_p.  */
+
+bool
+compatible_types_for_indirection_note_p (tree type1, tree type2)
+{
+  return same_type_p (type1, type2);
+}
+
 /* Return true if T1 and T2 are related as allowed by STRICT.  STRICT
    is a bitwise-or of the COMPARE_* flags.  */
 
diff --git a/gcc/testsuite/c-c++-common/indirection-fixits.c 
b/gcc/testsuite/c-c++-common/indirection-fixits.c
new file mode 100644
index 000000000000..0a658edd238d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/indirection-fixits.c
@@ -0,0 +1,317 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+void takes_int_ptr(int*);
+void takes_char_ptr(char*);
+void takes_int(int);
+int returns_int(void);
+
+int ivar;
+char cvar;
+int *int_ptr;
+char *char_ptr;
+
+void test_1 (void)
+{
+  takes_int_ptr(&ivar);
+  takes_int_ptr(int_ptr);
+  takes_char_ptr(&cvar);
+  takes_char_ptr(char_ptr);
+
+  ivar = 42;
+  cvar = 'b';
+  int_ptr = &ivar;
+  char_ptr = &cvar;
+}
+
+void test_2 (void)
+{
+  takes_int_ptr(ivar); /* { dg-error "" "" } */
+  /* { dg-message "possible fix: take the address with '&'" "" { target *-*-* 
} .-1 } */
+
+  /* Expect an '&' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr(ivar);
+                 ^~~~
+                 |
+                 int
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr(ivar);
+                 ^~~~
+                 &
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int_ptr(int*);
+                    ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+void test_3 (void)
+{
+  takes_int_ptr(cvar); /* { dg-error "" } */
+
+  /* Don't expect an '&' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr(cvar);
+                 ^~~~
+                 |
+                 char
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int_ptr(int*);
+                    ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+void test_4 (void)
+{
+  takes_char_ptr(ivar); /* { dg-error "" } */
+
+  /* Don't expect an '&' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_char_ptr(ivar);
+                  ^~~~
+                  |
+                  int
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_char_ptr(char*);
+                     ^~~~~
+     { dg-end-multiline-output "" } */
+}
+
+void test_5 (void)
+{
+  takes_char_ptr(cvar); /* { dg-error "" } */
+
+  /* Expect an '&' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_char_ptr(cvar); 
+                  ^~~~
+                  |
+                  char
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   takes_char_ptr(cvar); 
+                  ^~~~
+                  &
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_char_ptr(char*);
+                     ^~~~~
+     { dg-end-multiline-output "" } */
+}
+ 
+void test_6 (void)
+{
+  takes_int(int_ptr); /* { dg-error "" } */
+  /* { dg-message "possible fix: dereference with '*'" "" { target *-*-* } .-1 
} */
+
+  /* Expect a '*' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_int(int_ptr);
+             ^~~~~~~
+             |
+             int *
+     { dg-end-multiline-output "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   takes_int(int_ptr);
+             ^~~~~~~
+             |
+             int*
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   takes_int(int_ptr);
+             ^~~~~~~
+             *
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int(int);
+                ^~~
+     { dg-end-multiline-output "" } */
+}
+ 
+void test_7 (void)
+{
+  takes_int(char_ptr); /* { dg-error "" } */
+
+  /* Don't expect a '*' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_int(char_ptr);
+             ^~~~~~~~
+             |
+             char *
+     { dg-end-multiline-output "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   takes_int(char_ptr);
+             ^~~~~~~~
+             |
+             char*
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int(int);
+                ^~~
+     { dg-end-multiline-output "" } */
+}
+ 
+void test_8 (void)
+{
+  ivar = int_ptr; /* { dg-error "" } */
+
+  /* Expect a fix-it hint from the C++ FE, but not from C (due to missing
+     location).  */
+  /* { dg-begin-multiline-output "" }
+   ivar = int_ptr;
+          ^~~~~~~
+          |
+          int*
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   ivar = int_ptr;
+          ^~~~~~~
+          *
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   ivar = int_ptr;
+        ^
+     { dg-end-multiline-output "" { target c } } */
+}
+
+void test_9 (void)
+{
+  cvar = int_ptr; /* { dg-error "" } */
+
+  /* Don't expect a '*' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   cvar = int_ptr;
+          ^~~~~~~
+          |
+          int*
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   cvar = int_ptr;
+        ^
+     { dg-end-multiline-output "" { target c } } */
+}
+
+void test_10 (void)
+{
+  int_ptr = ivar; /* { dg-error "" } */
+
+  /* Expect a fix-it hint from the C++ FE, but not from C (due to missing
+     location).  */
+  /* { dg-begin-multiline-output "" }
+   int_ptr = ivar;
+             ^~~~
+             |
+             int
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   int_ptr = ivar;
+             ^~~~
+             &
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   int_ptr = ivar;
+           ^
+     { dg-end-multiline-output "" { target c } } */
+}
+
+void test_11 (void)
+{
+  char_ptr = ivar; /* { dg-error "" } */
+
+  /* Don't expect a fix-it hint, due to mismatching types.  */
+  /* { dg-begin-multiline-output "" }
+   char_ptr = ivar;
+              ^~~~
+              |
+              int
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+   char_ptr = ivar;
+            ^
+     { dg-end-multiline-output "" { target c } } */
+}
+
+/* We shouldn't offer '&' fix-it hints for non-lvalues.  */
+
+void test_12 (void)
+{
+  takes_int_ptr (returns_int ()); /* { dg-error "" } */
+
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr (returns_int ());
+                  ^~~~~~~~~~~~~~
+                  |
+                  int
+     { dg-end-multiline-output "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr (returns_int ());
+                  ~~~~~~~~~~~~^~
+                              |
+                              int
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int_ptr(int*);
+                    ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Ignore typedefs when offering fix-it hints.  */
+
+typedef int typedef_int_t;
+typedef_int_t typedef_int_t_var;
+
+void test_13 (void)
+{
+  takes_int_ptr (typedef_int_t_var); /* { dg-error "" } */
+  /* { dg-message "possible fix: take the address with '&'" "" { target *-*-* 
} .-1 } */
+
+  /* Expect an '&' fix-it hint.  */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr (typedef_int_t_var);
+                  ^~~~~~~~~~~~~~~~~
+                  |
+                  typedef_int_t {aka int}
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   takes_int_ptr (typedef_int_t_var);
+                  ^~~~~~~~~~~~~~~~~
+                  &
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int_ptr(int*);
+                    ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+enum foo
+{
+ FOO_0,         
+ FOO_1,         
+ FOO_2
+};
+
+void test_14 (void)
+{
+  enum foo f;
+  takes_int_ptr (f); /* { dg-error "" } */
+  /* We don't expect a fix-it hint here.  */
+  /* { dg-begin-multiline-output  "" }
+   takes_int_ptr (f);
+                  ^
+                  |
+                  enum foo
+     { dg-end-multiline-output "" { target c } } */
+  /* { dg-begin-multiline-output  "" }
+   takes_int_ptr (f);
+                  ^
+                  |
+                  foo
+     { dg-end-multiline-output "" { target c++ } } */
+  /* { dg-begin-multiline-output "" }
+ void takes_int_ptr(int*);
+                    ^~~~
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/template/error60.C 
b/gcc/testsuite/g++.dg/template/error60.C
index 8c2139b207c4..7d578b166cb3 100644
--- a/gcc/testsuite/g++.dg/template/error60.C
+++ b/gcc/testsuite/g++.dg/template/error60.C
@@ -35,3 +35,8 @@ void usage ()
                         |
                         int
      { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   my_pointer<Foo> ptr (val);
+                        ^~~
+                        &
+     { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/template/error60a.C 
b/gcc/testsuite/g++.dg/template/error60a.C
index 9d0f170c18ba..41af458d3f07 100644
--- a/gcc/testsuite/g++.dg/template/error60a.C
+++ b/gcc/testsuite/g++.dg/template/error60a.C
@@ -44,3 +44,8 @@ void usage ()
                         |
                         int
      { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   my_pointer<Foo> ptr (val);
+                        ^~~
+                        &
+     { dg-end-multiline-output "" } */
-- 
2.26.3

Reply via email to