1 /* Source locations within string literals.
2    Copyright (C) 2016-2018 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 
30 /* Emit a warning governed by option OPT, using SINGULAR_GMSGID as the
31    format string (or if PLURAL_GMSGID is different from SINGULAR_GMSGID,
32    using SINGULAR_GMSGID, PLURAL_GMSGID and N as arguments to ngettext)
33    and AP as its arguments.
34 
35    Attempt to obtain precise location information within a string
36    literal from FMT_LOC.
37 
38    Case 1: if substring location is available, and is within the range of
39    the format string itself, the primary location of the
40    diagnostic is the substring range obtained from FMT_LOC, with the
41    caret at the *end* of the substring range.
42 
43    For example:
44 
45      test.c:90:10: warning: problem with '%i' here [-Wformat=]
46      printf ("hello %i", msg);
47                     ~^
48 
49    Case 2: if the substring location is available, but is not within
50    the range of the format string, the primary location is that of the
51    format string, and an note is emitted showing the substring location.
52 
53    For example:
54      test.c:90:10: warning: problem with '%i' here [-Wformat=]
55      printf("hello " INT_FMT " world", msg);
56             ^~~~~~~~~~~~~~~~~~~~~~~~~
57      test.c:19: note: format string is defined here
58      #define INT_FMT "%i"
59                       ~^
60 
61    Case 3: if precise substring information is unavailable, the primary
62    location is that of the whole string passed to FMT_LOC's constructor.
63    For example:
64 
65      test.c:90:10: warning: problem with '%i' here [-Wformat=]
66      printf(fmt, msg);
67             ^~~
68 
69    For each of cases 1-3, if param_loc is not UNKNOWN_LOCATION, then it is used
70    as a secondary range within the warning.  For example, here it
71    is used with case 1:
72 
73      test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
74      printf ("foo %s bar", long_i + long_j);
75                   ~^       ~~~~~~~~~~~~~~~
76 
77    and here with case 2:
78 
79      test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
80      printf ("foo " STR_FMT " bar", long_i + long_j);
81              ^~~~~~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~
82      test.c:89:16: note: format string is defined here
83      #define STR_FMT "%s"
84                       ~^
85 
86    and with case 3:
87 
88      test.c:90:10: warning: '%i' here, but arg 2 is "const char *' [-Wformat=]
89      printf(fmt, msg);
90             ^~~  ~~~
91 
92    If CORRECTED_SUBSTRING is non-NULL, use it for cases 1 and 2 to provide
93    a fix-it hint, suggesting that it should replace the text within the
94    substring range.  For example:
95 
96      test.c:90:10: warning: problem with '%i' here [-Wformat=]
97      printf ("hello %i", msg);
98                     ~^
99                     %s
100 
101    Return true if a warning was emitted, false otherwise.  */
102 
103 bool
104 format_warning_n_va (const substring_loc &fmt_loc,
105 		     location_t param_loc,
106 		     const char *corrected_substring,
107 		     int opt, unsigned HOST_WIDE_INT n,
108 		     const char *singular_gmsgid,
109 		     const char *plural_gmsgid, va_list *ap)
110 {
111   bool substring_within_range = false;
112   location_t primary_loc;
113   location_t fmt_substring_loc = UNKNOWN_LOCATION;
114   source_range fmt_loc_range
115     = get_range_from_loc (line_table, fmt_loc.get_fmt_string_loc ());
116   const char *err = fmt_loc.get_location (&fmt_substring_loc);
117   source_range fmt_substring_range
118     = get_range_from_loc (line_table, fmt_substring_loc);
119   if (err)
120     /* Case 3: unable to get substring location.  */
121     primary_loc = fmt_loc.get_fmt_string_loc ();
122   else
123     {
124       if (fmt_substring_range.m_start >= fmt_loc_range.m_start
125 	  && fmt_substring_range.m_start <= fmt_loc_range.m_finish
126 	  && fmt_substring_range.m_finish >= fmt_loc_range.m_start
127 	  && fmt_substring_range.m_finish <= fmt_loc_range.m_finish)
128 	/* Case 1.  */
129 	{
130 	  substring_within_range = true;
131 	  primary_loc = fmt_substring_loc;
132 	}
133       else
134 	/* Case 2.  */
135 	{
136 	  substring_within_range = false;
137 	  primary_loc = fmt_loc.get_fmt_string_loc ();
138 	}
139     }
140 
141   rich_location richloc (line_table, primary_loc);
142 
143   if (param_loc != UNKNOWN_LOCATION)
144     richloc.add_range (param_loc, false);
145 
146   if (!err && corrected_substring && substring_within_range)
147     richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
148 
149   diagnostic_info diagnostic;
150   if (singular_gmsgid != plural_gmsgid)
151     {
152       unsigned long gtn;
153 
154       if (sizeof n <= sizeof gtn)
155 	gtn = n;
156       else
157 	/* Use the largest number ngettext can handle, otherwise
158 	   preserve the six least significant decimal digits for
159 	   languages where the plural form depends on them.  */
160 	gtn = n <= ULONG_MAX ? n : n % 1000000LU + 1000000LU;
161 
162       const char *text = ngettext (singular_gmsgid, plural_gmsgid, gtn);
163       diagnostic_set_info_translated (&diagnostic, text, ap, &richloc,
164 				      DK_WARNING);
165     }
166   else
167     diagnostic_set_info (&diagnostic, singular_gmsgid, ap, &richloc,
168 			 DK_WARNING);
169   diagnostic.option_index = opt;
170   bool warned = diagnostic_report_diagnostic (global_dc, &diagnostic);
171 
172   if (!err && fmt_substring_loc && !substring_within_range)
173     /* Case 2.  */
174     if (warned)
175       {
176 	rich_location substring_richloc (line_table, fmt_substring_loc);
177 	if (corrected_substring)
178 	  substring_richloc.add_fixit_replace (fmt_substring_range,
179 					       corrected_substring);
180 	inform (&substring_richloc,
181 		"format string is defined here");
182       }
183 
184   return warned;
185 }
186 
187 /* Singular-only version of the above.  */
188 
189 bool
190 format_warning_va (const substring_loc &fmt_loc,
191 		   location_t param_loc,
192 		   const char *corrected_substring,
193 		   int opt, const char *gmsgid, va_list *ap)
194 {
195   return format_warning_n_va (fmt_loc, param_loc, corrected_substring, opt,
196 			      0, gmsgid, gmsgid, ap);
197 }
198 
199 /* Variadic call to format_warning_va.  */
200 
201 bool
202 format_warning_at_substring (const substring_loc &fmt_loc,
203 			     location_t param_loc,
204 			     const char *corrected_substring,
205 			     int opt, const char *gmsgid, ...)
206 {
207   va_list ap;
208   va_start (ap, gmsgid);
209   bool warned = format_warning_va (fmt_loc, param_loc, corrected_substring,
210 				   opt, gmsgid, &ap);
211   va_end (ap);
212 
213   return warned;
214 }
215 
216 /* Variadic call to format_warning_n_va.  */
217 
218 bool
219 format_warning_at_substring_n (const substring_loc &fmt_loc,
220 			       location_t param_loc,
221 			       const char *corrected_substring,
222 			       int opt, unsigned HOST_WIDE_INT n,
223 			       const char *singular_gmsgid,
224 			       const char *plural_gmsgid, ...)
225 {
226   va_list ap;
227   va_start (ap, plural_gmsgid);
228   bool warned = format_warning_n_va (fmt_loc, param_loc, corrected_substring,
229 				     opt, n, singular_gmsgid, plural_gmsgid,
230 				     &ap);
231   va_end (ap);
232 
233   return warned;
234 }
235 
236 /* Attempt to determine the source location of the substring.
237    If successful, return NULL and write the source location to *OUT_LOC.
238    Otherwise return an error message.  Error messages are intended
239    for GCC developers (to help debugging) rather than for end-users.  */
240 
241 const char *
242 substring_loc::get_location (location_t *out_loc) const
243 {
244   gcc_assert (out_loc);
245   return lang_hooks.get_substring_location (*this, out_loc);
246 }
247