Bug 56856 - -Wformat warnings don't show location *within* format string in C++ FE
Summary: -Wformat warnings don't show location *within* format string in C++ FE
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.0
: P3 normal
Target Milestone: ---
Assignee: David Malcolm
URL:
Keywords: diagnostic, patch
: 71863 85948 (view as bug list)
Depends on:
Blocks: 52952 86567
  Show dependency treegraph
 
Reported: 2013-04-06 10:47 UTC by Manuel López-Ibáñez
Modified: 2024-01-20 17:00 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.9.3, 5.3.0, 6.1.0, 7.0
Last reconfirmed: 2016-07-13 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Manuel López-Ibáñez 2013-04-06 10:47:55 UTC
$ cc1plus gcc/testsuite/g++.dg/ext/builtin4.C -Wall
gcc/testsuite/g++.dg/ext/builtin4.C:9:14: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
   printf("%d");   // { dg-warning "expects a matching" }
              ^

Unfortunately, the format_tree we get at the time of the warning does not seem to have a valid location either:

(gdb) p debug_tree(format_tree)
 <nop_expr 0x7ffff7567660
    type <pointer_type 0x7ffff741d9d8
        type <integer_type 0x7ffff741d930 char readonly string-flag QI
            size <integer_cst 0x7ffff73f7f80 constant 8>
            unit size <integer_cst 0x7ffff73f7fa0 constant 1>
            align 8 symtab 0 alias set -1 canonical type 0x7ffff741d930 precision 8 min <integer_cst 0x7ffff73f7fe0 -128> max <integer_cst 0x7ffff7414020 127>
            pointer_to_this <pointer_type 0x7ffff741d9d8>>
        unsigned SI
        size <integer_cst 0x7ffff73f7dc0 constant 32>
        unit size <integer_cst 0x7ffff73f7de0 constant 4>
        align 32 symtab 0 alias set -1 canonical type 0x7ffff741d9d8
        pointer_to_this <pointer_type 0x7ffff7420690>>
    readonly constant
    arg 0 <addr_expr 0x7ffff7567640
        type <pointer_type 0x7ffff755dbd0 type <array_type 0x7ffff755da80>
            unsigned SI size <integer_cst 0x7ffff73f7dc0 32> unit size <integer_cst 0x7ffff73f7de0 4>
            align 32 symtab 0 alias set -1 canonical type 0x7ffff755dbd0>
        readonly constant
        arg 0 <string_cst 0x7ffff7565990 type <array_type 0x7ffff755da80>
            readonly constant static "%d\000">>>
Comment 1 Manuel López-Ibáñez 2013-04-06 10:50:20 UTC
And this blocks PR52952 because with the *correct* offset, we get this in C++:

/home/manuel/test2/src/gcc/testsuite/g++.dg/ext/builtin4.C:9:16: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
   printf("%d");   // { dg-warning "expects a matching" }
                ^

which is even worse than without offset locations.
Comment 2 Manuel López-Ibáñez 2015-10-22 21:18:33 UTC
*** Bug 68052 has been marked as a duplicate of this bug. ***
Comment 3 Martin Sebor 2016-07-13 18:47:59 UTC
*** Bug 71863 has been marked as a duplicate of this bug. ***
Comment 4 Martin Sebor 2016-07-13 18:49:02 UTC
Confirmed.  See the duplicate bug 71863 for another test case.
Comment 5 Martin Sebor 2016-07-13 19:26:45 UTC
I thought I'd look into this bug since it affects the testing of my patch for bug 49905 and I'm finding out that it seems to be a general problem with C++ and function arguments.  From what I can see, the C front end tracks the locations of function arguments through the parser, passing them from function to function, while the C++ front end doesn't.  Sadly, it doesn't look like a simple fix...

$ cat xyz.c && /build/gcc-trunk-svn/gcc/xgcc -B /build/gcc-trunk-svn/gcc -S -Wall -Wextra -Wpedantic -Wformat -xc++ xyz.c
void f (int, int, int);

