xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/substring-locations.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
1 /* Source locations within string literals.
2    Copyright (C) 2016-2017 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 "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_range is non-NULL, 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 
100 ATTRIBUTE_GCC_DIAG (5,0)
101 bool
102 format_warning_va (const substring_loc &fmt_loc,
103 		   const source_range *param_range,
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_range)
140     {
141       location_t param_loc = make_location (param_range->m_start,
142 					    param_range->m_start,
143 					    param_range->m_finish);
144       richloc.add_range (param_loc, false);
145     }
146 
147   if (!err && corrected_substring && substring_within_range)
148     richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
149 
150   diagnostic_info diagnostic;
151   diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING);
152   diagnostic.option_index = opt;
153   bool warned = report_diagnostic (&diagnostic);
154 
155   if (!err && fmt_substring_loc && !substring_within_range)
156     /* Case 2.  */
157     if (warned)
158       {
159 	rich_location substring_richloc (line_table, fmt_substring_loc);
160 	if (corrected_substring)
161 	  substring_richloc.add_fixit_replace (fmt_substring_range,
162 					       corrected_substring);
163 	inform_at_rich_loc (&substring_richloc,
164 			    "format string is defined here");
165       }
166 
167   return warned;
168 }
169 
170 /* Variadic call to format_warning_va.  */
171 
172 bool
173 format_warning_at_substring (const substring_loc &fmt_loc,
174 			     const source_range *param_range,
175 			     const char *corrected_substring,
176 			     int opt, const char *gmsgid, ...)
177 {
178   va_list ap;
179   va_start (ap, gmsgid);
180   bool warned = format_warning_va (fmt_loc, param_range, corrected_substring,
181 				   opt, gmsgid, &ap);
182   va_end (ap);
183 
184   return warned;
185 }
186 
187 /* Attempt to determine the source location of the substring.
188    If successful, return NULL and write the source location to *OUT_LOC.
189    Otherwise return an error message.  Error messages are intended
190    for GCC developers (to help debugging) rather than for end-users.  */
191 
192 const char *
193 substring_loc::get_location (location_t *out_loc) const
194 {
195   gcc_assert (out_loc);
196   return lang_hooks.get_substring_location (*this, out_loc);
197 }
198