On Tue, Apr 21, 2015 at 12:07:00PM -0400, David Malcolm wrote:
> On Thu, 2015-04-16 at 10:26 -0700, Mike Stump wrote:
> > On Apr 16, 2015, at 8:01 AM, David Malcolm <dmalc...@redhat.com> wrote:
> > > Attached is a work-in-progress patch for a new
> > >  -Wmisleading-indentation
> > > warning I've been experimenting with, for GCC 6.
> > 
> > Seems like a nice idea in general.
> > 
> > Does it also handle:
> > 
> > if (cone);
> >   stmt;
> > 
> > ?  Would be good to add that to the test suite, as that is another hard to 
> > spot common error that should be caught.
> 
> Not yet, but I agree that it would be a good thing to issue a warning
> for.
> 
> > I do think that it is reasonable to warn for things like:
> > 
> >   stmt;
> >     stmt;
> > 
> > one of those two lines is likely misindented, though, maybe you want to 
> > start with the high payback things first.
> 
> > > An issue here is how to determine (i), or if it's OK to default to 8
> > 
> > Yes, 8 is the proper value to default it to.
> > 
> > > and have a command-line option (param?) to override it? (though what 
> > > about,
> > > say, each header file?)
> > 
> > I’ll abstain from this.  The purist in me says no option for other
> > than 8, life goes on.  20 years ago, someone was confused over hard v
> > soft tabbing and what exactly the editor key TAB does.  That confusion
> > is over, the 8 people have won.  Catering to other than 8 gives the
> > impression that the people that lost still have a chance at
> > winning.  :-)
> > 
> > > Thoughts on this, and on the patch?
> > 
> > Would be nice to have a stricter version that warns about all wildly 
> > inconsistently or wrongly indented lines.
> > 
> > {
> >   stmt;
> >     stmt;  // must be same as above
> > }
> > 
> > {
> > stmt; // must be indented at least 1
> > }
> > 
> > if (cond)
> > stmt;  // must be indented at least 1
> 
> I think I want to make a distinction between
> 
> (A) classic C "gotchas", like the one in my mail and the:
> 
>   if (cond);
>     stmt;
> 
> one you mentioned above
> 
> vs
> 
> (B) wrong/inconsistent indentation.
> 
> I think (A) is high-value, since it detects subtly wrong code, likely to
> have misled the reader, whereas I don't find (B) as interesting.   I
> think (A) is "misleading", whereas (B) is "wrong"; the ugliness of the
> (B) cases tends to give me a "this code is ugly; beware, danger Will
> Robinson!" reaction, whereas (A) is less ugly and thus more dangerous.

So, while I was working on ifdef stuff in gcc I found the following
pattern

#ifdef FOO
if (FOO)
#endif
  bar ();

  which you may want to handle somehow.  In that sort of case one side
  of the ifdef will necessarily have the B type of miss indentation.

  Trev

> 
> (if that makes sense; this may just be my own visceral reaction to the
> erroneous code).
> 
> Or to put it another way, I hope to make (A) good enough to go into
> -Wall, whereas I think (B) would meet more resistance. 
> Also, I think autogenerated code is more likely to run into (B) than
> (A).
> 
> I have the patch working now for the C++ frontend.  Am attaching the
> work-in-progress (sans ChangeLog).  This one (v2) bootstrapped and
> regrtested on x86_64-unknown-linux-gnu (Fedora 20), with:
>   63 new "PASS" results in gcc.sum
>   189 new "PASS" results in g++.sum
> for the new test cases (relative to a control build of r222248).
> 
> I also moved the visual-parser.c/h to c-family, to make use of the
> -ftabstop option Tom mentioned in another mail.
> 
> I also made it identify the kind of clause, so error messages say things
> like:
> 
> ./Wmisleading-indentation-1.c:10:7: warning: statement is indented as if
> it were guarded by... [-Wmisleading-indentation]
> ./Wmisleading-indentation-1.c:8:3: note: ...this 'if' clause, but it is
> not
> 
> which makes it easier to read, especially when dealing with nesting.
> 
> This hasn't yet had any performance/leak fixes so it isn't ready as is.
> I plan to look at making it warn about the:
> 
>   if (cond);
>     stmt;
> 
> gotcha next, before trying to optimize it.
> 
> (and no ChangeLog yet)
> 
> Dave

> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 80c91f0..8154469 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1143,7 +1143,8 @@ C_COMMON_OBJS = c-family/c-common.o 
> c-family/c-cppbuiltin.o c-family/c-dump.o \
>    c-family/c-ppoutput.o c-family/c-pragma.o c-family/c-pretty-print.o \
>    c-family/c-semantics.o c-family/c-ada-spec.o \
>    c-family/c-cilkplus.o \
> -  c-family/array-notation-common.o c-family/cilk.o c-family/c-ubsan.o
> +  c-family/array-notation-common.o c-family/cilk.o c-family/c-ubsan.o \
> +  c-family/visual-parser.o
>  
>  # Language-independent object files.
>  # We put the insn-*.o files first so that a parallel make will build
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 983f4a8..88f1f94 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -554,6 +554,10 @@ Wmemset-transposed-args
>  C ObjC C++ ObjC++ Var(warn_memset_transposed_args) Warning LangEnabledBy(C 
> ObjC C++ ObjC++,Wall)
>  Warn about suspicious calls to memset where the third argument is constant 
> literal zero and the second is not
>  
> +Wmisleading-indentation
> +C C++ Common Var(warn_misleading_indentation) Warning
> +Warn when the indentation of the code does not reflect the block structure
> +
>  Wmissing-braces
>  C ObjC C++ ObjC++ Var(warn_missing_braces) Warning LangEnabledBy(C ObjC,Wall)
>  Warn about possibly missing braces around initializers
> diff --git a/gcc/c-family/visual-parser.c b/gcc/c-family/visual-parser.c
> new file mode 100644
> index 0000000..b1fcb8b
> --- /dev/null
> +++ b/gcc/c-family/visual-parser.c
> @@ -0,0 +1,337 @@
> +/* "Visual parser" for detecting misleading indentation.
> +   Copyright (C) 2015 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalc...@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"                      /* For rtl.h: needs enum reg_class.  */
> +#include "hash-set.h"
> +#include "machmode.h"
> +#include "vec.h"
> +#include "double-int.h"
> +#include "input.h"
> +#include "alias.h"
> +#include "symtab.h"
> +#include "wide-int.h"
> +#include "inchash.h"
> +#include "tree.h"
> +#include "stringpool.h"
> +#include "attribs.h"
> +#include "stor-layout.h"
> +#include "varasm.h"
> +#include "trans-mem.h"
> +#include "langhooks.h"
> +#include "input.h"
> +#include "cpplib.h"
> +#include "timevar.h"
> +#include "c-family/c-pragma.h"
> +#include "flags.h"
> +#include "ggc.h"
> +#include "vec.h"
> +#include "target.h"
> +#include "visual-parser.h"
> +#include "diagnostic-core.h"
> +#include "tree-iterator.h"
> +
> +extern cpp_options *cpp_opts;
> +
> +/* Ptr to the singleton instance of visual_parser.  */
> +visual_parser *vis_parser;
> +
> +/* The ctor for visual_parser.  */
> +visual_parser::visual_parser ()
> +{
> +  m_debug = false;
> +  m_last_xloc.file = NULL;
> +  m_last_xloc.line = 0;
> +  m_last_xloc.column = 0;
> +}
> +
> +static expanded_location
> +expand_location_with_visual_column (location_t loc)
> +{
> +  /* Get file/line info.  */
> +  expanded_location xloc = expand_location (loc);
> +
> +  /* Convert libcpp's notion of a column (a 1-based char count) to
> +     the "visual column" (respecting tabs), by reading the
> +     relevant line.  */
> +  int line_len;
> +  const char *line = location_get_source_line (xloc, &line_len);
> +  int vis_column = 0;
> +  for (int i = 1; i < xloc.column; i++)
> +    {
> +      unsigned char ch = line[i - 1];
> +      if (ch == '\t')
> +     {
> +       /* Round up to nearest tab stop. */
> +       const unsigned int tab_width = cpp_opts->tabstop;
> +       vis_column = ((vis_column + tab_width) * tab_width) / tab_width;
> +     }
> +      else
> +     vis_column++;
> +    }
> +
> +  xloc.column = vis_column;
> +  //inform (loc, "vis column is %i", vis_column);
> +  return xloc;
> +}
> +
> +/* Token-handling.  This takes a stream of locations, examining their
> +   spelling locations, and calling on_indent, on_line, on_outdent
> +   accordingly.  */
> +
> +void
> +visual_parser::on_token (location_t loc)
> +{
> +  if (m_debug)
> +    inform (loc, "on_token");
> +
> +  /* TODO: only do the perhaps-expensive vis_column work if the line 
> changed?  */
> +  expanded_location xloc = expand_location_with_visual_column (loc);
> +  if (xloc.line != m_last_xloc.line)
> +    {
> +      //inform (loc, "first token on new line");
> +      visual_block *curblock = get_stack_top ();
> +      if (curblock)
> +     {
> +       /* FIXME: what about entirely empty lines???
> +          Presumably they simply don't get tokens.  */
> +       int last_indent = curblock->get_column ();
> +       if (xloc.column > last_indent)
> +         {
> +           /* This line starts more indented than any current
> +              indentation level; begin a new "visual block". */
> +           visual_block *block = new visual_block (loc, xloc.column);
> +           if (m_debug)
> +             inform (loc, "new visual block here: %p", (void *)block);
> +           m_block_stack.safe_push (block);
> +           on_indent (loc);
> +         }
> +       else if (xloc.column == last_indent)
> +         {
> +           /* This line is at the same indentation level as before,
> +              within the current "visual block".  */
> +           on_line (loc);
> +         }
> +       else
> +         {
> +           /* We have some amount of outdenting; how much?  */
> +           while (m_block_stack.length ())
> +             {
> +               visual_block *block = m_block_stack.pop ();
> +               if (m_debug)
> +                 inform (block->get_location (),
> +                         "closing visual block %p", (void *)block);
> +               on_outdent (loc);
> +               visual_block *new_top = get_stack_top ();
> +               if (new_top)
> +                 if (new_top->get_column () <= xloc.column)
> +                   {
> +                     if (m_debug)
> +                       inform (new_top->get_location (),
> +                               "outdented to within visual block %p",
> +                               (void *)new_top);
> +                     break;
> +                   }
> +             }
> +         }
> +     }
> +      else
> +     {
> +       /* No current indentation level; start one. */
> +       visual_block *block = new visual_block (loc, xloc.column);
> +       m_block_stack.safe_push (block);
> +       on_indent (loc);
> +     }
> +    }
> +  m_last_xloc = xloc;
> +}
> +
> +/* Called when we have a token that's on a new line that's more indented
> +   than the token that began the last line.  */
> +void
> +visual_parser::on_indent (location_t loc)
> +{
> +  if (m_debug)
> +    inform (loc, "on_indent");
> +}
> +
> +/* Called when we have a token that's on a new line that's less indented
> +   than the token that began the last line.  */
> +
> +void
> +visual_parser::on_outdent (location_t loc)
> +{
> +  if (m_debug)
> +    inform (loc, "on_outdent");
> +}
> +
> +/* Called for the first token on a new line that's at the same indentation
> +   level as the previous line.  */
> +void
> +visual_parser::on_line (location_t loc)
> +{
> +  if (m_debug)
> +    inform (loc, "on_line");
> +  visual_block *curblock = get_stack_top ();
> +  curblock->on_line (loc);
> +}
> +
> +/* FIXME. */
> +visual_block *
> +visual_parser::get_block_containing_loc (location_t loc) const
> +{
> +  int i;
> +  visual_block *vis_block;
> +  visual_block *candidate = NULL;
> +  FOR_EACH_VEC_ELT(m_block_stack, i, vis_block)
> +    if (vis_block->get_location () <= loc)
> +      candidate = vis_block;
> +    else
> +      break;
> +
> +  return candidate;
> +}
> +
> +/* Called by the C/C++ FE when we have a guarding statement at GUARD_LOC
> +   containing BLOCK, where the block wasn't written using braces, like
> +   this:
> +
> +     guard-loc
> +     |
> +     V
> +     if (flag)
> +       foo (); <--BLOCK
> +
> +   so that we can detect followup statements that are within
> +   the same "visual block" as the guarded statement, but which
> +   aren't logically grouped within the guarding statement, such
> +   as:
> +
> +     if (flag)
> +       foo ();
> +       bar ();
> +
> +   In the above, "bar ();" isn't guarded by the "if", but
> +   misleading is in the same visual block as "foo ();".  */
> +
> +void
> +visual_parser::on_solo_stmt (tree block, location_t guard_loc,
> +                          const char *guard_kind)
> +{
> +  /* Locate the visual block containing the solo-stmt, and mark
> +     it as such.  */
> +
> +  tree_stmt_iterator tsi = tsi_start (block);
> +  tree stmt = tsi_stmt (tsi);
> +  location_t loc_solo_stmt = EXPR_LOCATION (stmt);
> +  visual_block *vis_block = get_block_containing_loc (loc_solo_stmt);
> +
> +  vis_block->on_solo_stmt (stmt, guard_loc, guard_kind);
> +}
> +
> +/* See comment above for visual_parser::on_solo_stmt.
> +   Mark the visual block containing the solo statement STMT as
> +   (supposedly) only containing a solo statement.  */
> +
> +void
> +visual_block::on_solo_stmt (tree stmt, location_t guard_loc,
> +                         const char *guard_kind)
> +{
> +  m_loc_solo_stmt = EXPR_LOCATION (stmt);
> +  m_loc_guard = guard_loc;
> +
> +  m_exploc_solo_stmt
> +    = expand_location_with_visual_column (m_loc_solo_stmt);
> +  m_exploc_guard
> +    = expand_location_with_visual_column (guard_loc);
> +
> +  /* If the solo-stmt is on a new line and more indented than
> +     the guard location, mark the current visual block (which
> +     presumably contains the solo stmt) for checking.
> +     Doing this rejects cases such as
> +       if (foo) bar (); baz ();
> +     where it's not clear whether or not we ought to warn about
> +     "baz ();" and hence we don't.  */
> +  if (m_exploc_solo_stmt.file == m_exploc_guard.file)
> +    if (m_exploc_solo_stmt.line > m_exploc_guard.line)
> +      if (m_exploc_solo_stmt.column > m_exploc_guard.column)
> +     {
> +       if (0)
> +         {
> +           inform (m_loc,
> +                   "visual_block here (this=%p)", (void *)this);
> +           inform (m_loc_solo_stmt,
> +                   "solo statement here (this=%p)", (void *)this);
> +           inform (m_loc_guard,
> +                   "visually guarded here (this=%p)", (void *)this);
> +         }
> +       m_has_solo_stmt = true;
> +       m_guard_kind = guard_kind;
> +     }
> +}
> +
> +/* Check NEW_STMT for misleading indentation.
> +   Called when adding NEW_STMT to a statement list.
> +
> +   If we have a solo statement, and we're in the same
> +   visual block, and NEW_STMT is visually after the
> +   solo statement, then NEW_STMT is misleadingly indented as
> +   if were guarded by the guard, but it isn't.
> +   Issue a warning for such a statement.  */
> +
> +void
> +visual_parser::check_stmt (tree new_stmt)
> +{
> +  if (m_debug)
> +    inform (EXPR_LOCATION (new_stmt), "check_stmt");
> +  get_stack_top ()->check_stmt (new_stmt);
> +}
> +
> +/* See comment above for visual_parser::check_stmt.  */
> +
> +void
> +visual_block::check_stmt (tree new_stmt)
> +{
> +  if (!m_has_solo_stmt)
> +    return;
> +
> +  location_t loc_new_stmt = EXPR_LOCATION (new_stmt);
> +  //inform (loc_new_stmt, "checking stmt here");
> +
> +  expanded_location exploc_new_stmt
> +    = expand_location_with_visual_column (loc_new_stmt);
> +
> +  if (exploc_new_stmt.file == m_exploc_guard.file)
> +    {
> +      if (/* Statement is visually after the guarded stmt.  */
> +       (exploc_new_stmt.line == m_exploc_solo_stmt.line
> +       && exploc_new_stmt.column > m_exploc_solo_stmt.column)
> +       || (exploc_new_stmt.line > m_exploc_solo_stmt.line))
> +     if (warning_at (loc_new_stmt,
> +                     OPT_Wmisleading_indentation,
> +                     "statement is indented as if it"
> +                     " were guarded by..."))
> +       inform (m_loc_guard,
> +               "...this '%s' clause, but it is not",
> +               m_guard_kind);
> +    }
> +}
> diff --git a/gcc/c-family/visual-parser.h b/gcc/c-family/visual-parser.h
> new file mode 100644
> index 0000000..bbc0483
> --- /dev/null
> +++ b/gcc/c-family/visual-parser.h
> @@ -0,0 +1,172 @@
> +/* "Visual parser" for detecting misleading indentation.
> +   Copyright (C) 2015 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalc...@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +/* Forward declaration.  */
> +class visual_block;
> +
> +/* A "visual parser" for detecting misleading indentation.
> +
> +   This is fed three things by the frontend:
> +
> +   (A) a series of location_t by the frontend's tokenizer,
> +   corresponding to the locations of the token stream.  It uses
> +   this to model a stack of "visual blocks", corresponding to
> +   indentation levels within the source code.
> +   For example:
> +
> +   Source       VBlock #  VBlock stack
> +   ------       --------  ------------
> +   void         0         [b0]
> +   foo (int i)  0         [b0]
> +   {            0         [b0]
> +    int j;      .1        [b0, b1]
> +    if (i)      .1        [b0, b1]
> +     {          ..2       [b0, b1, b2]
> +       foo (0); ...3      [b0, b1, b2, b3]
> +       bar (0); ...3      [b0, b1, b2, b3]
> +     }          ..2       [b0, b1, b2]
> +    else        .1        [b0, b1]
> +     foo (1);   ..4       [b0, b1, b4]
> +     bar (1);   ..4       [b0, b1, b4] <-- WARNING!
> +   }            0         [b0]
> +
> +   Note how the call to "bar (1);" called out with "WARNING!" is
> +   indented as if it's in the same block as the call to "foo (1);",
> +   guarded by the "else" (both are in visual block 4), but they are
> +   *not* in the same actual block as far as the real frontend
> +   (and language standards) see it.
> +   The purpose of this class is to issue a warning about this
> +   misleading indentation.
> +
> +   (2) The frontend notifies the class about "solo statements", that
> +   is, non-compound statements guarded by control-flow statements,
> +   such as "foo (1);", a non-compound statement guarded by the else
> +   clause.  Misleading indentation can occur in the statement
> +   immediately following such a non-compound statement, if the
> +   successor statement is indented in the same way
> +   (i.e. it is within the same visual block).
> +
> +   (3) The frontend notifiees the class about statements being added
> +   to a statement list.  If we have a guarded non-compound statement,
> +   the new statements can be checked for misleading indentation.
> +
> +   Note that we can't simply use statement locations; for example, in:
> +     if (flag)
> +       x = 1;
> +   the "if (flag)"'s location is at the open-paren, and that of the
> +   assignment "x = 1;" is at the equals-sign, so any attempt to use
> +   statement locations can be fooled by varying the spacing:
> +
> +        V
> +     if (flag)
> +       x = 1;
> +         ^ apparent indentation relative to conditional
> +
> +         V
> +     if  (flag)
> +       x = 1;
> +         ^ same column as conditional
> +
> +           V
> +     if    (flag)
> +       x = 1;
> +         ^ apparent "outdent" relative to conditional
> +
> +   Hence we have to use token locations.  */
> +
> +class GTY(()) visual_parser {
> + public:
> +  visual_parser ();
> +  void on_token (location_t loc);
> +  void on_solo_stmt (tree block, location_t guard_loc,
> +                  const char *guard_kind);
> +  void check_stmt (tree stmt);
> +
> + private:
> +  void on_newline (location_t loc);
> +
> +  void on_indent (location_t loc);
> +  void on_outdent (location_t loc);
> +  void on_line (location_t loc);
> +
> +  visual_block * get_stack_top () const
> +  {
> +    if (m_block_stack.length ())
> +      return m_block_stack[m_block_stack.length () - 1];
> +    else
> +      return NULL;
> +  }
> +
> +  visual_block * get_block_containing_loc (location_t loc) const;
> +
> + private:
> +  bool m_debug;
> +  expanded_location m_last_xloc;
> +  /* A stack of indentation levels.  */
> +  auto_vec<visual_block *> m_block_stack;
> +};
> +
> +/* A visual block: a run of lines with the same initial indentation.  */
> +
> +class visual_block
> +{
> + public:
> +  visual_block (location_t loc, int column_start)
> +    : m_loc (loc),
> +      m_loc_last_line (loc),
> +      m_column_start (column_start),
> +      m_has_solo_stmt (false)
> +  {}
> +
> +  location_t get_location () const { return m_loc; }
> +  location_t get_last_location () const { return m_loc_last_line; }
> +  int get_column () const { return m_column_start; }
> +
> +  void on_line (location_t loc) { m_loc_last_line = loc; }
> +
> +  void on_solo_stmt (tree stmt, location_t guard_loc,
> +                  const char *guard_kind);
> +  void check_stmt (tree stmt);
> +
> + private:
> +  location_t m_loc;
> +  location_t m_loc_last_line;
> +  int m_column_start;
> +
> +  /* Detection of misleading indentation.
> +     If m_has_solo_stmt is true, then this visual
> +     block contains a "solo statement" i.e. one within a block
> +     created without braces, such as:
> +       if (flag) <- guard
> +         foo (); <- solo stmt in this visblock
> +     Any followup statements that are in the same visual block as
> +     "foo ();" are therefore misleadingly indented.  */
> +  bool m_has_solo_stmt;
> +  const char *m_guard_kind;
> +  location_t m_loc_solo_stmt;
> +  location_t m_loc_guard;
> +  expanded_location m_exploc_solo_stmt;
> +  expanded_location m_exploc_guard;
> +
> +};
> +
> +/* The singleton instance of the visual_parser, created by the
> +   C/C++ frontend if -Wmisleading-indentation is enabled.  */
> +extern visual_parser *vis_parser;
> diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> index e28a294..ec7a0a4 100644
> --- a/gcc/c/c-decl.c
> +++ b/gcc/c/c-decl.c
> @@ -81,6 +81,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "c-family/c-ada-spec.h"
>  #include "cilk.h"
>  #include "builtins.h"
> +#include "c-family/visual-parser.h"
>  
>  /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
>  enum decl_context
> @@ -651,6 +652,10 @@ add_stmt (tree t)
>       recorded during statement expressions.  */
>    if (!building_stmt_list_p ())
>      push_stmt_list ();
> +
> +  if (vis_parser)
> +    vis_parser->check_stmt (t);
> +
>    append_to_statement_list_force (t, &cur_stmt_list);
>  
>    return t;
> diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
> index f5e2ac2c..47186cb 100644
> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c
> @@ -82,6 +82,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "omp-low.h"
>  #include "builtins.h"
>  #include "gomp-constants.h"
> +#include "c-family/visual-parser.h"
>  
>  
>  /* Initialization routine for this file.  */
> @@ -242,7 +243,6 @@ typedef struct GTY(()) c_parser {
>    vec <c_token, va_gc> *cilk_simd_fn_tokens;
>  } c_parser;
>  
> -
>  /* The actual parser and external interface.  ??? Does this need to be
>     garbage-collected?  */
>  
> @@ -262,6 +262,9 @@ c_lex_one_token (c_parser *parser, c_token *token)
>    token->keyword = RID_MAX;
>    token->pragma_kind = PRAGMA_NONE;
>  
> +  if (vis_parser)
> +    vis_parser->on_token (token->location);
> +
>    switch (token->type)
>      {
>      case CPP_NAME:
> @@ -5168,11 +5171,14 @@ c_parser_paren_condition (c_parser *parser)
>  /* Parse a statement which is a block in C99.  */
>  
>  static tree
> -c_parser_c99_block_statement (c_parser *parser)
> +c_parser_c99_block_statement (c_parser *parser, location_t guard_loc,
> +                           const char *guard_kind)
>  {
>    tree block = c_begin_compound_stmt (flag_isoc99);
>    location_t loc = c_parser_peek_token (parser)->location;
>    c_parser_statement (parser);
> +  if (vis_parser)
> +    vis_parser->on_solo_stmt (block, guard_loc, guard_kind);
>    return c_end_compound_stmt (loc, block, flag_isoc99);
>  }
>  
> @@ -5185,7 +5191,7 @@ c_parser_c99_block_statement (c_parser *parser)
>     parser->in_if_block.  */
>  
>  static tree
> -c_parser_if_body (c_parser *parser, bool *if_p)
> +c_parser_if_body (c_parser *parser, bool *if_p, location_t if_loc)
>  {
>    tree block = c_begin_compound_stmt (flag_isoc99);
>    location_t body_loc = c_parser_peek_token (parser)->location;
> @@ -5203,7 +5209,11 @@ c_parser_if_body (c_parser *parser, bool *if_p)
>    else if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
>      add_stmt (c_parser_compound_statement (parser));
>    else
> -    c_parser_statement_after_labels (parser);
> +    {
> +      c_parser_statement_after_labels (parser);
> +      if (vis_parser)
> +     vis_parser->on_solo_stmt (block, if_loc, "if");
> +    }
>    return c_end_compound_stmt (body_loc, block, flag_isoc99);
>  }
>  
> @@ -5212,7 +5222,7 @@ c_parser_if_body (c_parser *parser, bool *if_p)
>     specially for the sake of -Wempty-body warnings.  */
>  
>  static tree
> -c_parser_else_body (c_parser *parser)
> +c_parser_else_body (c_parser *parser, location_t else_tok_loc)
>  {
>    location_t else_loc = c_parser_peek_token (parser)->location;
>    tree block = c_begin_compound_stmt (flag_isoc99);
> @@ -5227,7 +5237,12 @@ c_parser_else_body (c_parser *parser)
>        c_parser_consume_token (parser);
>      }
>    else
> -    c_parser_statement_after_labels (parser);
> +    {
> +      c_parser_statement_after_labels (parser);
> +      if (vis_parser)
> +     vis_parser->on_solo_stmt (block, else_tok_loc, "else");
> +    }
> +
>    return c_end_compound_stmt (else_loc, block, flag_isoc99);
>  }
>  
> @@ -5242,7 +5257,7 @@ static void
>  c_parser_if_statement (c_parser *parser)
>  {
>    tree block;
> -  location_t loc;
> +  location_t if_loc, cond_loc;
>    tree cond;
>    bool first_if = false;
>    tree first_body, second_body;
> @@ -5250,28 +5265,30 @@ c_parser_if_statement (c_parser *parser)
>    tree if_stmt;
>  
>    gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
> +  if_loc = c_parser_peek_token (parser)->location;
>    c_parser_consume_token (parser);
>    block = c_begin_compound_stmt (flag_isoc99);
> -  loc = c_parser_peek_token (parser)->location;
> +  cond_loc = c_parser_peek_token (parser)->location;
>    cond = c_parser_paren_condition (parser);
>    if (flag_cilkplus && contains_cilk_spawn_stmt (cond))
>      {
> -      error_at (loc, "if statement cannot contain %<Cilk_spawn%>");
> +      error_at (cond_loc, "if statement cannot contain %<Cilk_spawn%>");
>        cond = error_mark_node;
>      }
>    in_if_block = parser->in_if_block;
>    parser->in_if_block = true;
> -  first_body = c_parser_if_body (parser, &first_if);
> +  first_body = c_parser_if_body (parser, &first_if, if_loc);
>    parser->in_if_block = in_if_block;
>    if (c_parser_next_token_is_keyword (parser, RID_ELSE))
>      {
> +      location_t else_tok_loc = c_parser_peek_token (parser)->location;
>        c_parser_consume_token (parser);
> -      second_body = c_parser_else_body (parser);
> +      second_body = c_parser_else_body (parser, else_tok_loc);
>      }
>    else
>      second_body = NULL_TREE;
> -  c_finish_if_stmt (loc, cond, first_body, second_body, first_if);
> -  if_stmt = c_end_compound_stmt (loc, block, flag_isoc99);
> +  c_finish_if_stmt (cond_loc, cond, first_body, second_body, first_if);
> +  if_stmt = c_end_compound_stmt (cond_loc, block, flag_isoc99);
>  
>    /* If the if statement contains array notations, then we expand them.  */
>    if (flag_cilkplus && contains_array_notation_expr (if_stmt))
> @@ -5321,7 +5338,7 @@ c_parser_switch_statement (c_parser *parser)
>    c_start_case (switch_loc, switch_cond_loc, expr, explicit_cast_p);
>    save_break = c_break_label;
>    c_break_label = NULL_TREE;
> -  body = c_parser_c99_block_statement (parser);
> +  body = c_parser_c99_block_statement (parser, switch_loc, "switch");
>    c_finish_case (body, ce.original_type);
>    if (c_break_label)
>      {
> @@ -5346,6 +5363,7 @@ c_parser_while_statement (c_parser *parser, bool ivdep)
>    tree block, cond, body, save_break, save_cont;
>    location_t loc;
>    gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE));
> +  location_t while_tok_loc = c_parser_peek_token (parser)->location;
>    c_parser_consume_token (parser);
>    block = c_begin_compound_stmt (flag_isoc99);
>    loc = c_parser_peek_token (parser)->location;
> @@ -5362,7 +5380,7 @@ c_parser_while_statement (c_parser *parser, bool ivdep)
>    c_break_label = NULL_TREE;
>    save_cont = c_cont_label;
>    c_cont_label = NULL_TREE;
> -  body = c_parser_c99_block_statement (parser);
> +  body = c_parser_c99_block_statement (parser, while_tok_loc, "while");
>    c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true);
>    add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
>    c_break_label = save_break;
> @@ -5381,6 +5399,7 @@ c_parser_do_statement (c_parser *parser, bool ivdep)
>    tree block, cond, body, save_break, save_cont, new_break, new_cont;
>    location_t loc;
>    gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
> +  location_t do_tok_loc = c_parser_peek_token (parser)->location;
>    c_parser_consume_token (parser);
>    if (c_parser_next_token_is (parser, CPP_SEMICOLON))
>      warning_at (c_parser_peek_token (parser)->location,
> @@ -5392,7 +5411,7 @@ c_parser_do_statement (c_parser *parser, bool ivdep)
>    c_break_label = NULL_TREE;
>    save_cont = c_cont_label;
>    c_cont_label = NULL_TREE;
> -  body = c_parser_c99_block_statement (parser);
> +  body = c_parser_c99_block_statement (parser, do_tok_loc, "do");
>    c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>");
>    new_break = c_break_label;
>    c_break_label = save_break;
> @@ -5640,7 +5659,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep)
>    c_break_label = NULL_TREE;
>    save_cont = c_cont_label;
>    c_cont_label = NULL_TREE;
> -  body = c_parser_c99_block_statement (parser);
> +  body = c_parser_c99_block_statement (parser, for_loc, "for");
>    if (is_foreach_statement)
>      objc_finish_foreach_loop (loc, object_expression, collection_expression, 
> body, c_break_label, c_cont_label);
>    else
> @@ -13000,7 +13019,7 @@ c_parser_omp_for_loop (location_t loc, c_parser 
> *parser, enum tree_code code,
>        add_stmt (c_end_compound_stmt (here, stmt, true));
>      }
>    else
> -    add_stmt (c_parser_c99_block_statement (parser));
> +    add_stmt (c_parser_c99_block_statement (parser, loc, "omp for"));
>    if (c_cont_label)
>      {
>        tree t = build1 (LABEL_EXPR, void_type_node, c_cont_label);
> @@ -15403,6 +15422,9 @@ c_parse_file (void)
>    tparser.tokens = &tparser.tokens_buf[0];
>    the_parser = &tparser;
>  
> +  if (warn_misleading_indentation)
> +    vis_parser = new visual_parser ();
> +
>    if (c_parser_peek_token (&tparser)->pragma_kind == 
> PRAGMA_GCC_PCH_PREPROCESS)
>      c_parser_pragma_pch_preprocess (&tparser);
>  
> @@ -15416,6 +15438,10 @@ c_parse_file (void)
>      using_eh_for_cleanups ();
>  
>    c_parser_translation_unit (the_parser);
> +
> +  delete vis_parser;
> +  vis_parser = NULL;
> +
>    the_parser = NULL;
>  }
>  
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 4ea2ca2..d6c8b89 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "type-utils.h"
>  #include "omp-low.h"
>  #include "gomp-constants.h"
> +#include "c-family/visual-parser.h"
>  
>  
>  /* The lexer.  */
> @@ -651,6 +652,11 @@ cp_lexer_new_main (void)
>  
>    lexer = cp_lexer_alloc ();
>  
> +  if (warn_misleading_indentation)
> +    vis_parser = new visual_parser ();
> +
> +  /* FIXME: delete this.  */
> +
>    /* Put the first token in the buffer.  */
>    lexer->buffer->quick_push (token);
>  
> @@ -1058,6 +1064,9 @@ cp_lexer_consume_token (cp_lexer* lexer)
>    gcc_assert (token != &eof_token);
>    gcc_assert (!lexer->in_pragma || token->type != CPP_PRAGMA_EOL);
>  
> +  if (vis_parser)
> +    vis_parser->on_token (lexer->next_token->location);
> +
>    do
>      {
>        lexer->next_token++;
> @@ -2065,9 +2074,9 @@ static void cp_parser_declaration_statement
>    (cp_parser *);
>  
>  static tree cp_parser_implicitly_scoped_statement
> -  (cp_parser *, bool *);
> +  (cp_parser *, bool *, location_t, const char *);
>  static void cp_parser_already_scoped_statement
> -  (cp_parser *);
> +  (cp_parser *, location_t, const char *);
>  
>  /* Declarations [gram.dcl.dcl] */
>  
> @@ -10174,7 +10183,8 @@ cp_parser_selection_statement (cp_parser* parser, 
> bool *if_p)
>               nested_if = false;
>             }
>           else
> -           cp_parser_implicitly_scoped_statement (parser, &nested_if);
> +           cp_parser_implicitly_scoped_statement (parser, &nested_if,
> +                                                  token->location, "if");
>           parser->in_statement = in_statement;
>  
>           finish_then_clause (statement);
> @@ -10184,7 +10194,8 @@ cp_parser_selection_statement (cp_parser* parser, 
> bool *if_p)
>                                               RID_ELSE))
>             {
>               /* Consume the `else' keyword.  */
> -             cp_lexer_consume_token (parser->lexer);
> +             location_t else_tok_loc
> +               = cp_lexer_consume_token (parser->lexer)->location;
>               begin_else_clause (statement);
>               /* Parse the else-clause.  */
>               if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
> @@ -10198,7 +10209,8 @@ cp_parser_selection_statement (cp_parser* parser, 
> bool *if_p)
>                   cp_lexer_consume_token (parser->lexer);
>                 }
>               else
> -               cp_parser_implicitly_scoped_statement (parser, NULL);
> +               cp_parser_implicitly_scoped_statement (parser, NULL,
> +                                                      else_tok_loc, "else");
>  
>               finish_else_clause (statement);
>  
> @@ -10238,7 +10250,8 @@ cp_parser_selection_statement (cp_parser* parser, 
> bool *if_p)
>           in_statement = parser->in_statement;
>           parser->in_switch_statement_p = true;
>           parser->in_statement |= IN_SWITCH_STMT;
> -         cp_parser_implicitly_scoped_statement (parser, NULL);
> +         cp_parser_implicitly_scoped_statement (parser, NULL,
> +                                                0, "switch");
>           parser->in_switch_statement_p = in_switch_statement_p;
>           parser->in_statement = in_statement;
>  
> @@ -10783,6 +10796,7 @@ static tree
>  cp_parser_iteration_statement (cp_parser* parser, bool ivdep)
>  {
>    cp_token *token;
> +  location_t tok_loc;
>    enum rid keyword;
>    tree statement;
>    unsigned char in_statement;
> @@ -10792,6 +10806,8 @@ cp_parser_iteration_statement (cp_parser* parser, 
> bool ivdep)
>    if (!token)
>      return error_mark_node;
>  
> +  tok_loc = token->location;
> +
>    /* Remember whether or not we are already within an iteration
>       statement.  */
>    in_statement = parser->in_statement;
> @@ -10815,7 +10831,7 @@ cp_parser_iteration_statement (cp_parser* parser, 
> bool ivdep)
>       cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
>       /* Parse the dependent statement.  */
>       parser->in_statement = IN_ITERATION_STMT;
> -     cp_parser_already_scoped_statement (parser);
> +     cp_parser_already_scoped_statement (parser, tok_loc, "while");
>       parser->in_statement = in_statement;
>       /* We're done with the while-statement.  */
>       finish_while_stmt (statement);
> @@ -10830,7 +10846,7 @@ cp_parser_iteration_statement (cp_parser* parser, 
> bool ivdep)
>       statement = begin_do_stmt ();
>       /* Parse the body of the do-statement.  */
>       parser->in_statement = IN_ITERATION_STMT;
> -     cp_parser_implicitly_scoped_statement (parser, NULL);
> +     cp_parser_implicitly_scoped_statement (parser, NULL, 0, "do");
>       parser->in_statement = in_statement;
>       finish_do_body (statement);
>       /* Look for the `while' keyword.  */
> @@ -10860,7 +10876,7 @@ cp_parser_iteration_statement (cp_parser* parser, 
> bool ivdep)
>  
>       /* Parse the body of the for-statement.  */
>       parser->in_statement = IN_ITERATION_STMT;
> -     cp_parser_already_scoped_statement (parser);
> +     cp_parser_already_scoped_statement (parser, tok_loc, "for");
>       parser->in_statement = in_statement;
>  
>       /* We're done with the for-statement.  */
> @@ -11129,7 +11145,9 @@ cp_parser_declaration_statement (cp_parser* parser)
>     Returns the new statement.  */
>  
>  static tree
> -cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p)
> +cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p,
> +                                    location_t guard_loc,
> +                                    const char *guard_kind)
>  {
>    tree statement;
>  
> @@ -11155,6 +11173,9 @@ cp_parser_implicitly_scoped_statement (cp_parser* 
> parser, bool *if_p)
>        cp_parser_statement (parser, NULL_TREE, false, if_p);
>        /* Finish the dummy compound-statement.  */
>        finish_compound_stmt (statement);
> +      /* FIXME.  */
> +      if (vis_parser)
> +     vis_parser->on_solo_stmt (cur_stmt_list, guard_loc, guard_kind);
>      }
>  
>    /* Return the statement.  */
> @@ -11167,11 +11188,17 @@ cp_parser_implicitly_scoped_statement (cp_parser* 
> parser, bool *if_p)
>     scope.  */
>  
>  static void
> -cp_parser_already_scoped_statement (cp_parser* parser)
> +cp_parser_already_scoped_statement (cp_parser* parser, location_t guard_loc,
> +                                 const char *guard_kind)
>  {
>    /* If the token is a `{', then we must take special action.  */
>    if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
> -    cp_parser_statement (parser, NULL_TREE, false, NULL);
> +    {
> +      cp_parser_statement (parser, NULL_TREE, false, NULL);
> +      if (vis_parser)
> +     vis_parser->on_solo_stmt (cur_stmt_list, guard_loc,
> +                               guard_kind);
> +    }
>    else
>      {
>        /* Avoid calling cp_parser_compound_statement, so that we
> diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
> index 0fc08b5f..2eab2ca 100644
> --- a/gcc/cp/semantics.c
> +++ b/gcc/cp/semantics.c
> @@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "builtins.h"
>  #include "convert.h"
>  #include "gomp-constants.h"
> +#include "c-family/visual-parser.h"
>  
>  /* There routines provide a modular interface to perform many parsing
>     operations.  They may therefore be used during actual parsing, or
> @@ -400,6 +401,9 @@ add_stmt (tree t)
>    if (code == LABEL_EXPR || code == CASE_LABEL_EXPR)
>      STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1;
>  
> +  if (vis_parser)
> +    vis_parser->check_stmt (t);
> +
>    /* Add T to the statement-tree.  Non-side-effect statements need to be
>       recorded during statement expressions.  */
>    gcc_checking_assert (!stmt_list_stack->is_empty ());
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-1.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-1.c
> new file mode 100644
> index 0000000..dc0deb1
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-1.c
> @@ -0,0 +1,12 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +
> +int
> +foo (int flag)
> +{
> +  int x = 4, y = 5;
> +  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
> +    x = 3;
> +    y = 2; /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +  return x * y;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-10.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-10.c
> new file mode 100644
> index 0000000..8417e02
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-10.c
> @@ -0,0 +1,15 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +extern void bar (int);
> +
> +void foo (int flag)
> +{
> +  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
> +    if (flag / 2)
> +      {
> +        bar (0);
> +        bar (1);
> +      }
> +    bar (2); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +  bar (3);
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-11.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-11.c
> new file mode 100644
> index 0000000..dfcefe2
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-11.c
> @@ -0,0 +1,12 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +int test (int flagA, int flagB, int flagC)
> +{
> +  if (flagA)
> +    if (flagB)
> +      if (flagC) /* { dg-message "7: ...this 'if' clause, but it is not" } */
> +        foo ();
> +        bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-12.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-12.c
> new file mode 100644
> index 0000000..5c01d3e
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-12.c
> @@ -0,0 +1,12 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +int test (int flagA, int flagB, int flagC)
> +{
> +  if (flagA)
> +    if (flagB) /* { dg-message "5: ...this 'if' clause, but it is not" } */
> +      if (flagC)
> +        foo ();
> +      bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-13.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-13.c
> new file mode 100644
> index 0000000..198395f
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-13.c
> @@ -0,0 +1,12 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +int test (int flagA, int flagB, int flagC)
> +{
> +  if (flagA) /* { dg-message "3: ...this 'if' clause, but it is not" } */
> +    if (flagB)
> +      if (flagC)
> +        foo ();
> +    bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-14.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-14.c
> new file mode 100644
> index 0000000..87a9040
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-14.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +#define FOR_EACH(VAR, START, STOP) \
> +  for ((VAR) = (START); (VAR) < (STOP); (VAR++)) /* { dg-message "3: ...this 
> 'for' clause, but it is not" } */
> +
> +int test (void)
> +{
> +  int i;
> +  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
> +    foo ();
> +    bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-15.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-15.c
> new file mode 100644
> index 0000000..d8e2815
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-15.c
> @@ -0,0 +1,13 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +#define FOR_EACH(VAR, START, STOP) for ((VAR) = (START); (VAR) < (STOP); 
> (VAR++)) /* { dg-message "36: ...this 'for' clause, but it is not" } */
> +
> +int test (void)
> +{
> +  int i;
> +  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
> +    foo ();
> +    bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-16-tabs.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-16-tabs.c
> new file mode 100644
> index 0000000..f224f9e
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-16-tabs.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++)
> +    while (flagA)
> +      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
> +     foo ();
> +     bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-16.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-16.c
> new file mode 100644
> index 0000000..e8e63ca
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-16.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++)
> +    while (flagA)
> +      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
> +        foo ();
> +        bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-17-tabs.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-17-tabs.c
> new file mode 100644
> index 0000000..db25945f
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-17-tabs.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it 
> is not" } */
> +    while (flagA)
> +      if (flagB)
> +     foo ();
> +    bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-17.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-17.c
> new file mode 100644
> index 0000000..61c3890
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-17.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it 
> is not" } */
> +    while (flagA)
> +      if (flagB)
> +        foo ();
> +    bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-18-tabs.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-18-tabs.c
> new file mode 100644
> index 0000000..12d9443
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-18-tabs.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++)
> +    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" 
> } */
> +      if (flagB)
> +     foo ();
> +      bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-18.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-18.c
> new file mode 100644
> index 0000000..b42dbd6
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-18.c
> @@ -0,0 +1,16 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern void foo (void);
> +extern void bar (void);
> +
> +extern int flagA;
> +extern int flagB;
> +
> +int test (void)
> +{
> +  int i;
> +  for (i = 0; i < 10; i++)
> +    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" 
> } */
> +      if (flagB)
> +        foo ();
> +      bar (); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
> new file mode 100644
> index 0000000..4aeb4e6
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
> @@ -0,0 +1,11 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +
> +int
> +foo (int flag, int x, int y)
> +{
> +  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
> +    x++; y++; /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +
> +  return x * y;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-3.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-3.c
> new file mode 100644
> index 0000000..e37080d
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-3.c
> @@ -0,0 +1,14 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +
> +int
> +foo (int flag)
> +{
> +  int x = 4, y = 5;
> +  if (flag)
> +    x = 3;
> +  else /* { dg-message "3: ...this 'else' clause, but it is not" } */
> +    x = 2;
> +    y = 2; /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +  return x * y;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-4.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-4.c
> new file mode 100644
> index 0000000..0c72782
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-4.c
> @@ -0,0 +1,11 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +
> +void
> +foo (double *a, double *b, double *c)
> +{
> +  int i = 0;
> +  while (i < 10) /* { dg-message "3: ...this 'while' clause, but it is not" 
> } */
> +    a[i] = b[i] * c[i];
> +    i++; /* { dg-warning "statement is indented as if it were guarded by..." 
> } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-5.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-5.c
> new file mode 100644
> index 0000000..7537e8f
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-5.c
> @@ -0,0 +1,11 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +
> +void
> +foo (double *a, double *b, double *sum, double *prod)
> +{
> +  int i = 0;
> +  for (i = 0; i < 10; i++) /* { dg-output "3: ...this 'for' clause, but it 
> is not" } */
> +    sum[i] = a[i] * b[i];
> +    prod[i] = a[i] * b[i]; /* { dg-warning "statement is indented as if it 
> were guarded by..." } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-6.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-6.c
> new file mode 100644
> index 0000000..fc19537
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-6.c
> @@ -0,0 +1,22 @@
> +/* Based on CVE-2014-1266 aka "goto fail" */
> +/* { dg-options "-Wmisleading-indentation" } */
> +extern int foo (int);
> +
> +static int
> +goto_fail(int a, int b, int c)
> +{
> +     int err;
> +
> +     /* ... */
> +     if ((err = foo (a)) != 0)
> +             goto fail;
> +     if ((err = foo (b)) != 0) /* { dg-message "2: ...this 'if' clause, but 
> it is not" } */
> +             goto fail;
> +             goto fail; /* { dg-warning "statement is indented as if it were 
> guarded by..." } */
> +     if ((err = foo (c)) != 0)
> +             goto fail;
> +     /* ... */
> +
> +fail:
> +     return err;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-7.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-7.c
> new file mode 100644
> index 0000000..500884a
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-7.c
> @@ -0,0 +1,15 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +
> +extern int bar (int, int);
> +
> +int foo (int p, int q, int r, int s, int t)
> +{
> +  if (bar (p, q))
> +    {
> +      if (p) /* { dg-message "7: ...this 'if' clause, but it is not" } */
> +        q++; r++; /* { dg-warning "statement is indented as if it were 
> guarded by..." } */
> +        s++; /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +      t++;
> +    }
> +  return p + q + r + s + t;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-8.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-8.c
> new file mode 100644
> index 0000000..82c1e6b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-8.c
> @@ -0,0 +1,6 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +int foo (int a, int b, int c)
> +{
> +  /* This should *not* be flagged as misleading indentation.  */
> +  if (a) return b; else return c;
> +}
> diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-9.c 
> b/gcc/testsuite/c-c++-common/Wmisleading-indentation-9.c
> new file mode 100644
> index 0000000..3fcba34
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-9.c
> @@ -0,0 +1,10 @@
> +/* { dg-options "-Wmisleading-indentation" } */
> +/* { dg-do compile } */
> +extern void bar (int);
> +
> +void foo (int flag)
> +{
> +  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
> +    bar (0);
> +    bar (1); /* { dg-warning "statement is indented as if it were guarded 
> by..." } */
> +}

Reply via email to