diff options
author | David Malcolm <dmalcolm@redhat.com> | 2024-11-27 19:21:15 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2024-11-27 19:21:15 -0500 |
commit | 5336b63fe81029cfd790208fbc69a08c70b82b01 (patch) | |
tree | ef4e3d88378a00915f2a6e2a508781450aacbfe2 | |
parent | 9f06b910a840d8ed06e27942bc23f260a0e0ccf3 (diff) | |
download | gcc-5336b63fe81029cfd790208fbc69a08c70b82b01.zip gcc-5336b63fe81029cfd790208fbc69a08c70b82b01.tar.gz gcc-5336b63fe81029cfd790208fbc69a08c70b82b01.tar.bz2 |
C/C++: add fix-it hints for missing '&' and '*' (v5) [PR87850]
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 <dmalcolm@redhat.com>
-rw-r--r-- | gcc/c-family/c-common.cc | 35 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 4 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 18 | ||||
-rw-r--r-- | gcc/cp/call.cc | 2 | ||||
-rw-r--r-- | gcc/cp/typeck.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/indirection-fixits.c | 317 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/error60.C | 5 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/error60a.C | 5 |
8 files changed, 392 insertions, 2 deletions
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 7fcd978..7214071 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-vector-builder.h" #include "vec-perm-indices.h" #include "tree-pretty-print-markup.h" +#include "gcc-rich-location.h" cpp_reader *parse_in; /* Declared in c-pragma.h. */ @@ -9630,6 +9631,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 e079096..a323982 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1697,6 +1697,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 f465123..e429ce9 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1438,6 +1438,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 2179332..9fbc160 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -8584,6 +8584,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 102a8ed..6e7f5e8 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -1686,6 +1686,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 0000000..0a658ed --- /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 8c2139b..7d578b1 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 9d0f170..41af458 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 "" } */ |