Branch data Line data Source code
1 : : /* Source locations within string literals.
2 : : Copyright (C) 2016-2024 Free Software Foundation, Inc.
3 : :
4 : : This file is part of GCC.
5 : :
6 : : GCC is free software; you can redistribute it and/or modify it under
7 : : the terms of the GNU General Public License as published by the Free
8 : : Software Foundation; either version 3, or (at your option) any later
9 : : version.
10 : :
11 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : : for more details.
15 : :
16 : : You should have received a copy of the GNU General Public License
17 : : along with GCC; see the file COPYING3. If not see
18 : : <http://www.gnu.org/licenses/>. */
19 : :
20 : : #include "config.h"
21 : : #include "system.h"
22 : : #include "coretypes.h"
23 : : #include "intl.h"
24 : : #include "diagnostic.h"
25 : : #include "cpplib.h"
26 : : #include "tree.h"
27 : : #include "langhooks.h"
28 : : #include "substring-locations.h"
29 : : #include "gcc-rich-location.h"
30 : :
31 : : /* format_string_diagnostic_t's ctor, giving information for use by
32 : : the emit_warning* member functions, as follows:
33 : :
34 : : They attempt to obtain precise location information within a string
35 : : literal from FMT_LOC.
36 : :
37 : : Case 1: if substring location is available, and is within the range of
38 : : the format string itself, the primary location of the
39 : : diagnostic is the substring range obtained from FMT_LOC, with the
40 : : caret at the *end* of the substring range.
41 : :
42 : : For example:
43 : :
44 : : test.c:90:10: warning: problem with '%i' here [-Wformat=]
45 : : printf ("hello %i", msg);
46 : : ~^
47 : :
48 : : Case 2: if the substring location is available, but is not within
49 : : the range of the format string, the primary location is that of the
50 : : format string, and a note is emitted showing the substring location.
51 : :
52 : : For example:
53 : : test.c:90:10: warning: problem with '%i' here [-Wformat=]
54 : : printf("hello " INT_FMT " world", msg);
55 : : ^~~~~~~~~~~~~~~~~~~~~~~~~
56 : : test.c:19: note: format string is defined here
57 : : #define INT_FMT "%i"
58 : : ~^
59 : :
60 : : Case 3: if precise substring information is unavailable, the primary
61 : : location is that of the whole string passed to FMT_LOC's constructor.
62 : : For example:
63 : :
64 : : test.c:90:10: warning: problem with '%i' here [-Wformat=]
65 : : printf(fmt, msg);
66 : : ^~~
67 : :
68 : : For each of cases 1-3, if param_loc is not UNKNOWN_LOCATION, then it is used
69 : : as a secondary range within the warning. For example, here it
70 : : is used with case 1:
71 : :
72 : : test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
73 : : printf ("foo %s bar", long_i + long_j);
74 : : ~^ ~~~~~~~~~~~~~~~
75 : :
76 : : and here with case 2:
77 : :
78 : : test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
79 : : printf ("foo " STR_FMT " bar", long_i + long_j);
80 : : ^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
81 : : test.c:89:16: note: format string is defined here
82 : : #define STR_FMT "%s"
83 : : ~^
84 : :
85 : : and with case 3:
86 : :
87 : : test.c:90:10: warning: '%i' here, but arg 2 is "const char *' [-Wformat=]
88 : : printf(fmt, msg);
89 : : ^~~ ~~~
90 : :
91 : : If non-NULL, then FMT_LABEL will be used to label the location within the
92 : : string for cases 1 and 2; if non-NULL, then PARAM_LABEL will be used to label
93 : : the parameter. For example with case 1:
94 : :
95 : : test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
96 : : printf ("foo %s bar", long_i + long_j);
97 : : ~^ ~~~~~~~~~~~~~~~
98 : : |
99 : : int
100 : :
101 : : and with case 2:
102 : :
103 : : test.c:90:10: warning: problem with '%i' here [-Wformat=]
104 : : printf("hello " INT_FMT " world", msg);
105 : : ^~~~~~~~~~~~~~~~~~~~~~~~~
106 : : test.c:19: note: format string is defined here
107 : : #define INT_FMT "%i"
108 : : ~^
109 : : |
110 : : int
111 : :
112 : : If CORRECTED_SUBSTRING is non-NULL, use it for cases 1 and 2 to provide
113 : : a fix-it hint, suggesting that it should replace the text within the
114 : : substring range. For example:
115 : :
116 : : test.c:90:10: warning: problem with '%i' here [-Wformat=]
117 : : printf ("hello %i", msg);
118 : : ~^
119 : : %s
120 : :
121 : : */
122 : :
123 : 10529 : format_string_diagnostic_t::
124 : : format_string_diagnostic_t (const substring_loc &fmt_loc,
125 : : const range_label *fmt_label,
126 : : location_t param_loc,
127 : : const range_label *param_label,
128 : 10529 : const char *corrected_substring)
129 : 10529 : : m_fmt_loc (fmt_loc),
130 : 10529 : m_fmt_label (fmt_label),
131 : 10529 : m_param_loc (param_loc),
132 : 10529 : m_param_label (param_label),
133 : 10529 : m_corrected_substring (corrected_substring)
134 : : {
135 : 10529 : }
136 : :
137 : : /* Emit a warning governed by option OPT, using SINGULAR_GMSGID as the
138 : : format string (or if PLURAL_GMSGID is different from SINGULAR_GMSGID,
139 : : using SINGULAR_GMSGID, PLURAL_GMSGID and N as arguments to ngettext)
140 : : and AP as its arguments.
141 : :
142 : : Return true if a warning was emitted, false otherwise. */
143 : :
144 : : bool
145 : 10529 : format_string_diagnostic_t::emit_warning_n_va (int opt,
146 : : unsigned HOST_WIDE_INT n,
147 : : const char *singular_gmsgid,
148 : : const char *plural_gmsgid,
149 : : va_list *ap) const
150 : : {
151 : 10529 : bool substring_within_range = false;
152 : 10529 : location_t primary_loc;
153 : 10529 : location_t fmt_substring_loc = UNKNOWN_LOCATION;
154 : 10529 : source_range fmt_loc_range
155 : 10529 : = get_range_from_loc (line_table, m_fmt_loc.get_fmt_string_loc ());
156 : 10529 : const char *err = m_fmt_loc.get_location (&fmt_substring_loc);
157 : 10529 : source_range fmt_substring_range
158 : 10529 : = get_range_from_loc (line_table, fmt_substring_loc);
159 : 10529 : if (err)
160 : : /* Case 3: unable to get substring location. */
161 : 3177 : primary_loc = m_fmt_loc.get_fmt_string_loc ();
162 : : else
163 : : {
164 : 7352 : if (fmt_substring_range.m_start >= fmt_loc_range.m_start
165 : 5737 : && fmt_substring_range.m_start <= fmt_loc_range.m_finish
166 : 5587 : && fmt_substring_range.m_finish >= fmt_loc_range.m_start
167 : 5576 : && fmt_substring_range.m_finish <= fmt_loc_range.m_finish)
168 : : /* Case 1. */
169 : : {
170 : 5574 : substring_within_range = true;
171 : 5574 : primary_loc = fmt_substring_loc;
172 : : }
173 : : else
174 : : /* Case 2. */
175 : : {
176 : 1778 : substring_within_range = false;
177 : 1778 : primary_loc = m_fmt_loc.get_fmt_string_loc ();
178 : : }
179 : : }
180 : :
181 : : /* Only use fmt_label in the initial warning for case 1. */
182 : 10529 : const range_label *primary_label = NULL;
183 : 4955 : if (substring_within_range)
184 : 5574 : primary_label = m_fmt_label;
185 : :
186 : 10529 : auto_diagnostic_group d;
187 : 10529 : gcc_rich_location richloc (primary_loc, primary_label);
188 : :
189 : 10529 : if (m_param_loc != UNKNOWN_LOCATION)
190 : 1586 : richloc.add_range (m_param_loc, SHOW_RANGE_WITHOUT_CARET, m_param_label);
191 : :
192 : 10529 : if (!err && m_corrected_substring && substring_within_range)
193 : 941 : richloc.add_fixit_replace (fmt_substring_range, m_corrected_substring);
194 : :
195 : 10529 : diagnostic_info diagnostic;
196 : 10529 : if (singular_gmsgid != plural_gmsgid)
197 : : {
198 : 544 : unsigned long gtn;
199 : :
200 : 544 : if (sizeof n <= sizeof gtn)
201 : 544 : gtn = n;
202 : : else
203 : : /* Use the largest number ngettext can handle, otherwise
204 : : preserve the six least significant decimal digits for
205 : : languages where the plural form depends on them. */
206 : : gtn = n <= ULONG_MAX ? n : n % 1000000LU + 1000000LU;
207 : :
208 : 544 : const char *text = ngettext (singular_gmsgid, plural_gmsgid, gtn);
209 : 544 : diagnostic_set_info_translated (&diagnostic, text, ap, &richloc,
210 : : DK_WARNING);
211 : : }
212 : : else
213 : 9985 : diagnostic_set_info (&diagnostic, singular_gmsgid, ap, &richloc,
214 : : DK_WARNING);
215 : 10529 : diagnostic.option_index = opt;
216 : 10529 : bool warned = diagnostic_report_diagnostic (global_dc, &diagnostic);
217 : :
218 : 10529 : if (!err && fmt_substring_loc && !substring_within_range)
219 : : /* Case 2. */
220 : 1778 : if (warned)
221 : : {
222 : : /* Use fmt_label in the note for case 2. */
223 : 55 : rich_location substring_richloc (line_table, fmt_substring_loc,
224 : 55 : m_fmt_label);
225 : 55 : if (m_corrected_substring)
226 : 28 : substring_richloc.add_fixit_replace (fmt_substring_range,
227 : : m_corrected_substring);
228 : 55 : inform (&substring_richloc,
229 : : "format string is defined here");
230 : 55 : }
231 : :
232 : 10529 : return warned;
233 : 10529 : }
234 : :
235 : : /* Singular-only version of the above. */
236 : :
237 : : bool
238 : 9985 : format_string_diagnostic_t::emit_warning_va (int opt, const char *gmsgid,
239 : : va_list *ap) const
240 : : {
241 : 9985 : return emit_warning_n_va (opt, 0, gmsgid, gmsgid, ap);
242 : : }
243 : :
244 : : /* Variadic version of the above (singular only). */
245 : :
246 : : bool
247 : 1220 : format_string_diagnostic_t::emit_warning (int opt, const char *gmsgid,
248 : : ...) const
249 : : {
250 : 1220 : va_list ap;
251 : 1220 : va_start (ap, gmsgid);
252 : 1220 : bool warned = emit_warning_va (opt, gmsgid, &ap);
253 : 1220 : va_end (ap);
254 : :
255 : 1220 : return warned;
256 : : }
257 : :
258 : : /* Variadic version of the above (singular vs plural). */
259 : :
260 : : bool
261 : 0 : format_string_diagnostic_t::emit_warning_n (int opt, unsigned HOST_WIDE_INT n,
262 : : const char *singular_gmsgid,
263 : : const char *plural_gmsgid,
264 : : ...) const
265 : : {
266 : 0 : va_list ap;
267 : 0 : va_start (ap, plural_gmsgid);
268 : 0 : bool warned = emit_warning_n_va (opt, n, singular_gmsgid, plural_gmsgid,
269 : : &ap);
270 : 0 : va_end (ap);
271 : :
272 : 0 : return warned;
273 : : }
274 : :
275 : : /* Attempt to determine the source location of the substring.
276 : : If successful, return NULL and write the source location to *OUT_LOC.
277 : : Otherwise return an error message. Error messages are intended
278 : : for GCC developers (to help debugging) rather than for end-users. */
279 : :
280 : : const char *
281 : 11666 : substring_loc::get_location (location_t *out_loc) const
282 : : {
283 : 11666 : gcc_assert (out_loc);
284 : 11666 : return lang_hooks.get_substring_location (*this, out_loc);
285 : : }
|