Branch data Line data Source code
1 : : /* Implementation of -Wmisleading-indentation
2 : : Copyright (C) 2015-2025 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 "tm.h"
24 : : #include "c-common.h"
25 : : #include "c-indentation.h"
26 : : #include "selftest.h"
27 : : #include "diagnostic.h"
28 : :
29 : : /* Round up VIS_COLUMN to nearest tab stop. */
30 : :
31 : : static unsigned int
32 : 341987 : next_tab_stop (unsigned int vis_column, unsigned int tab_width)
33 : : {
34 : 342014 : vis_column = ((vis_column + tab_width) / tab_width) * tab_width;
35 : 341987 : return vis_column;
36 : : }
37 : :
38 : : /* Convert libcpp's notion of a column (a 1-based char count) to
39 : : the "visual column" (0-based column, respecting tabs), by reading the
40 : : relevant line.
41 : :
42 : : Returns true if a conversion was possible, writing the result to OUT,
43 : : otherwise returns false. If FIRST_NWS is not NULL, then write to it
44 : : the visual column corresponding to the first non-whitespace character
45 : : on the line (up to or before EXPLOC). */
46 : :
47 : : static bool
48 : 733434 : get_visual_column (file_cache &fc,
49 : : expanded_location exploc,
50 : : unsigned int *out,
51 : : unsigned int *first_nws,
52 : : unsigned int tab_width)
53 : : {
54 : 733434 : char_span line = fc.get_source_line (exploc.file, exploc.line);
55 : 733434 : if (!line)
56 : : return false;
57 : 733434 : if ((size_t)exploc.column > line.length ())
58 : : return false;
59 : : unsigned int vis_column = 0;
60 : 4020040 : for (int i = 1; i < exploc.column; i++)
61 : : {
62 : 3286612 : unsigned char ch = line[i - 1];
63 : :
64 : 3286612 : if (first_nws != NULL && !ISSPACE (ch))
65 : : {
66 : 41902 : *first_nws = vis_column;
67 : 41902 : first_nws = NULL;
68 : : }
69 : :
70 : 3286612 : if (ch == '\t')
71 : 341979 : vis_column = next_tab_stop (vis_column, tab_width);
72 : : else
73 : 2944633 : vis_column++;
74 : : }
75 : :
76 : 733428 : if (first_nws != NULL)
77 : 691526 : *first_nws = vis_column;
78 : :
79 : 733428 : *out = vis_column;
80 : 733428 : return true;
81 : : }
82 : :
83 : : /* Attempt to determine the first non-whitespace character in line LINE_NUM
84 : : of source line FILE.
85 : :
86 : : If this is possible, return true and write its "visual column" to
87 : : *FIRST_NWS.
88 : : Otherwise, return false, leaving *FIRST_NWS untouched. */
89 : :
90 : : static bool
91 : 106 : get_first_nws_vis_column (file_cache &fc,
92 : : const char *file, int line_num,
93 : : unsigned int *first_nws,
94 : : unsigned int tab_width)
95 : : {
96 : 106 : gcc_assert (first_nws);
97 : :
98 : 106 : char_span line = fc.get_source_line (file, line_num);
99 : 106 : if (!line)
100 : : return false;
101 : : unsigned int vis_column = 0;
102 : 570 : for (size_t i = 1; i < line.length (); i++)
103 : : {
104 : 550 : unsigned char ch = line[i - 1];
105 : :
106 : 550 : if (!ISSPACE (ch))
107 : : {
108 : 86 : *first_nws = vis_column;
109 : 86 : return true;
110 : : }
111 : :
112 : 464 : if (ch == '\t')
113 : 8 : vis_column = next_tab_stop (vis_column, tab_width);
114 : : else
115 : 456 : vis_column++;
116 : : }
117 : :
118 : : /* No non-whitespace characters found. */
119 : : return false;
120 : : }
121 : :
122 : : /* Determine if there is an unindent/outdent between
123 : : BODY_EXPLOC and NEXT_STMT_EXPLOC, to ensure that we don't
124 : : issue a warning for cases like the following:
125 : :
126 : : (1) Preprocessor logic
127 : :
128 : : if (flagA)
129 : : foo ();
130 : : ^ BODY_EXPLOC
131 : : #if SOME_CONDITION_THAT_DOES_NOT_HOLD
132 : : if (flagB)
133 : : #endif
134 : : bar ();
135 : : ^ NEXT_STMT_EXPLOC
136 : :
137 : : "bar ();" is visually aligned below "foo ();" and
138 : : is (as far as the parser sees) the next token, but
139 : : this isn't misleading to a human reader.
140 : :
141 : : (2) Empty macro with bad indentation
142 : :
143 : : In the following, the
144 : : "if (i > 0)"
145 : : is poorly indented, and ought to be on the same column as
146 : : "engine_ref_debug(e, 0, -1)"
147 : : However, it is not misleadingly indented, due to the presence
148 : : of that macro.
149 : :
150 : : #define engine_ref_debug(X, Y, Z)
151 : :
152 : : if (locked)
153 : : i = foo (0);
154 : : else
155 : : i = foo (1);
156 : : engine_ref_debug(e, 0, -1)
157 : : if (i > 0)
158 : : return 1;
159 : :
160 : : Return true if such an unindent/outdent is detected. */
161 : :
162 : : static bool
163 : 210 : detect_intervening_unindent (file_cache &fc,
164 : : const char *file,
165 : : int body_line,
166 : : int next_stmt_line,
167 : : unsigned int vis_column,
168 : : unsigned int tab_width)
169 : : {
170 : 210 : gcc_assert (file);
171 : 210 : gcc_assert (next_stmt_line > body_line);
172 : :
173 : 286 : for (int line = body_line + 1; line < next_stmt_line; line++)
174 : : {
175 : 106 : unsigned int line_vis_column;
176 : 106 : if (get_first_nws_vis_column (fc, file, line, &line_vis_column, tab_width))
177 : 86 : if (line_vis_column < vis_column)
178 : 30 : return true;
179 : : }
180 : :
181 : : /* Not found. */
182 : : return false;
183 : : }
184 : :
185 : :
186 : : /* Helper function for warn_for_misleading_indentation; see
187 : : description of that function below. */
188 : :
189 : : static bool
190 : 1002258 : should_warn_for_misleading_indentation (const token_indent_info &guard_tinfo,
191 : : const token_indent_info &body_tinfo,
192 : : const token_indent_info &next_tinfo)
193 : : {
194 : : /* Don't attempt to compare indentation if #line or # 44 "file"-style
195 : : directives are present, suggesting generated code.
196 : :
197 : : All bets are off if these are present: the file that the #line
198 : : directive could have an entirely different coding layout to C/C++
199 : : (e.g. .md files).
200 : :
201 : : To determine if a #line is present, in theory we could look for a
202 : : map with reason == LC_RENAME_VERBATIM. However, if there has
203 : : subsequently been a long line requiring a column number larger than
204 : : that representable by the original LC_RENAME_VERBATIM map, then
205 : : we'll have a map with reason LC_RENAME.
206 : : Rather than attempting to search all of the maps for a
207 : : LC_RENAME_VERBATIM, instead we have libcpp set a flag whenever one
208 : : is seen, and we check for the flag here.
209 : : */
210 : 1002258 : if (line_table->seen_line_directive)
211 : : return false;
212 : :
213 : : /* We can't usefully warn about do-while and switch statements since the
214 : : bodies of these statements are always explicitly delimited at both ends,
215 : : so control flow is quite obvious. */
216 : 1001867 : if (guard_tinfo.keyword == RID_DO
217 : 929180 : || guard_tinfo.keyword == RID_SWITCH)
218 : : return false;
219 : :
220 : : /* If the token following the body is a close brace or an "else"
221 : : then while indentation may be sloppy, there is not much ambiguity
222 : : about control flow, e.g.
223 : :
224 : : if (foo) <- GUARD
225 : : bar (); <- BODY
226 : : else baz (); <- NEXT
227 : :
228 : : {
229 : : while (foo) <- GUARD
230 : : bar (); <- BODY
231 : : } <- NEXT
232 : : baz ();
233 : : */
234 : 927125 : enum cpp_ttype next_tok_type = next_tinfo.type;
235 : 927125 : if (next_tok_type == CPP_CLOSE_BRACE
236 : 610424 : || next_tinfo.keyword == RID_ELSE)
237 : : return false;
238 : :
239 : : /* Likewise, if the body of the guard is a compound statement then control
240 : : flow is quite visually explicit regardless of the code's possibly poor
241 : : indentation, e.g.
242 : :
243 : : while (foo) <- GUARD
244 : : { <- BODY
245 : : bar ();
246 : : }
247 : : baz (); <- NEXT
248 : :
249 : : Things only get muddy when the body of the guard does not have
250 : : braces, e.g.
251 : :
252 : : if (foo) <- GUARD
253 : : bar (); <- BODY
254 : : baz (); <- NEXT
255 : : */
256 : 443933 : enum cpp_ttype body_type = body_tinfo.type;
257 : 443933 : if (body_type == CPP_OPEN_BRACE)
258 : : return false;
259 : :
260 : : /* Don't warn here about spurious semicolons. */
261 : 273633 : if (next_tok_type == CPP_SEMICOLON)
262 : : return false;
263 : :
264 : 270725 : location_t guard_loc = guard_tinfo.location;
265 : 270725 : location_t body_loc = body_tinfo.location;
266 : 270725 : location_t next_stmt_loc = next_tinfo.location;
267 : :
268 : : /* Resolve each token location to the respective macro expansion
269 : : point that produced the token. */
270 : 270725 : if (linemap_location_from_macro_expansion_p (line_table, guard_loc))
271 : 31194 : guard_loc = linemap_resolve_location (line_table, guard_loc,
272 : : LRK_MACRO_EXPANSION_POINT, NULL);
273 : 270725 : if (linemap_location_from_macro_expansion_p (line_table, body_loc))
274 : 35560 : body_loc = linemap_resolve_location (line_table, body_loc,
275 : : LRK_MACRO_EXPANSION_POINT, NULL);
276 : 270725 : if (linemap_location_from_macro_expansion_p (line_table, next_stmt_loc))
277 : 38262 : next_stmt_loc = linemap_resolve_location (line_table, next_stmt_loc,
278 : : LRK_MACRO_EXPANSION_POINT, NULL);
279 : :
280 : : /* When all three tokens are produced from a single macro expansion, we
281 : : instead consider their loci inside that macro's definition. */
282 : 270725 : if (guard_loc == body_loc && body_loc == next_stmt_loc)
283 : : {
284 : 20317 : const line_map *guard_body_common_map
285 : 40634 : = first_map_in_common (line_table,
286 : 20317 : guard_tinfo.location, body_tinfo.location,
287 : : &guard_loc, &body_loc);
288 : 20317 : const line_map *body_next_common_map
289 : 40634 : = first_map_in_common (line_table,
290 : 20317 : body_tinfo.location, next_tinfo.location,
291 : : &body_loc, &next_stmt_loc);
292 : :
293 : : /* Punt on complicated nesting of macros. */
294 : 20317 : if (guard_body_common_map != body_next_common_map)
295 : : return false;
296 : :
297 : 4981 : guard_loc = linemap_resolve_location (line_table, guard_loc,
298 : : LRK_MACRO_DEFINITION_LOCATION, NULL);
299 : 4981 : body_loc = linemap_resolve_location (line_table, body_loc,
300 : : LRK_MACRO_DEFINITION_LOCATION, NULL);
301 : 4981 : next_stmt_loc = linemap_resolve_location (line_table, next_stmt_loc,
302 : : LRK_MACRO_DEFINITION_LOCATION,
303 : : NULL);
304 : : }
305 : :
306 : 255389 : expanded_location body_exploc = expand_location (body_loc);
307 : 255389 : expanded_location next_stmt_exploc = expand_location (next_stmt_loc);
308 : 255389 : expanded_location guard_exploc = expand_location (guard_loc);
309 : :
310 : : /* PR c++/68819: if the column number is zero, we presumably
311 : : had a location_t > LINE_MAP_MAX_LOCATION_WITH_COLS, and so
312 : : we have no column information. */
313 : 255389 : if (!guard_exploc.column || !body_exploc.column || !next_stmt_exploc.column)
314 : : {
315 : 2 : static bool issued_note = false;
316 : 2 : if (!issued_note)
317 : : {
318 : : /* Notify the user the first time this happens. */
319 : 1 : issued_note = true;
320 : 1 : inform (guard_loc,
321 : : "%<-Wmisleading-indentation%> is disabled from this point"
322 : : " onwards, since column-tracking was disabled due to"
323 : : " the size of the code/headers");
324 : : }
325 : 2 : return false;
326 : : }
327 : :
328 : : /* Give up if the loci are not all distinct. */
329 : 255387 : if (guard_loc == body_loc || body_loc == next_stmt_loc)
330 : : return false;
331 : :
332 : 244502 : const unsigned int tab_width = global_dc->m_tabstop;
333 : :
334 : : /* They must be in the same file. */
335 : 244502 : if (next_stmt_exploc.file != body_exploc.file)
336 : : return false;
337 : :
338 : 244502 : file_cache &fc = global_dc->get_file_cache ();
339 : :
340 : : /* If NEXT_STMT_LOC and BODY_LOC are on the same line, consider
341 : : the location of the guard.
342 : :
343 : : Cases where we want to issue a warning:
344 : :
345 : : if (flag)
346 : : foo (); bar ();
347 : : ^ WARN HERE
348 : :
349 : : if (flag) foo (); bar ();
350 : : ^ WARN HERE
351 : :
352 : :
353 : : if (flag) ; {
354 : : ^ WARN HERE
355 : :
356 : : if (flag)
357 : : ; {
358 : : ^ WARN HERE
359 : :
360 : : Cases where we don't want to issue a warning:
361 : :
362 : : various_code (); if (flag) foo (); bar (); more_code ();
363 : : ^ DON'T WARN HERE. */
364 : 244502 : if (next_stmt_exploc.line == body_exploc.line)
365 : : {
366 : 48 : if (guard_exploc.file != body_exploc.file)
367 : : return true;
368 : 48 : if (guard_exploc.line < body_exploc.line)
369 : : /* The guard is on a line before a line that contains both
370 : : the body and the next stmt. */
371 : : return true;
372 : 36 : else if (guard_exploc.line == body_exploc.line)
373 : : {
374 : : /* They're all on the same line. */
375 : 36 : gcc_assert (guard_exploc.file == next_stmt_exploc.file);
376 : 36 : gcc_assert (guard_exploc.line == next_stmt_exploc.line);
377 : 36 : unsigned int guard_vis_column;
378 : 36 : unsigned int guard_line_first_nws;
379 : 36 : if (!get_visual_column (fc,
380 : : guard_exploc,
381 : : &guard_vis_column,
382 : : &guard_line_first_nws, tab_width))
383 : 17 : return false;
384 : : /* Heuristic: only warn if the guard is the first thing
385 : : on its line. */
386 : 36 : if (guard_vis_column == guard_line_first_nws)
387 : : return true;
388 : : }
389 : : }
390 : :
391 : : /* If NEXT_STMT_LOC is on a line after BODY_LOC, consider
392 : : their relative locations, and of the guard.
393 : :
394 : : Cases where we want to issue a warning:
395 : : if (flag)
396 : : foo ();
397 : : bar ();
398 : : ^ WARN HERE
399 : :
400 : : Cases where we don't want to issue a warning:
401 : : if (flag)
402 : : foo ();
403 : : bar ();
404 : : ^ DON'T WARN HERE (autogenerated code?)
405 : :
406 : : if (flagA)
407 : : foo ();
408 : : #if SOME_CONDITION_THAT_DOES_NOT_HOLD
409 : : if (flagB)
410 : : #endif
411 : : bar ();
412 : : ^ DON'T WARN HERE
413 : :
414 : : if (flag)
415 : : ;
416 : : foo ();
417 : : ^ DON'T WARN HERE
418 : :
419 : : #define emit
420 : : if (flag)
421 : : foo ();
422 : : emit bar ();
423 : : ^ DON'T WARN HERE
424 : :
425 : : */
426 : 244473 : if (next_stmt_exploc.line > body_exploc.line)
427 : : {
428 : : /* Determine if GUARD_LOC and NEXT_STMT_LOC are aligned on the same
429 : : "visual column"... */
430 : 244454 : unsigned int next_stmt_vis_column;
431 : 244454 : unsigned int next_stmt_line_first_nws;
432 : 244454 : unsigned int body_vis_column;
433 : 244454 : unsigned int body_line_first_nws;
434 : 244454 : unsigned int guard_vis_column;
435 : 244454 : unsigned int guard_line_first_nws;
436 : : /* If we can't determine it, don't issue a warning. This is sometimes
437 : : the case for input files containing #line directives, and these
438 : : are often for autogenerated sources (e.g. from .md files), where
439 : : it's not clear that it's meaningful to look at indentation. */
440 : 244454 : if (!get_visual_column (fc,
441 : : next_stmt_exploc,
442 : : &next_stmt_vis_column,
443 : : &next_stmt_line_first_nws, tab_width))
444 : 677 : return false;
445 : 244454 : if (!get_visual_column (fc,
446 : : body_exploc,
447 : : &body_vis_column,
448 : : &body_line_first_nws, tab_width))
449 : : return false;
450 : 244454 : if (!get_visual_column (fc,
451 : : guard_exploc,
452 : : &guard_vis_column,
453 : : &guard_line_first_nws, tab_width))
454 : : return false;
455 : :
456 : : /* If the line where the next stmt starts has non-whitespace
457 : : on it before the stmt, then don't warn:
458 : : #define emit
459 : : if (flag)
460 : : foo ();
461 : : emit bar ();
462 : : ^ DON'T WARN HERE
463 : : (PR c/69122). */
464 : 244454 : if (next_stmt_line_first_nws < next_stmt_vis_column)
465 : : return false;
466 : :
467 : 244170 : if ((body_type != CPP_SEMICOLON
468 : 243622 : && next_stmt_vis_column == body_vis_column)
469 : : /* As a special case handle the case where the body is a semicolon
470 : : that may be hidden by a preceding comment, e.g. */
471 : :
472 : : // if (p)
473 : : // /* blah */;
474 : : // foo (1);
475 : :
476 : : /* by looking instead at the column of the first non-whitespace
477 : : character on the body line. */
478 : : || (body_type == CPP_SEMICOLON
479 : 548 : && body_exploc.line > guard_exploc.line
480 : 485 : && body_line_first_nws != body_vis_column
481 : 355 : && next_stmt_vis_column > guard_line_first_nws))
482 : : {
483 : : /* Don't warn if they are aligned on the same column
484 : : as the guard itself (suggesting autogenerated code that doesn't
485 : : bother indenting at all).
486 : : For "else" clauses, we consider the column of the first
487 : : non-whitespace character on the guard line instead of the column
488 : : of the actual guard token itself because it is more sensible.
489 : : Consider:
490 : :
491 : : if (p) {
492 : : foo (1);
493 : : } else // GUARD
494 : : foo (2); // BODY
495 : : foo (3); // NEXT
496 : :
497 : : and:
498 : :
499 : : if (p)
500 : : foo (1);
501 : : } else // GUARD
502 : : foo (2); // BODY
503 : : foo (3); // NEXT
504 : :
505 : : If we just used the column of the "else" token, we would warn on
506 : : the first example and not warn on the second. But we want the
507 : : exact opposite to happen: to not warn on the first example (which
508 : : is probably autogenerated) and to warn on the second (whose
509 : : indentation is misleading). Using the column of the first
510 : : non-whitespace character on the guard line makes that
511 : : happen. */
512 : 706 : unsigned int guard_column = (guard_tinfo.keyword == RID_ELSE
513 : 353 : ? guard_line_first_nws
514 : : : guard_vis_column);
515 : 353 : if (guard_column == body_vis_column)
516 : : return false;
517 : :
518 : : /* We may have something like:
519 : :
520 : : if (p)
521 : : {
522 : : foo (1);
523 : : } else // GUARD
524 : : foo (2); // BODY
525 : : foo (3); // NEXT
526 : :
527 : : in which case the columns are not aligned but the code is not
528 : : misleadingly indented. If the column of the body isn't indented
529 : : more than the guard line then don't warn. */
530 : 230 : if (body_vis_column <= guard_line_first_nws)
531 : : return false;
532 : :
533 : : /* Don't warn if there is an unindent between the two statements. */
534 : 210 : int vis_column = MIN (next_stmt_vis_column, body_vis_column);
535 : 210 : if (detect_intervening_unindent (fc,
536 : : body_exploc.file, body_exploc.line,
537 : : next_stmt_exploc.line,
538 : : vis_column, tab_width))
539 : : return false;
540 : :
541 : : /* Otherwise, they are visually aligned: issue a warning. */
542 : : return true;
543 : : }
544 : :
545 : : /* Also issue a warning for code having the form:
546 : :
547 : : if (flag);
548 : : foo ();
549 : :
550 : : while (flag);
551 : : {
552 : : ...
553 : : }
554 : :
555 : : for (...);
556 : : {
557 : : ...
558 : : }
559 : :
560 : : if (flag)
561 : : ;
562 : : else if (flag);
563 : : foo ();
564 : :
565 : : where the semicolon at the end of each guard is most likely spurious.
566 : :
567 : : But do not warn on:
568 : :
569 : : for (..);
570 : : foo ();
571 : :
572 : : where the next statement is aligned with the guard.
573 : : */
574 : 528 : if (body_type == CPP_SEMICOLON)
575 : : {
576 : 528 : if (body_exploc.line == guard_exploc.line)
577 : : {
578 : 63 : if (next_stmt_vis_column > guard_line_first_nws
579 : 31 : || (next_tok_type == CPP_OPEN_BRACE
580 : 8 : && next_stmt_vis_column == guard_line_first_nws))
581 : : return true;
582 : : }
583 : : }
584 : : }
585 : :
586 : : return false;
587 : : }
588 : :
589 : : /* Return the string identifier corresponding to the given guard token. */
590 : :
591 : : const char *
592 : 602 : guard_tinfo_to_string (enum rid keyword)
593 : : {
594 : 602 : switch (keyword)
595 : : {
596 : : case RID_FOR:
597 : : return "for";
598 : 76 : case RID_ELSE:
599 : 76 : return "else";
600 : 334 : case RID_IF:
601 : 334 : return "if";
602 : 64 : case RID_WHILE:
603 : 64 : return "while";
604 : 0 : case RID_DO:
605 : 0 : return "do";
606 : 16 : case RID_SWITCH:
607 : 16 : return "switch";
608 : 0 : default:
609 : 0 : gcc_unreachable ();
610 : : }
611 : : }
612 : :
613 : : /* Called by the C/C++ frontends when we have a guarding statement at
614 : : GUARD_LOC containing a statement at BODY_LOC, where the block wasn't
615 : : written using braces, like this:
616 : :
617 : : if (flag)
618 : : foo ();
619 : :
620 : : along with the location of the next token, at NEXT_STMT_LOC,
621 : : so that we can detect followup statements that are within
622 : : the same "visual block" as the guarded statement, but which
623 : : aren't logically grouped within the guarding statement, such
624 : : as:
625 : :
626 : : GUARD_LOC
627 : : |
628 : : V
629 : : if (flag)
630 : : foo (); <- BODY_LOC
631 : : bar (); <- NEXT_STMT_LOC
632 : :
633 : : In the above, "bar ();" isn't guarded by the "if", but
634 : : is indented to misleadingly suggest that it is in the same
635 : : block as "foo ();".
636 : :
637 : : GUARD_KIND identifies the kind of clause e.g. "if", "else" etc. */
638 : :
639 : : void
640 : 61913555 : warn_for_misleading_indentation (const token_indent_info &guard_tinfo,
641 : : const token_indent_info &body_tinfo,
642 : : const token_indent_info &next_tinfo)
643 : : {
644 : : /* Early reject for the case where -Wmisleading-indentation is disabled,
645 : : to avoid doing work only to have the warning suppressed inside the
646 : : diagnostic machinery. */
647 : 61913555 : if (!warn_misleading_indentation)
648 : : return;
649 : :
650 : 1002258 : if (should_warn_for_misleading_indentation (guard_tinfo,
651 : : body_tinfo,
652 : : next_tinfo))
653 : : {
654 : 249 : auto_diagnostic_group d;
655 : 249 : if (warning_at (guard_tinfo.location, OPT_Wmisleading_indentation,
656 : : "this %qs clause does not guard...",
657 : 249 : guard_tinfo_to_string (guard_tinfo.keyword)))
658 : 245 : inform (next_tinfo.location,
659 : : "...this statement, but the latter is misleadingly indented"
660 : : " as if it were guarded by the %qs",
661 : 245 : guard_tinfo_to_string (guard_tinfo.keyword));
662 : 249 : }
663 : : }
664 : :
665 : : #if CHECKING_P
666 : :
667 : : namespace selftest {
668 : :
669 : : /* Verify that next_tab_stop works as expected. */
670 : :
671 : : static void
672 : 3 : test_next_tab_stop ()
673 : : {
674 : 3 : const unsigned int tab_width = 8;
675 : :
676 : 3 : ASSERT_EQ (next_tab_stop (0, tab_width), 8);
677 : 3 : ASSERT_EQ (next_tab_stop (1, tab_width), 8);
678 : 3 : ASSERT_EQ (next_tab_stop (7, tab_width), 8);
679 : :
680 : 3 : ASSERT_EQ (next_tab_stop (8, tab_width), 16);
681 : 3 : ASSERT_EQ (next_tab_stop (9, tab_width), 16);
682 : 3 : ASSERT_EQ (next_tab_stop (15, tab_width), 16);
683 : :
684 : 3 : ASSERT_EQ (next_tab_stop (16, tab_width), 24);
685 : 3 : ASSERT_EQ (next_tab_stop (17, tab_width), 24);
686 : 3 : ASSERT_EQ (next_tab_stop (23, tab_width), 24);
687 : 3 : }
688 : :
689 : : /* Verify that the given call to get_visual_column succeeds, with
690 : : the given results. */
691 : :
692 : : static void
693 : 30 : assert_get_visual_column_succeeds (const location &loc,
694 : : file_cache &fc,
695 : : const char *file, int line, int column,
696 : : const unsigned int tab_width,
697 : : unsigned int expected_visual_column,
698 : : unsigned int expected_first_nws)
699 : : {
700 : 30 : expanded_location exploc;
701 : 30 : exploc.file = file;
702 : 30 : exploc.line = line;
703 : 30 : exploc.column = column;
704 : 30 : exploc.data = NULL;
705 : 30 : exploc.sysp = false;
706 : 30 : unsigned int actual_visual_column;
707 : 30 : unsigned int actual_first_nws;
708 : 30 : bool result = get_visual_column (fc,
709 : : exploc,
710 : : &actual_visual_column,
711 : : &actual_first_nws, tab_width);
712 : 30 : ASSERT_TRUE_AT (loc, result);
713 : 30 : ASSERT_EQ_AT (loc, actual_visual_column, expected_visual_column);
714 : 30 : ASSERT_EQ_AT (loc, actual_first_nws, expected_first_nws);
715 : 30 : }
716 : :
717 : : /* Verify that the given call to get_visual_column succeeds, with
718 : : the given results. */
719 : :
720 : : #define ASSERT_GET_VISUAL_COLUMN_SUCCEEDS(FILE_CACHE, \
721 : : FILENAME, LINE, COLUMN, \
722 : : TAB_WIDTH, \
723 : : EXPECTED_VISUAL_COLUMN, \
724 : : EXPECTED_FIRST_NWS) \
725 : : SELFTEST_BEGIN_STMT \
726 : : assert_get_visual_column_succeeds (SELFTEST_LOCATION, \
727 : : FILE_CACHE, \
728 : : FILENAME, LINE, COLUMN, \
729 : : TAB_WIDTH, \
730 : : EXPECTED_VISUAL_COLUMN, \
731 : : EXPECTED_FIRST_NWS); \
732 : : SELFTEST_END_STMT
733 : :
734 : : /* Verify that the given call to get_visual_column fails gracefully. */
735 : :
736 : : static void
737 : 6 : assert_get_visual_column_fails (const location &loc,
738 : : file_cache &fc,
739 : : const char *file, int line, int column,
740 : : const unsigned int tab_width)
741 : : {
742 : 6 : expanded_location exploc;
743 : 6 : exploc.file = file;
744 : 6 : exploc.line = line;
745 : 6 : exploc.column = column;
746 : 6 : exploc.data = NULL;
747 : 6 : exploc.sysp = false;
748 : 6 : unsigned int actual_visual_column;
749 : 6 : unsigned int actual_first_nws;
750 : 6 : bool result = get_visual_column (fc,
751 : : exploc,
752 : : &actual_visual_column,
753 : : &actual_first_nws, tab_width);
754 : 6 : ASSERT_FALSE_AT (loc, result);
755 : 6 : }
756 : :
757 : : /* Verify that the given call to get_visual_column fails gracefully. */
758 : :
759 : : #define ASSERT_GET_VISUAL_COLUMN_FAILS(FILE_CACHE, \
760 : : FILENAME, LINE, COLUMN, \
761 : : TAB_WIDTH) \
762 : : SELFTEST_BEGIN_STMT \
763 : : assert_get_visual_column_fails (SELFTEST_LOCATION, \
764 : : FILE_CACHE, \
765 : : FILENAME, LINE, COLUMN, \
766 : : TAB_WIDTH); \
767 : : SELFTEST_END_STMT
768 : :
769 : : /* Verify that get_visual_column works as expected. */
770 : :
771 : : static void
772 : 3 : test_get_visual_column ()
773 : : {
774 : : /* Create a tempfile with a mixture of tabs and spaces.
775 : :
776 : : Both lines have either a space or a tab, then " line N",
777 : : for 8 characters in total.
778 : :
779 : : 1-based "columns" (w.r.t. to line 1):
780 : : .....................0000000001111.
781 : : .....................1234567890123. */
782 : 3 : const char *content = (" line 1\n"
783 : : "\t line 2\n");
784 : 3 : line_table_test ltt;
785 : 3 : temp_source_file tmp (SELFTEST_LOCATION, ".txt", content);
786 : 3 : file_cache fc;
787 : :
788 : 3 : const unsigned int tab_width = 8;
789 : 3 : const char *file = tmp.get_filename ();
790 : :
791 : : /* Line 1 (space-based indentation). */
792 : 3 : {
793 : 3 : const int line = 1;
794 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 1, tab_width, 0, 0);
795 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 2, tab_width, 1, 1);
796 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 3, tab_width, 2, 2);
797 : : /* first_nws should have stopped increasing. */
798 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 4, tab_width, 3, 2);
799 : : /* Verify the end-of-line boundary. */
800 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 8, tab_width, 7, 2);
801 : 3 : ASSERT_GET_VISUAL_COLUMN_FAILS (fc, file, line, 9, tab_width);
802 : : }
803 : :
804 : : /* Line 2 (tab-based indentation). */
805 : 3 : {
806 : 3 : const int line = 2;
807 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 1, tab_width, 0, 0);
808 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 2, tab_width, 8, 8);
809 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 3, tab_width, 9, 9);
810 : : /* first_nws should have stopped increasing. */
811 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 4, tab_width, 10, 9);
812 : : /* Verify the end-of-line boundary. */
813 : 3 : ASSERT_GET_VISUAL_COLUMN_SUCCEEDS (fc, file, line, 8, tab_width, 14, 9);
814 : 3 : ASSERT_GET_VISUAL_COLUMN_FAILS (fc, file, line, 9, tab_width);
815 : : }
816 : 3 : }
817 : :
818 : : /* Run all of the selftests within this file. */
819 : :
820 : : void
821 : 3 : c_indentation_cc_tests ()
822 : : {
823 : 3 : test_next_tab_stop ();
824 : 3 : test_get_visual_column ();
825 : 3 : }
826 : :
827 : : } // namespace selftest
828 : :
829 : : #endif /* CHECKING_P */
|