This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
- From: David Malcolm <dmalcolm at redhat dot com>
- To: Mike Stump <mikestump at comcast dot net>
- Cc: "gcc-patches at gcc dot gnu dot org" <gcc-patches at gcc dot gnu dot org>
- Date: Tue, 21 Apr 2015 12:07:00 -0400
- Subject: Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
- Authentication-results: sourceware.org; auth=none
- References: <1429196485 dot 32584 dot 46 dot camel at surprise> <6A97A5CB-3000-4030-9E5C-BE55AE79F164 at comcast dot net>
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..." } */
+}