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