void g (void)
{
  f (0, "", 1);
}
xyz.c: In function ‘void g()’:
xyz.c:5:14: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
   f (0, "", 1);
              ^
xyz.c:1:6: note:   initializing argument 2 of ‘void f(int, int, int)’
 void f (int, int, int);
      ^
Comment 6 Manuel López-Ibáñez 2016-07-13 21:03:36 UTC
(In reply to Martin Sebor from comment #5)
> I thought I'd look into this bug since it affects the testing of my patch
> for bug 49905 and I'm finding out that it seems to be a general problem with
> C++ and function arguments.  From what I can see, the C front end tracks the
> locations of function arguments through the parser, passing them from
> function to function, while the C++ front end doesn't.  Sadly, it doesn't
> look like a simple fix...

This happens for "expressions" that don't have a location (such as constants, variable uses, etc.). I'm not even sure that passing the location is enough, since the C++ parser will do a lot of tentative parsing and, by the time you call the function, input_location is too far away and the original location of the argument is lost. You may need to store the locations together with the arguments when they are parsed and somehow keep them together throughout the C++ FE. Which is of course redundant when the expressions actually have a location.


At the end, everything comes back to fixing PR43486
Comment 7 Martin Sebor 2016-07-13 22:35:04 UTC
Thanks for the background and the pointer.  Is this report then a duplicate of bug 43486?
Comment 8 Manuel López-Ibáñez 2016-07-14 20:14:28 UTC
(In reply to Martin Sebor from comment #7)
> Thanks for the background and the pointer.  Is this report then a duplicate
> of bug 43486?

It may be possible to fix this one imperfectly without fixing PR43486. As you say, one can start passing locations from the parsing point to the warning point case by case and eventually you will cover most testcases.

Many people have tried to fix PR43486 and eventually desisted. It doesn't seem to be a problem that can be solved piecemeal: either you have a thoroughly designed solution that is supported by the maintainers and you commit yourself to do the transition in one go and fix all the fall-out or it will never happen.
Comment 9 David Malcolm 2018-10-04 09:43:53 UTC
*** Bug 85948 has been marked as a duplicate of this bug. ***
Comment 10 David Malcolm 2018-10-04 14:20:50 UTC
Candidate patch: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00269.html
Comment 11 David Malcolm 2018-10-05 19:02:49 UTC
Author: dmalcolm
Date: Fri Oct  5 19:02:17 2018
New Revision: 264887

URL: https://gcc.gnu.org/viewcvs?rev=264887&root=gcc&view=rev
Log:
Support string locations for C++ in -Wformat (PR c++/56856)

-Wformat in the C++ FE doesn't work as well as it could:
(a) it doesn't report precise locations within the string literal, and
(b) it doesn't underline arguments for those arguments !CAN_HAVE_LOCATION_P,
despite having location wrapper nodes.

For example:

  Wformat-ranges.C:32:10: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=]
  32 |   printf("hello %s", 42);
     |          ^~~~~~~~~~

(a) is due to not wiring up the langhook for extracting substring
    locations.

    This patch uses the one in c-family; it also fixes string literal
    parsing so that it records string concatenations (needed for
    extracting substring locations from concatenated strings).

(b) is due to the call to maybe_constant_value here:
       fargs[j] = maybe_constant_value (argarray[j]);
    within build_over_call.

    The patch fixes this by building a vec of location_t values when
    calling check_function_arguments.
    I attempted to eliminate the maybe_constant_value call here, but
    it's needed by e.g. check_function_sentinel for detecting NULL,
    and that code is in "c-family", so it can't simply call into
    maybe_constant_value (which is in "cp").

With this patch, the output for the above example is improved to:

  Wformat-ranges.C:32:18: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=]
  32 |   printf("hello %s", 42);
     |                 ~^   ~~
     |                  |   |
     |                  |   int
     |                  char*
     |                 %d

