public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: David Malcolm <dmalcolm@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: Martin Sebor <msebor@gmail.com>,
	Eric Gallager <egall@gwmail.gwu.edu>,
	gcc-patches List <gcc-patches@gcc.gnu.org>,
	David Malcolm <dmalcolm@redhat.com>
Subject: [PATCH] v2: C/C++: add fix-it hints for missing '&' and '*' (PR c++/87850)
Date: Thu, 15 Nov 2018 21:48:00 -0000	[thread overview]
Message-ID: <1542318534-62032-1-git-send-email-dmalcolm@redhat.com> (raw)
In-Reply-To: <CADzB+2m7ZMhyYi2taNEYirkerHTavC7RZaMLR6F+2dQztEGGtA@mail.gmail.com>

On Tue, 2018-11-13 at 16:34 -0500, Jason Merrill wrote:
> On Mon, Nov 12, 2018 at 4:32 PM Martin Sebor <msebor@gmail.com>
> wrote:
> > On 11/11/2018 02:02 PM, David Malcolm wrote:
> > > On Sun, 2018-11-11 at 11:01 -0700, Martin Sebor wrote:
> > > > On 11/10/2018 12:01 AM, Eric Gallager wrote:
> > > > > On 11/9/18, David Malcolm <dmalcolm@redhat.com> wrote:
> > > > > > This patch adds 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: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}
> > > > > >       |                      &
> > > > > 
> > > > > Having both the type and the fixit underneath the caret looks
> > > > > kind
> > > > > of confusing
> > > > 
> > > > I agree it's rather subtle.  Keeping the diagnostics separate
> > > > from
> > > > the suggested fix should avoid the confusion.
> > > 
> > > FWIW, the fix-it hint is in a different color (assuming that gcc
> > > is
> > > invoked in an environment that prints that...)
> > 
> > I figured it would be, but I'm still not sure it's good design
> > to be relying on color alone to distinguish between the problem
> > and the suggested fix.  Especially when they are so close to one
> > another and the fix is just a single character with no obvious
> > relationship to the rest of the text on the screen.  In other
> > warnings there's at least the "did you forget the '@'?" part
> > to give a clue, even though even there the connection between
> > the "did you forget" and the & several lines down wouldn't
> > necessarily be immediately apparent.
> 
> Agreed, something along those lines would help to understand why the
> compiler is throwing a random & into the diagnostic.
> 
> Jason

Here's an updated version which adds a note, putting the fix-it hint
on that instead (I attempted adding the text to the initial error,
but there was something of a combinatorial explosion of messages).

The above example becomes:

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,
      |                                ~~~~~~~~~~~~~~~^~~~~

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

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

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

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

gcc/testsuite/ChangeLog:
	PR c++/87850
	* c-c++-common/indirection-fixits.c: New test.
---
 gcc/c-family/c-common.c                         |  33 +++
 gcc/c-family/c-common.h                         |   2 +
 gcc/c/c-typeck.c                                |  10 +-
 gcc/cp/call.c                                   |   2 +
 gcc/testsuite/c-c++-common/indirection-fixits.c | 270 ++++++++++++++++++++++++
 5 files changed, 315 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/indirection-fixits.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index cd88f3a..d5c7c7f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "substring-locations.h"
 #include "spellcheck.h"
+#include "gcc-rich-location.h"
 #include "selftest.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
@@ -8405,6 +8406,38 @@ 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
+      && 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
+      && 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 31cc273..e765d3c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1340,6 +1340,8 @@ 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 tree braced_list_to_string (tree, tree);
 
 #if CHECKING_P
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 5d4e973..efa836d 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -7061,7 +7061,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	      if (pedwarn (&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:
@@ -7097,7 +7100,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	    if (pedwarn (&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.c b/gcc/cp/call.c
index ee099cc..a25d109 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6862,6 +6862,8 @@ convert_like_real (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 (complained && fn)
 	inform (get_fndecl_argument_location (fn, argnum),
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 0000000..4e45d66
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/indirection-fixits.c
@@ -0,0 +1,270 @@
+/* { 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+  /* { dg-message "possible fix: take the address with '&'" "" { target *-*-* } .-2 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+  /* { dg-message "possible fix: dereference with '*'" "" { target *-*-* } .-2 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* 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-warning "" "" { target c } } */
+  /* { dg-error "" "" { target c++ } .-1 } */
+
+  /* { 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 "" } */
+}
-- 
1.8.5.3

  reply	other threads:[~2018-11-15 21:48 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-09 22:36 [PATCH] " David Malcolm
2018-11-10  7:01 ` Eric Gallager
2018-11-11 18:01   ` Martin Sebor
2018-11-11 21:02     ` David Malcolm
2018-11-12 21:32       ` Martin Sebor
2018-11-13 21:34         ` Jason Merrill
2018-11-15 21:48           ` David Malcolm [this message]
2018-11-16 18:13             ` [PATCH] v2: " Jason Merrill
2018-11-19 21:23               ` [PATCH] v3: " David Malcolm
2018-11-20  2:46                 ` Joseph Myers
2018-11-20 22:05                   ` David Malcolm
2018-11-20 22:23                     ` Joseph Myers
2018-11-30 23:01                       ` [PATCH] v4: " David Malcolm
2018-12-01 18:38                         ` Jason Merrill
2018-12-03 22:14                           ` Joseph Myers
2018-12-05 16:03                             ` Jason Merrill
2018-12-05 16:18                               ` David Malcolm
2018-11-21  0:36                 ` [PATCH] v3: " Jeff Law
2018-11-21  0:39                 ` Martin Sebor
2018-11-30 15:27                   ` David Malcolm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1542318534-62032-1-git-send-email-dmalcolm@redhat.com \
    --to=dmalcolm@redhat.com \
    --cc=egall@gwmail.gwu.edu \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=msebor@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).