1/* Source locations within string literals.
2 Copyright (C) 2016-2017 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along 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 "diagnostic.h"
24#include "cpplib.h"
25#include "tree.h"
26#include "langhooks.h"
27#include "substring-locations.h"
28
29/* Emit a warning governed by option OPT, using GMSGID as the format
30 string and AP as its arguments.
31
32 Attempt to obtain precise location information within a string
33 literal from FMT_LOC.
34
35 Case 1: if substring location is available, and is within the range of
36 the format string itself, the primary location of the
37 diagnostic is the substring range obtained from FMT_LOC, with the
38 caret at the *end* of the substring range.
39
40 For example:
41
42 test.c:90:10: warning: problem with '%i' here [-Wformat=]
43 printf ("hello %i", msg);
44 ~^
45
46 Case 2: if the substring location is available, but is not within
47 the range of the format string, the primary location is that of the
48 format string, and an note is emitted showing the substring location.
49
50 For example:
51 test.c:90:10: warning: problem with '%i' here [-Wformat=]
52 printf("hello " INT_FMT " world", msg);
53 ^~~~~~~~~~~~~~~~~~~~~~~~~
54 test.c:19: note: format string is defined here
55 #define INT_FMT "%i"
56 ~^
57
58 Case 3: if precise substring information is unavailable, the primary
59 location is that of the whole string passed to FMT_LOC's constructor.
60 For example:
61
62 test.c:90:10: warning: problem with '%i' here [-Wformat=]
63 printf(fmt, msg);
64 ^~~
65
66 For each of cases 1-3, if param_loc is not UNKNOWN_LOCATION, then it is used
67 as a secondary range within the warning. For example, here it
68 is used with case 1:
69
70 test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
71 printf ("foo %s bar", long_i + long_j);
72 ~^ ~~~~~~~~~~~~~~~
73
74 and here with case 2:
75
76 test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
77 printf ("foo " STR_FMT " bar", long_i + long_j);
78 ^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
79 test.c:89:16: note: format string is defined here
80 #define STR_FMT "%s"
81 ~^
82
83 and with case 3:
84
85 test.c:90:10: warning: '%i' here, but arg 2 is "const char *' [-Wformat=]
86 printf(fmt, msg);
87 ^~~ ~~~
88
89 If CORRECTED_SUBSTRING is non-NULL, use it for cases 1 and 2 to provide
90 a fix-it hint, suggesting that it should replace the text within the
91 substring range. For example:
92
93 test.c:90:10: warning: problem with '%i' here [-Wformat=]
94 printf ("hello %i", msg);
95 ~^
96 %s
97
98 Return true if a warning was emitted, false otherwise. */
99
100ATTRIBUTE_GCC_DIAG (5,0)
101bool
102format_warning_va (const substring_loc &fmt_loc,
103 location_t param_loc,
104 const char *corrected_substring,
105 int opt, const char *gmsgid, va_list *ap)
106{
107 bool substring_within_range = false;
108 location_t primary_loc;
109 location_t fmt_substring_loc = UNKNOWN_LOCATION;
110 source_range fmt_loc_range
111 = get_range_from_loc (line_table, fmt_loc.get_fmt_string_loc ());
112 const char *err = fmt_loc.get_location (&fmt_substring_loc);
113 source_range fmt_substring_range
114 = get_range_from_loc (line_table, fmt_substring_loc);
115 if (err)
116 /* Case 3: unable to get substring location. */
117 primary_loc = fmt_loc.get_fmt_string_loc ();
118 else
119 {
120 if (fmt_substring_range.m_start >= fmt_loc_range.m_start
121 && fmt_substring_range.m_start <= fmt_loc_range.m_finish
122 && fmt_substring_range.m_finish >= fmt_loc_range.m_start
123 && fmt_substring_range.m_finish <= fmt_loc_range.m_finish)
124 /* Case 1. */
125 {
126 substring_within_range = true;
127 primary_loc = fmt_substring_loc;
128 }
129 else
130 /* Case 2. */
131 {
132 substring_within_range = false;
133 primary_loc = fmt_loc.get_fmt_string_loc ();
134 }
135 }
136
137 rich_location richloc (line_table, primary_loc);
138
139 if (param_loc != UNKNOWN_LOCATION)
140 richloc.add_range (param_loc, false);
141
142 if (!err && corrected_substring && substring_within_range)
143 richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
144
145 diagnostic_info diagnostic;
146 diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING);
147 diagnostic.option_index = opt;
148 bool warned = diagnostic_report_diagnostic (global_dc, &diagnostic);
149
150 if (!err && fmt_substring_loc && !substring_within_range)
151 /* Case 2. */
152 if (warned)
153 {
154 rich_location substring_richloc (line_table, fmt_substring_loc);
155 if (corrected_substring)
156 substring_richloc.add_fixit_replace (fmt_substring_range,
157 corrected_substring);
158 inform (&substring_richloc,
159 "format string is defined here");
160 }
161
162 return warned;
163}
164
165/* Variadic call to format_warning_va. */
166
167bool
168format_warning_at_substring (const substring_loc &fmt_loc,
169 location_t param_loc,
170 const char *corrected_substring,
171 int opt, const char *gmsgid, ...)
172{
173 va_list ap;
174 va_start (ap, gmsgid);
175 bool warned = format_warning_va (fmt_loc, param_loc, corrected_substring,
176 opt, gmsgid, &ap);
177 va_end (ap);
178
179 return warned;
180}
181
182/* Attempt to determine the source location of the substring.
183 If successful, return NULL and write the source location to *OUT_LOC.
184 Otherwise return an error message. Error messages are intended
185 for GCC developers (to help debugging) rather than for end-users. */
186
187const char *
188substring_loc::get_location (location_t *out_loc) const
189{
190 gcc_assert (out_loc);
191 return lang_hooks.get_substring_location (*this, out_loc);
192}
193