Add support for mutually-incompatible fix-it hints
[official-gcc.git] / gcc / testsuite / gcc.dg / plugin / diagnostic_plugin_test_show_locus.c
blob0a8eeba1846d7fa1ae3961867915e98abe547c26
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_rich_loc (&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_rich_loc (&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_rich_loc (&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_rich_loc (&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_rich_loc (&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 warning_at (make_location (get_loc (line, 94), get_loc (line, 90),
239 get_loc (line, 98)),
240 0, "test");
241 global_dc->show_ruler_p = false;
244 /* Example of multiple carets. */
245 if (0 == strcmp (fnname, "test_multiple_carets"))
247 const int line = fnstart_line + 2;
248 location_t caret_a = get_loc (line, 7);
249 location_t caret_b = get_loc (line, 11);
250 rich_location richloc (line_table, caret_a);
251 add_range (&richloc, caret_b, caret_b, true);
252 global_dc->caret_chars[0] = 'A';
253 global_dc->caret_chars[1] = 'B';
254 warning_at_rich_loc (&richloc, 0, "test");
255 global_dc->caret_chars[0] = '^';
256 global_dc->caret_chars[1] = '^';
259 /* Tests of rendering fixit hints. */
260 if (0 == strcmp (fnname, "test_fixit_insert"))
262 const int line = fnstart_line + 2;
263 location_t start = get_loc (line, 19);
264 location_t finish = get_loc (line, 22);
265 rich_location richloc (line_table, make_location (start, start, finish));
266 richloc.add_fixit_insert_before ("{");
267 richloc.add_fixit_insert_after ("}");
268 warning_at_rich_loc (&richloc, 0, "example of insertion hints");
271 if (0 == strcmp (fnname, "test_fixit_insert_newline"))
273 const int line = fnstart_line + 6;
274 location_t line_start = get_loc (line, 0);
275 location_t case_start = get_loc (line, 4);
276 location_t case_finish = get_loc (line, 11);
277 location_t case_loc = make_location (case_start, case_start, case_finish);
278 rich_location richloc (line_table, case_loc);
279 richloc.add_fixit_insert_before (line_start, " break;\n");
280 warning_at_rich_loc (&richloc, 0, "example of newline insertion hint");
283 if (0 == strcmp (fnname, "test_fixit_remove"))
285 const int line = fnstart_line + 2;
286 location_t start = get_loc (line, 8);
287 location_t finish = get_loc (line, 8);
288 rich_location richloc (line_table, make_location (start, start, finish));
289 source_range src_range;
290 src_range.m_start = start;
291 src_range.m_finish = finish;
292 richloc.add_fixit_remove (src_range);
293 warning_at_rich_loc (&richloc, 0, "example of a removal hint");
296 if (0 == strcmp (fnname, "test_fixit_replace"))
298 const int line = fnstart_line + 2;
299 location_t start = get_loc (line, 2);
300 location_t finish = get_loc (line, 19);
301 rich_location richloc (line_table, make_location (start, start, finish));
302 source_range src_range;
303 src_range.m_start = start;
304 src_range.m_finish = finish;
305 richloc.add_fixit_replace (src_range, "gtk_widget_show_all");
306 warning_at_rich_loc (&richloc, 0, "example of a replacement hint");
309 if (0 == strcmp (fnname, "test_mutually_exclusive_suggestions"))
311 const int line = fnstart_line + 2;
312 location_t start = get_loc (line, 2);
313 location_t finish = get_loc (line, 9);
314 source_range src_range;
315 src_range.m_start = start;
316 src_range.m_finish = finish;
319 rich_location richloc (line_table, make_location (start, start, finish));
320 richloc.add_fixit_replace (src_range, "replacement_1");
321 richloc.fixits_cannot_be_auto_applied ();
322 warning_at_rich_loc (&richloc, 0, "warning 1");
326 rich_location richloc (line_table, make_location (start, start, finish));
327 richloc.add_fixit_replace (src_range, "replacement_2");
328 richloc.fixits_cannot_be_auto_applied ();
329 warning_at_rich_loc (&richloc, 0, "warning 2");
333 /* Example of two carets where both carets appear to have an off-by-one
334 error appearing one column early.
335 Seen with gfortran.dg/associate_5.f03.
336 In an earlier version of the printer, the printing of caret 0 aka
337 "1" was suppressed due to it appearing within the leading whitespace
338 before the text in its line. Ensure that we at least faithfully
339 print both carets, at the given (erroneous) locations. */
340 if (0 == strcmp (fnname, "test_caret_on_leading_whitespace"))
342 const int line = fnstart_line + 3;
343 location_t caret_a = get_loc (line, 5);
344 location_t caret_b = get_loc (line - 1, 19);
345 rich_location richloc (line_table, caret_a);
346 richloc.add_range (caret_b, true);
347 global_dc->caret_chars[0] = '1';
348 global_dc->caret_chars[1] = '2';
349 warning_at_rich_loc (&richloc, 0, "test");
350 global_dc->caret_chars[0] = '^';
351 global_dc->caret_chars[1] = '^';
354 /* Example of using the "%q+D" format code, which as well as printing
355 a quoted decl, overrides the given location to use the location of
356 the decl. */
357 if (0 == strcmp (fnname, "test_percent_q_plus_d"))
359 const int line = fnstart_line + 3;
360 tree local = (*fun->local_decls)[0];
361 warning_at (input_location, 0,
362 "example of plus in format code for %q+D", local);
365 /* Example of many locations and many fixits.
366 Underline (separately) every word in a comment, and convert them
367 to upper case. */
368 if (0 == strcmp (fnname, "test_many_nested_locations"))
370 const char *file = LOCATION_FILE (fnstart);
371 const int start_line = fnstart_line + 2;
372 const int finish_line = start_line + 7;
373 location_t loc = get_loc (start_line - 1, 2);
374 rich_location richloc (line_table, loc);
375 for (int line = start_line; line <= finish_line; line++)
377 int line_size;
378 const char *content = location_get_source_line (file, line,
379 &line_size);
380 gcc_assert (content);
381 /* Split line up into words. */
382 for (int idx = 0; idx < line_size; idx++)
384 if (ISALPHA (content[idx]))
386 int start_idx = idx;
387 while (idx < line_size && ISALPHA (content[idx]))
388 idx++;
389 if (idx == line_size || !ISALPHA (content[idx]))
391 location_t start_of_word = get_loc (line, start_idx);
392 location_t end_of_word = get_loc (line, idx - 1);
393 location_t word
394 = make_location (start_of_word, start_of_word,
395 end_of_word);
396 richloc.add_range (word, true);
398 /* Add a fixit, converting to upper case. */
399 char *copy = xstrndup (content + start_idx,
400 idx - start_idx);
401 for (char *ch = copy; *ch; ch++)
402 *ch = TOUPPER (*ch);
403 richloc.add_fixit_replace (word, copy);
404 free (copy);
409 /* Verify that we added enough locations to fully exercise
410 rich_location. We want to exceed both the
411 statically-allocated buffer in class rich_location,
412 and then trigger a reallocation of the dynamic buffer. */
413 gcc_assert (richloc.get_num_locations () > 3 + (2 * 16));
414 warning_at_rich_loc (&richloc, 0, "test of %i locations",
415 richloc.get_num_locations ());
419 unsigned int
420 pass_test_show_locus::execute (function *fun)
422 test_show_locus (fun);
423 return 0;
426 static gimple_opt_pass *
427 make_pass_test_show_locus (gcc::context *ctxt)
429 return new pass_test_show_locus (ctxt);
433 plugin_init (struct plugin_name_args *plugin_info,
434 struct plugin_gcc_version *version)
436 struct register_pass_info pass_info;
437 const char *plugin_name = plugin_info->base_name;
438 int argc = plugin_info->argc;
439 struct plugin_argument *argv = plugin_info->argv;
441 if (!plugin_default_version_check (version, &gcc_version))
442 return 1;
444 for (int i = 0; i < argc; i++)
446 if (0 == strcmp (argv[i].key, "color"))
447 force_show_locus_color = true;
450 pass_info.pass = make_pass_test_show_locus (g);
451 pass_info.reference_pass_name = "ssa";
452 pass_info.ref_pass_instance_number = 1;
453 pass_info.pos_op = PASS_POS_INSERT_AFTER;
454 register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
455 &pass_info);
457 return 0;