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