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