Eliminate source_location in favor of location_t
[official-gcc.git] / gcc / testsuite / gcc.dg / plugin / diagnostic_plugin_test_show_locus.c
blob7cc90de802e8b38f407fc4cbb0236c679833f240
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 location_t, 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"
63 #include "gcc-rich-location.h"
65 int plugin_is_GPL_compatible;
67 const pass_data pass_data_test_show_locus =
69 GIMPLE_PASS, /* type */
70 "test_show_locus", /* name */
71 OPTGROUP_NONE, /* optinfo_flags */
72 TV_NONE, /* tv_id */
73 PROP_ssa, /* properties_required */
74 0, /* properties_provided */
75 0, /* properties_destroyed */
76 0, /* todo_flags_start */
77 0, /* todo_flags_finish */
80 class pass_test_show_locus : public gimple_opt_pass
82 public:
83 pass_test_show_locus(gcc::context *ctxt)
84 : gimple_opt_pass(pass_data_test_show_locus, ctxt)
87 /* opt_pass methods: */
88 bool gate (function *) { return true; }
89 virtual unsigned int execute (function *);
91 }; // class pass_test_show_locus
93 /* Given LINE_NUM and COL_NUM, generate a location_t in the
94 current file, relative to input_location. This relies on the
95 location being expressible in the same ordinary line_map as
96 input_location (which is typically at the end of the source file
97 when this is called). Hence the test files we compile with this
98 plugin must have an initial very long line (to avoid long lines
99 starting a new line map), and must not use macros.
101 COL_NUM uses the Emacs convention of 0-based column numbers. */
103 static location_t
104 get_loc (unsigned int line_num, unsigned int col_num)
106 /* Use input_location to get the relevant line_map */
107 const struct line_map_ordinary *line_map
108 = (const line_map_ordinary *)(linemap_lookup (line_table,
109 input_location));
111 /* Convert from 0-based column numbers to 1-based column numbers. */
112 location_t loc
113 = linemap_position_for_line_and_column (line_table,
114 line_map,
115 line_num, col_num + 1);
117 return loc;
120 /* Was "color" passed in as a plugin argument? */
121 static bool force_show_locus_color = false;
123 /* We want to verify the colorized output of diagnostic_show_locus,
124 but turning on colorization for everything confuses "dg-warning" etc.
125 Hence we special-case it within this plugin by using this modified
126 version of default_diagnostic_finalizer, which, if "color" is
127 passed in as a plugin argument turns on colorization, but just
128 for diagnostic_show_locus. */
130 static void
131 custom_diagnostic_finalizer (diagnostic_context *context,
132 diagnostic_info *diagnostic)
134 bool old_show_color = pp_show_color (context->printer);
135 if (force_show_locus_color)
136 pp_show_color (context->printer) = true;
137 diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
138 pp_show_color (context->printer) = old_show_color;
140 pp_destroy_prefix (context->printer);
141 pp_flush (context->printer);
144 /* Add a location to RICHLOC with caret==start at START, ranging to FINISH. */
146 static void
147 add_range (rich_location *richloc, location_t start, location_t finish,
148 enum range_display_kind range_display_kind
149 = SHOW_RANGE_WITHOUT_CARET,
150 const range_label *label = NULL)
152 richloc->add_range (make_location (start, start, finish), range_display_kind,
153 label);
156 /* Exercise the diagnostic machinery to emit various warnings,
157 for use by diagnostic-test-show-locus-*.c.
159 We inject each warning relative to the start of a function,
160 which avoids lots of hardcoded absolute locations. */
162 static void
163 test_show_locus (function *fun)
165 tree fndecl = fun->decl;
166 tree identifier = DECL_NAME (fndecl);
167 const char *fnname = IDENTIFIER_POINTER (identifier);
168 location_t fnstart = fun->function_start_locus;
169 int fnstart_line = LOCATION_LINE (fnstart);
171 diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
173 /* Hardcode the "terminal width", to verify the behavior of
174 very wide lines. */
175 global_dc->caret_max_width = 70;
177 if (0 == strcmp (fnname, "test_simple"))
179 const int line = fnstart_line + 2;
180 rich_location richloc (line_table, get_loc (line, 15));
181 add_range (&richloc, get_loc (line, 10), get_loc (line, 14));
182 add_range (&richloc, get_loc (line, 16), get_loc (line, 16));
183 warning_at (&richloc, 0, "test");
186 if (0 == strcmp (fnname, "test_simple_2"))
188 const int line = fnstart_line + 2;
189 rich_location richloc (line_table, get_loc (line, 24));
190 add_range (&richloc, get_loc (line, 6), get_loc (line, 22));
191 add_range (&richloc, get_loc (line, 26), get_loc (line, 43));
192 warning_at (&richloc, 0, "test");
195 if (0 == strcmp (fnname, "test_multiline"))
197 const int line = fnstart_line + 2;
198 text_range_label label ("label");
199 rich_location richloc (line_table, get_loc (line + 1, 7), &label);
200 add_range (&richloc, get_loc (line, 7), get_loc (line, 23));
201 add_range (&richloc, get_loc (line + 1, 9), get_loc (line + 1, 26));
202 warning_at (&richloc, 0, "test");
205 if (0 == strcmp (fnname, "test_many_lines"))
207 const int line = fnstart_line + 2;
208 text_range_label label0 ("label 0");
209 text_range_label label1 ("label 1");
210 text_range_label label2 ("label 2");
211 rich_location richloc (line_table, get_loc (line + 5, 7), &label0);
212 add_range (&richloc, get_loc (line, 7), get_loc (line + 4, 65),
213 SHOW_RANGE_WITHOUT_CARET, &label1);
214 add_range (&richloc, get_loc (line + 5, 9), get_loc (line + 10, 61),
215 SHOW_RANGE_WITHOUT_CARET, &label2);
216 warning_at (&richloc, 0, "test");
219 /* Example of a rich_location where the range is larger than
220 one character. */
221 if (0 == strcmp (fnname, "test_richloc_from_proper_range"))
223 const int line = fnstart_line + 2;
224 location_t start = get_loc (line, 12);
225 location_t finish = get_loc (line, 16);
226 rich_location richloc (line_table, make_location (start, start, finish));
227 warning_at (&richloc, 0, "test");
230 /* Example of a single-range location where the range starts
231 before the caret. */
232 if (0 == strcmp (fnname, "test_caret_within_proper_range"))
234 const int line = fnstart_line + 2;
235 warning_at (make_location (get_loc (line, 16), get_loc (line, 12),
236 get_loc (line, 20)),
237 0, "test");
240 /* Example of a very wide line, where the information of interest
241 is beyond the width of the terminal (hardcoded above), with
242 a secondary location that exactly fits on the left-margin. */
243 if (0 == strcmp (fnname, "test_very_wide_line"))
245 const int line = fnstart_line + 2;
246 global_dc->show_ruler_p = true;
247 text_range_label label0 ("label 0");
248 text_range_label label1 ("label 1");
249 rich_location richloc (line_table,
250 make_location (get_loc (line, 94),
251 get_loc (line, 90),
252 get_loc (line, 98)),
253 &label0);
254 richloc.add_range (get_loc (line, 35), SHOW_RANGE_WITHOUT_CARET,
255 &label1);
256 richloc.add_fixit_replace ("bar * foo");
257 warning_at (&richloc, 0, "test");
258 global_dc->show_ruler_p = false;
261 /* Likewise, but with a secondary location that's immediately before
262 the left margin; the location and label should be gracefully dropped. */
263 if (0 == strcmp (fnname, "test_very_wide_line_2"))
265 const int line = fnstart_line + 2;
266 global_dc->show_ruler_p = true;
267 text_range_label label0 ("label 0");
268 text_range_label label1 ("label 1");
269 rich_location richloc (line_table,
270 make_location (get_loc (line, 94),
271 get_loc (line, 90),
272 get_loc (line, 98)),
273 &label0);
274 richloc.add_fixit_replace ("bar * foo");
275 richloc.add_range (get_loc (line, 34), SHOW_RANGE_WITHOUT_CARET,
276 &label1);
277 warning_at (&richloc, 0, "test");
278 global_dc->show_ruler_p = false;
281 /* Example of multiple carets. */
282 if (0 == strcmp (fnname, "test_multiple_carets"))
284 const int line = fnstart_line + 2;
285 location_t caret_a = get_loc (line, 7);
286 location_t caret_b = get_loc (line, 11);
287 rich_location richloc (line_table, caret_a);
288 add_range (&richloc, caret_b, caret_b, SHOW_RANGE_WITH_CARET);
289 global_dc->caret_chars[0] = 'A';
290 global_dc->caret_chars[1] = 'B';
291 warning_at (&richloc, 0, "test");
292 global_dc->caret_chars[0] = '^';
293 global_dc->caret_chars[1] = '^';
296 /* Tests of rendering fixit hints. */
297 if (0 == strcmp (fnname, "test_fixit_insert"))
299 const int line = fnstart_line + 2;
300 location_t start = get_loc (line, 19);
301 location_t finish = get_loc (line, 22);
302 rich_location richloc (line_table, make_location (start, start, finish));
303 richloc.add_fixit_insert_before ("{");
304 richloc.add_fixit_insert_after ("}");
305 warning_at (&richloc, 0, "example of insertion hints");
308 if (0 == strcmp (fnname, "test_fixit_insert_newline"))
310 const int line = fnstart_line + 6;
311 location_t line_start = get_loc (line, 0);
312 location_t case_start = get_loc (line, 4);
313 location_t case_finish = get_loc (line, 11);
314 location_t case_loc = make_location (case_start, case_start, case_finish);
315 rich_location richloc (line_table, case_loc);
316 richloc.add_fixit_insert_before (line_start, " break;\n");
317 warning_at (&richloc, 0, "example of newline insertion hint");
320 if (0 == strcmp (fnname, "test_fixit_remove"))
322 const int line = fnstart_line + 2;
323 location_t start = get_loc (line, 8);
324 location_t finish = get_loc (line, 8);
325 rich_location richloc (line_table, make_location (start, start, finish));
326 source_range src_range;
327 src_range.m_start = start;
328 src_range.m_finish = finish;
329 richloc.add_fixit_remove (src_range);
330 warning_at (&richloc, 0, "example of a removal hint");
333 if (0 == strcmp (fnname, "test_fixit_replace"))
335 const int line = fnstart_line + 2;
336 location_t start = get_loc (line, 2);
337 location_t finish = get_loc (line, 19);
338 rich_location richloc (line_table, make_location (start, start, finish));
339 source_range src_range;
340 src_range.m_start = start;
341 src_range.m_finish = finish;
342 richloc.add_fixit_replace (src_range, "gtk_widget_show_all");
343 warning_at (&richloc, 0, "example of a replacement hint");
346 if (0 == strcmp (fnname, "test_mutually_exclusive_suggestions"))
348 const int line = fnstart_line + 2;
349 location_t start = get_loc (line, 2);
350 location_t finish = get_loc (line, 9);
351 source_range src_range;
352 src_range.m_start = start;
353 src_range.m_finish = finish;
356 rich_location richloc (line_table, make_location (start, start, finish));
357 richloc.add_fixit_replace (src_range, "replacement_1");
358 richloc.fixits_cannot_be_auto_applied ();
359 warning_at (&richloc, 0, "warning 1");
363 rich_location richloc (line_table, make_location (start, start, finish));
364 richloc.add_fixit_replace (src_range, "replacement_2");
365 richloc.fixits_cannot_be_auto_applied ();
366 warning_at (&richloc, 0, "warning 2");
370 /* Tests of gcc_rich_location::add_fixit_insert_formatted. */
372 if (0 == strcmp (fnname, "test_add_fixit_insert_formatted_single_line"))
374 const int line = fnstart_line + 1;
375 location_t insertion_point = get_loc (line, 3);
376 location_t indent = get_loc (line, 2);
377 gcc_rich_location richloc (insertion_point);
378 richloc.add_fixit_insert_formatted ("INSERTED-CONTENT",
379 insertion_point, indent);
380 inform (&richloc, "single-line insertion");
383 if (0 == strcmp (fnname, "test_add_fixit_insert_formatted_multiline"))
385 location_t insertion_point = fun->function_end_locus;
386 location_t indent = get_loc (fnstart_line + 1, 2);
387 gcc_rich_location richloc (insertion_point);
388 richloc.add_fixit_insert_formatted ("INSERTED-CONTENT",
389 insertion_point, indent);
390 inform (&richloc, "multiline insertion");
393 /* Example of two carets where both carets appear to have an off-by-one
394 error appearing one column early.
395 Seen with gfortran.dg/associate_5.f03.
396 In an earlier version of the printer, the printing of caret 0 aka
397 "1" was suppressed due to it appearing within the leading whitespace
398 before the text in its line. Ensure that we at least faithfully
399 print both carets, at the given (erroneous) locations. */
400 if (0 == strcmp (fnname, "test_caret_on_leading_whitespace"))
402 const int line = fnstart_line + 3;
403 location_t caret_a = get_loc (line, 5);
404 location_t caret_b = get_loc (line - 1, 19);
405 rich_location richloc (line_table, caret_a);
406 richloc.add_range (caret_b, SHOW_RANGE_WITH_CARET);
407 global_dc->caret_chars[0] = '1';
408 global_dc->caret_chars[1] = '2';
409 warning_at (&richloc, 0, "test");
410 global_dc->caret_chars[0] = '^';
411 global_dc->caret_chars[1] = '^';
414 /* Example of using the "%q+D" format code, which as well as printing
415 a quoted decl, overrides the given location to use the location of
416 the decl. */
417 if (0 == strcmp (fnname, "test_percent_q_plus_d"))
419 const int line = fnstart_line + 3;
420 tree local = (*fun->local_decls)[0];
421 warning_at (input_location, 0,
422 "example of plus in format code for %q+D", local);
425 /* Example of many locations and many fixits.
426 Underline (separately) every word in a comment, and convert them
427 to upper case. Give all of the ranges labels (sharing one label). */
428 if (0 == strcmp (fnname, "test_many_nested_locations"))
430 const char *file = LOCATION_FILE (fnstart);
431 const int start_line = fnstart_line + 2;
432 const int finish_line = start_line + 7;
433 location_t loc = get_loc (start_line - 1, 2);
434 text_range_label label ("label");
435 rich_location richloc (line_table, loc);
436 for (int line = start_line; line <= finish_line; line++)
438 char_span content = location_get_source_line (file, line);
439 gcc_assert (content);
440 /* Split line up into words. */
441 for (int idx = 0; idx < content.length (); idx++)
443 if (ISALPHA (content[idx]))
445 int start_idx = idx;
446 while (idx < content.length () && ISALPHA (content[idx]))
447 idx++;
448 if (idx == content.length () || !ISALPHA (content[idx]))
450 location_t start_of_word = get_loc (line, start_idx);
451 location_t end_of_word = get_loc (line, idx - 1);
452 location_t word
453 = make_location (start_of_word, start_of_word,
454 end_of_word);
455 richloc.add_range (word, SHOW_RANGE_WITH_CARET, &label);
457 /* Add a fixit, converting to upper case. */
458 char_span word_span = content.subspan (start_idx, idx - start_idx);
459 char *copy = word_span.xstrdup ();
460 for (char *ch = copy; *ch; ch++)
461 *ch = TOUPPER (*ch);
462 richloc.add_fixit_replace (word, copy);
463 free (copy);
468 /* Verify that we added enough locations to fully exercise
469 rich_location. We want to exceed both the
470 statically-allocated buffer in class rich_location,
471 and then trigger a reallocation of the dynamic buffer. */
472 gcc_assert (richloc.get_num_locations () > 3 + (2 * 16));
473 warning_at (&richloc, 0, "test of %i locations",
474 richloc.get_num_locations ());
478 unsigned int
479 pass_test_show_locus::execute (function *fun)
481 test_show_locus (fun);
482 return 0;
485 static gimple_opt_pass *
486 make_pass_test_show_locus (gcc::context *ctxt)
488 return new pass_test_show_locus (ctxt);
492 plugin_init (struct plugin_name_args *plugin_info,
493 struct plugin_gcc_version *version)
495 struct register_pass_info pass_info;
496 const char *plugin_name = plugin_info->base_name;
497 int argc = plugin_info->argc;
498 struct plugin_argument *argv = plugin_info->argv;
500 if (!plugin_default_version_check (version, &gcc_version))
501 return 1;
503 for (int i = 0; i < argc; i++)
505 if (0 == strcmp (argv[i].key, "color"))
506 force_show_locus_color = true;
509 pass_info.pass = make_pass_test_show_locus (g);
510 pass_info.reference_pass_name = "ssa";
511 pass_info.ref_pass_instance_number = 1;
512 pass_info.pos_op = PASS_POS_INSERT_AFTER;
513 register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
514 &pass_info);
516 return 0;