gcc/cp/ChangeLog:
	PR c++/56856
	* call.c (build_over_call): Build a vec of locations of the
	arguments before the call to maybe_constant_value, and pass to
	check_function_arguments.
	* cp-lang.c (LANG_HOOKS_GET_SUBSTRING_LOCATION): Define as
	c_get_substring_location.
	* parser.c (cp_parser_string_literal): Capture string
	concatenation locations.

gcc/ChangeLog:
	PR c++/56856
	* input.c (expand_location_to_spelling_point): Add param "aspect"
	and use rather than hardcoding LOCATION_ASPECT_CARET.
	(get_substring_ranges_for_loc): Handle the case of a single token
	within a macro expansion.
	* input.h (expand_location_to_spelling_point): Add "aspect" param,
	defaulting to LOCATION_ASPECT_CARET.

gcc/testsuite/ChangeLog:
	PR c++/56856
	* g++.dg/ext/builtin4.C: Set expected location for warning to the
	correct location within the format string.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the plugin and
	files for testing locations within string literal locations from
	the C frontend.
	* g++.dg/warn/Wformat-method.C: New test.
	* g++.dg/warn/Wformat-pr71863.C: New test.
	* g++.dg/warn/Wformat-ranges-c++11.C: New test.
	* g++.dg/warn/Wformat-ranges.C: New test, based on
	gcc.dg/format/diagnostic-ranges.c.
	* gcc.dg/plugin/diagnostic-test-string-literals-1.c
	(test_multitoken_macro): Generalize expected output to work with
	both C and C++.
	* gcc.dg/plugin/diagnostic-test-string-literals-2.c
	(test_stringified_token_1): Likewise.
	(test_stringified_token_3): Likewise.


Added:
    trunk/gcc/testsuite/g++.dg/warn/Wformat-method.C
    trunk/gcc/testsuite/g++.dg/warn/Wformat-pr71863.C
    trunk/gcc/testsuite/g++.dg/warn/Wformat-ranges-c++11.C
    trunk/gcc/testsuite/g++.dg/warn/Wformat-ranges.C
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/cp/cp-lang.c
    trunk/gcc/cp/parser.c
    trunk/gcc/input.c
    trunk/gcc/input.h
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/g++.dg/ext/builtin4.C
    trunk/gcc/testsuite/g++.dg/plugin/plugin.exp
    trunk/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
    trunk/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c
    trunk/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c
Comment 12 David Malcolm 2018-10-05 19:04:18 UTC
Should be fixed for gcc 9 by r264887.
Comment 13 David Malcolm 2018-10-29 23:44:41 UTC
Author: dmalcolm
Date: Mon Oct 29 23:44:10 2018
New Revision: 265609

URL: https://gcc.gnu.org/viewcvs?rev=265609&root=gcc&view=rev
Log:
Folding and check_function_arguments

This patch eliminates the arglocs array I introduced to build_over_call
in r264887, and eliminates the call to maybe_constant_value when building
"fargs" (thus retaining location wrapper nodes).

Instead, this patch requires that any checks within
check_function_arguments that need folded arguments do their own folding.

Of the various checks:
(a) check_function_nonnull already calls fold_for_warn,
(b) check_function_format doesn't need folding
(c) check_function_sentinel needs fold_for_warn in one place, which the
patch adds, and
(d) check_function_restrict needs per-argument folding, which the patch
adds.  Given that it scans before and after resetting TREE_VISITED on
each argument, it seemed best to make a copy of the array, folding each
argument from the outset, rather than repeatedly calling fold_for_warn;

gcc/c-family/ChangeLog:
	PR c++/56856
	* c-common.c (check_function_sentinel): Call fold_for_warn on the
	argument.
	(check_function_restrict): Rename param "argarray" to
	"unfolded_argarray", and make a copy named "argarray", calling
	fold_for_warn on each argument.
	(check_function_arguments): Add note about responsibility for
	folding the arguments.

gcc/cp/ChangeLog:
	PR c++/56856
	* call.c (build_over_call): Eliminate the "arglocs" array, and the
	call to maybe_constant_value when building "fargs".


Modified:
    trunk/gcc/c-family/ChangeLog
    trunk/gcc/c-family/c-common.c
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c