This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation


On Thu, 2015-04-16 at 10:26 -0700, Mike Stump wrote:
> On Apr 16, 2015, at 8:01 AM, David Malcolm <dmalcolm@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.

(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 <dmalcolm@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 <dmalcolm@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..." } */
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]