Use char_span for return type of location_get_source_line
[official-gcc.git] / gcc / testsuite / gcc.dg / plugin / diagnostic_plugin_test_show_locus.c
blobdabc0e421307d3a9a332f868725adba7ef8b1600
1 /* { dg-options "-O" } */
3 /* This plugin exercises the diagnostics-printing code.
5 The goal is to unit-test the range-printing code without needing any
6 correct range data within the compiler's IR. We can't use any real
7 diagnostics for this, so we have to fake it, hence this plugin.
9 There are two test files used with this code:
11 diagnostic-test-show-locus-ascii-bw.c
12 ..........................-ascii-color.c
14 to exercise uncolored vs colored output by supplying plugin arguments
15 to hack in the desired behavior:
17 -fplugin-arg-diagnostic_plugin_test_show_locus-color
19 The test files contain functions, but the body of each
20 function is disabled using the preprocessor. The plugin detects
21 the functions by name, and inject diagnostics within them, using
22 hard-coded locations relative to the top of each function.
24 The plugin uses a function "get_loc" below to map from line/column
25 numbers to source_location, and this relies on input_location being in
26 the same ordinary line_map as the locations in question. The plugin
27 runs after parsing, so input_location will be at the end of the file.
29 This need for all of the test code to be in a single ordinary line map
30 means that each test file needs to have a very long line near the top
31 (potentially to cover the extra byte-count of colorized data),
32 to ensure that further very long lines don't start a new linemap.
33 This also means that we can't use macros in the test files. */
35 #include "gcc-plugin.h"
36 #include "config.h"
37 #include "system.h"
38 #include "coretypes.h"
39 #include "tm.h"
40 #include "tree.h"
41 #include "stringpool.h"
42 #include "toplev.h"
43 #include "basic-block.h"
44 #include "hash-table.h"
45 #include "vec.h"
46 #include "ggc.h"
47 #include "basic-block.h"
48 #include "tree-ssa-alias.h"
49 #include "internal-fn.h"
50 #include "gimple-fold.h"
51 #include "tree-eh.h"
52 #include "gimple-expr.h"
53 #include "is-a.h"
54 #include "gimple.h"
55 #include "gimple-iterator.h"
56 #include "tree.h"
57 #include "tree-pass.h"
58 #include "intl.h"
59 #include "plugin-version.h"
60 #include "diagnostic.h"
61 #include "context.h"
62 #include "print-tree.h"
64 int plugin_is_GPL_compatible;
66 const pass_data pass_data_test_show_locus =
68 GIMPLE_PASS, /* type */
69 "test_show_locus", /* name */
70 OPTGROUP_NONE, /* optinfo_flags */
71 TV_NONE, /* tv_id */
72 PROP_ssa, /* properties_required */
73 0, /* properties_provided */
74 0, /* properties_destroyed */
75 0, /* todo_flags_start */
76 0, /* todo_flags_finish */
79 class pass_test_show_locus : public gimple_opt_pass
81 public:
82 pass_test_show_locus(gcc::context *ctxt)
83 : gimple_opt_pass(pass_data_test_show_locus, ctxt)
86 /* opt_pass methods: */
87 bool gate (function *) { return true; }
88 virtual unsigned int execute (function *);
90 }; // class pass_test_show_locus
92 /* Given LINE_NUM and COL_NUM, generate a source_location in the
93 current file, relative to input_location. This relies on the
94 location being expressible in the same ordinary line_map as
95 input_location (which is typically at the end of the source file
96 when this is called). Hence the test files we compile with this
97 plugin must have an initial very long line (to avoid long lines
98 starting a new line map), and must not use macros.
100 COL_NUM uses the Emacs convention of 0-based column numbers. */
102 static source_location
103 get_loc (unsigned int line_num, unsigned int col_num)
105 /* Use input_location to get the relevant line_map */
106 const struct line_map_ordinary *line_map
107 = (const line_map_ordinary *)(linemap_lookup (line_table,
108 input_location));
110 /* Convert from 0-based column numbers to 1-based column numbers. */
111 source_location loc
112 = linemap_position_for_line_and_column (line_table,
113 line_map,
114 line_num, col_num + 1);
116 return loc;
119 /* Was "color" passed in as a plugin argument? */
120 static bool force_show_locus_color = false;
122 /* We want to verify the colorized output of diagnostic_show_locus,
123 but turning on colorization for everything confuses "dg-warning" etc.
124 Hence we special-case it within this plugin by using this modified
125 version of default_diagnostic_finalizer, which, if "color" is
126 passed in as a plugin argument turns on colorization, but just
127 for diagnostic_show_locus. */
129 static void
130 custom_diagnostic_finalizer (diagnostic_context *context,
131 diagnostic_info *diagnostic)
133 bool old_show_color = pp_show_color (context->printer);
134 if (force_show_locus_color)
135 pp_show_color (context->printer) = true;
136 diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
137 pp_show_color (context->printer) = old_show_color;
139 pp_destroy_prefix (context->printer);
140 pp_flush (context->printer);
143 /* Add a location to RICHLOC with caret==start at START, ranging to FINISH. */
145 static void
146 add_range (rich_location *richloc, location_t start, location_t finish,
147 bool show_caret_p)
149 richloc->add_range (make_location (start, start, finish), show_caret_p);
152 /* Exercise the diagnostic machinery to emit various warnings,
153 for use by diagnostic-test-show-locus-*.c.
155 We inject each warning relative to the start of a function,
156 which avoids lots of hardcoded absolute locations. */
158 static void
159 test_show_locus (function *fun)
161 tree fndecl = fun->decl;
162 tree identifier = DECL_NAME (fndecl);
163 const char *fnname = IDENTIFIER_POINTER (identifier);
164 location_t fnstart = fun->function_start_locus;
165 int fnstart_line = LOCATION_LINE (fnstart);
167 diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
169 /* Hardcode the "terminal width", to verify the behavior of
170 very wide lines. */
171 global_dc->caret_max_width = 70;
173 if (0 == strcmp (fnname, "test_simple"))
175 const int line = fnstart_line + 2;
176 rich_location richloc (line_table, get_loc (line, 15));
177 add_range (&richloc, get_loc (line, 10), get_loc (line, 14), false);
178 add_range (&richloc, get_loc (line, 16), get_loc (line, 16), false);
179 warning_at (&richloc, 0, "test");
182 if (0 == strcmp (fnname, "test_simple_2"))
184 const int line = fnstart_line + 2;
185 rich_location richloc (line_table, get_loc (line, 24));
186 add_range (&richloc, get_loc (line, 6), get_loc (line, 22), false);
187 add_range (&richloc, get_loc (line, 26), get_loc (line, 43), false);
188 warning_at (&richloc, 0, "test");
191 if (0 == strcmp (fnname, "test_multiline"))
193 const int line = fnstart_line + 2;
194 rich_location richloc (line_table, get_loc (line + 1, 7));
195 add_range (&richloc, get_loc (line, 7), get_loc (line, 23), false);
196 add_range (&richloc, get_loc (line + 1, 9), get_loc (line + 1, 26),
197 false);
198 warning_at (&richloc, 0, "test");
201 if (0 == strcmp (fnname, "test_many_lines"))
203 const int line = fnstart_line + 2;
204 rich_location richloc (line_table, get_loc (line + 5, 7));
205 add_range (&richloc, get_loc (line, 7), get_loc (line + 4, 65), false);
206 add_range (&richloc, get_loc (line + 5, 9), get_loc (line + 10, 61),
207 false);
208 warning_at (&richloc, 0, "test");
211 /* Example of a rich_location where the range is larger than
212 one character. */
213 if (0 == strcmp (fnname, "test_richloc_from_proper_range"))
215 const int line = fnstart_line + 2;
216 location_t start = get_loc (line, 12);
217 location_t finish = get_loc (line, 16);
218 rich_location richloc (line_table, make_location (start, start, finish));
219 warning_at (&richloc, 0, "test");
222 /* Example of a single-range location where the range starts
223 before the caret. */
224 if (0 == strcmp (fnname, "test_caret_within_proper_range"))
226 const int line = fnstart_line + 2;
227 warning_at (make_location (get_loc (line, 16), get_loc (line, 12),
228 get_loc (line, 20)),
229 0, "test");
232 /* Example of a very wide line, where the information of interest
233 is beyond the width of the terminal (hardcoded above). */
234 if (0 == strcmp (fnname, "test_very_wide_line"))
236 const int line = fnstart_line + 2;
237 global_dc->show_ruler_p = true;
238 rich_location richloc (line_table,
239 make_location (get_loc (line, 94),
240 get_loc (line, 90),
241 get_loc (line, 98)));
242 richloc.add_fixit_replace ("bar * foo");
243 warning_at (&richloc, 0, "test");
244 global_dc->show_ruler_p = false;
247 /* Example of multiple carets. */
248 if (0 == strcmp (fnname, "test_multiple_carets"))
250 const int line = fnstart_line + 2;
251 location_t caret_a = get_loc (line, 7);
252 location_t caret_b = get_loc (line, 11);
253 rich_location richloc (line_table, caret_a);
254 add_range (&richloc, caret_b, caret_b, true);
255 global_dc->caret_chars[0] = 'A';
256 global_dc->caret_chars[1] = 'B';
257 warning_at (&richloc, 0, "test");
258 global_dc->caret_chars[0] = '^';
259 global_dc->caret_chars[1] = '^';
262 /* Tests of rendering fixit hints. */
263 if (0 == strcmp (fnname, "test_fixit_insert"))
265 const int line = fnstart_line + 2;
266 location_t start = get_loc (line, 19);
267 location_t finish = get_loc (line, 22);
268 rich_location richloc (line_table, make_location (start, start, finish));
269 richloc.add_fixit_insert_before ("{");
270 richloc.add_fixit_insert_after ("}");
271 warning_at (&richloc, 0, "example of insertion hints");
274 if (0 == strcmp (fnname, "test_fixit_insert_newline"))
276 const int line = fnstart_line + 6;
277 location_t line_start = get_loc (line, 0);
278 location_t case_start = get_loc (line, 4);
279 location_t case_finish = get_loc (line, 11);
280 location_t case_loc = make_location (case_start, case_start, case_finish);
281 rich_location richloc (line_table, case_loc);
282 richloc.add_fixit_insert_before (line_start, " break;\n");
283 warning_at (&richloc, 0, "example of newline insertion hint");
286 if (0 == strcmp (fnname, "test_fixit_remove"))
288 const int line = fnstart_line + 2;
289 location_t start = get_loc (line, 8);
290 location_t finish = get_loc (line, 8);
291 rich_location richloc (line_table, make_location (start, start, finish));
292 source_range src_range;
293 src_range.m_start = start;
294 src_range.m_finish = finish;
295 richloc.add_fixit_remove (src_range);
296 warning_at (&richloc, 0, "example of a removal hint");
299 if (0 == strcmp (fnname, "test_fixit_replace"))
301 const int line = fnstart_line + 2;
302 location_t start = get_loc (line, 2);
303 location_t finish = get_loc (line, 19);
304 rich_location richloc (line_table, make_location (start, start, finish));
305 source_range src_range;
306 src_range.m_start = start;
307 src_range.m_finish = finish;
308 richloc.add_fixit_replace (src_range, "gtk_widget_show_all");
309 warning_at (&richloc, 0, "example of a replacement hint");
312 if (0 == strcmp (fnname, "test_mutually_exclusive_suggestions"))
314 const int line = fnstart_line + 2;
315 location_t start = get_loc (line, 2);
316 location_t finish = get_loc (line, 9);
317 source_range src_range;
318 src_range.m_start = start;
319 src_range.m_finish = finish;
322 rich_location richloc (line_table, make_location (start, start, finish));
323 richloc.add_fixit_replace (src_range, "replacement_1");
324 richloc.fixits_cannot_be_auto_applied ();
325 warning_at (&richloc, 0, "warning 1");
329 rich_location richloc (line_table, make_location (start, start, finish));
330 richloc.add_fixit_replace (src_range, "replacement_2");
331 richloc.fixits_cannot_be_auto_applied ();
332 warning_at (&richloc, 0, "warning 2");
336 /* Example of two carets where both carets appear to have an off-by-one
337 error appearing one column early.
338 Seen with gfortran.dg/associate_5.f03.
339 In an earlier version of the printer, the printing of caret 0 aka
340 "1" was suppressed due to it appearing within the leading whitespace
341 before the text in its line. Ensure that we at least faithfully
342 print both carets, at the given (erroneous) locations. */
343 if (0 == strcmp (fnname, "test_caret_on_leading_whitespace"))
345 const int line = fnstart_line + 3;
346 location_t caret_a = get_loc (line, 5);
347 location_t caret_b = get_loc (line - 1, 19);
348 rich_location richloc (line_table, caret_a);
349 richloc.add_range (caret_b, true);
350 global_dc->caret_chars[0] = '1';
351 global_dc->caret_chars[1] = '2';
352 warning_at (&richloc, 0, "test");
353 global_dc->caret_chars[0] = '^';
354 global_dc->caret_chars[1] = '^';
357 /* Example of using the "%q+D" format code, which as well as printing
358 a quoted decl, overrides the given location to use the location of
359 the decl. */
360 if (0 == strcmp (fnname, "test_percent_q_plus_d"))
362 const int line = fnstart_line + 3;
363 tree local = (*fun->local_decls)[0];
364 warning_at (input_location, 0,
365 "example of plus in format code for %q+D", local);
368 /* Example of many locations and many fixits.
369 Underline (separately) every word in a comment, and convert them
370 to upper case. */
371 if (0 == strcmp (fnname, "test_many_nested_locations"))
373 const char *file = LOCATION_FILE (fnstart);
374 const int start_line = fnstart_line + 2;
375 const int finish_line = start_line + 7;
376 location_t loc = get_loc (start_line - 1, 2);
377 rich_location richloc (line_table, loc);
378 for (int line = start_line; line <= finish_line; line++)
380 char_span content = location_get_source_line (file, line);
381 gcc_assert (content);
382 /* Split line up into words. */
383 for (int idx = 0; idx < content.length (); idx++)
385 if (ISALPHA (content[idx]))
387 int start_idx = idx;
388 while (idx < content.length () && ISALPHA (content[idx]))
389 idx++;
390 if (idx == content.length () || !ISALPHA (content[idx]))
392 location_t start_of_word = get_loc (line, start_idx);
393 location_t end_of_word = get_loc (line, idx - 1);
394 location_t word
395 = make_location (start_of_word, start_of_word,
396 end_of_word);
397 richloc.add_range (word, true);
399 /* Add a fixit, converting to upper case. */
400 char_span word_span = content.subspan (start_idx, idx - start_idx);
401 char *copy = word_span.xstrdup ();
402 for (char *ch = copy; *ch; ch++)
403 *ch = TOUPPER (*ch);
404 richloc.add_fixit_replace (word, copy);
405 free (copy);
410 /* Verify that we added enough locations to fully exercise
411 rich_location. We want to exceed both the
412 statically-allocated buffer in class rich_location,
413 and then trigger a reallocation of the dynamic buffer. */
414 gcc_assert (richloc.get_num_locations () > 3 + (2 * 16));
415 warning_at (&richloc, 0, "test of %i locations",
416 richloc.get_num_locations ());
420 unsigned int
421 pass_test_show_locus::execute (function *fun)
423 test_show_locus (fun);
424 return 0;
427 static gimple_opt_pass *
428 make_pass_test_show_locus (gcc::context *ctxt)
430 return new pass_test_show_locus (ctxt);
434 plugin_init (struct plugin_name_args *plugin_info,
435 struct plugin_gcc_version *version)
437 struct register_pass_info pass_info;
438 const char *plugin_name = plugin_info->base_name;
439 int argc = plugin_info->argc;
440 struct plugin_argument *argv = plugin_info->argv;
442 if (!plugin_default_version_check (version, &gcc_version))
443 return 1;
445 for (int i = 0; i < argc; i++)
447 if (0 == strcmp (argv[i].key, "color"))
448 force_show_locus_color = true;
451 pass_info.pass = make_pass_test_show_locus (g);
452 pass_info.reference_pass_name = "ssa";
453 pass_info.ref_pass_instance_number = 1;
454 pass_info.pos_op = PASS_POS_INSERT_AFTER;
455 register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
456 &pass_info);
458 return 0;