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