public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC stage 1] Proposed new warning: -Wmisleading-indentation
@ 2015-04-16 15:08 David Malcolm
  2015-04-16 17:27 ` Mike Stump
                   ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: David Malcolm @ 2015-04-16 15:08 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4508 bytes --]

Attached is a work-in-progress patch for a new
  -Wmisleading-indentation
warning I've been experimenting with, for GCC 6.

It kind-of-works, but there are some issues, so I wanted to get feedback
on it here.

The idea is to issue a warning when the C/C++ compiler's view of the
block structure doesn't match that of a naive human reader of the code
via indentation.

For example, CVE-2014-1266 (aka "goto fail") had:

  hashOut.data = hashes + SSL_MD5_DIGEST_LEN;
  hashOut.length = SSL_SHA1_DIGEST_LEN;
  if ((err = SSLFreeBuffer(&hashCtx)) != 0)
      goto fail;
  if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
      goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)
      goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
      goto fail;
  if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
      goto fail;
      goto fail;  /* MISTAKE! THIS LINE SHOULD NOT BE HERE */
  if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
      goto fail;

  err = sslRawVerify(...);

where the second "goto fail;" here:

  if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
      goto fail;
      goto fail;  /* MISTAKE! THIS LINE SHOULD NOT BE HERE */

is indented as if it's guarded by the conditional.  It isn't, making the
real meaning of the code hard to discern, and masking a security
vulnerability (where the stray goto led to the  certificate-checking
code later in the function being dead code, skipping it with err == 0,
for success).

The patch successfully issues a warning on a simplified version of this:

	if ((err = foo (b)) != 0)
		goto fail;
		goto fail;

./Wmisleading-indentation-6.c: In function ‘foo’:
./Wmisleading-indentation-6.c:15:3: warning: statement is indented as if
it were guarded by... [-Wmisleading-indentation]
   goto fail;
   ^
./Wmisleading-indentation-6.c:13:2: note: ...this clause, but it is not
  if ((err = foo (b)) != 0)
  ^

and on various other testcases.

That said, there are several major issues with the patch as it stands:

(A) works with the C FE, but not with C++ yet.  The implementation
assumes a single lexer consuming the token stream, so it doesn't yet
work with the C++ FE's ideas of tentatively-consumed tokens
(save/commit/rollback), and with its lexer stack.  I *think* that can be
fixed by relying on the fact that the integer locations of the token
stream are normally monotonic, and using that to capture and handle
those two C++ FE impl details.

(B) GTY/PCH/etc, and performance;  I know that it leaks visual_block
instances, and there's probably a way to do this with much less dynamic
memory allocation, but I wanted to get it right before making it fast.

(C) no ChangeLog yet, though it's heavily commented.

(D) tabs vs spaces.  This is probably the biggest can of worms.

Consider:

{
  if (flagA)
    if (flagB)
      if (flagC)
        foo ();
        bar (); /* would like to warn about this */
}

If this is indented using spaces&tabs with 8-space tabs, we have:

<no indent>{
<2 spaces>if (flagA)
<4 spaces>if (flagB)
<6 spaces>if (flagC)
<1 tab>foo ();
<1 tab>bar ();
<no indent>}

What does this look like in an editor?  Consider emacs, which uses uses
0-based offsets to what I call "visual columns".  The column numbers for
the start of the first token for each line are reported by emacs as:

<0>{
<2>if (flagA)
<4>if (flagB)
<6>if (flagC)
<8>foo ();
<8>bar ();
<0>}

However within libcpp and gcc, in linemap's expanded_location and in
diagnostic messages, the "column" numbers are actually 1-based counts of
*characters*, so the "column" numbers emitted in diagnostics for the
start of the first token in each line are actually:

<1>{
<3>if (flagA)
<5>if (flagB)
<7>if (flagC)
<2>foo ();
<2>bar ();
<1>}

Note the transition at tab offsets, where we go from increasing visual
column numbers:

<6>if (flagC)
<8>foo ();

to a decrease in the character count:

<7>if (flagC)
<2>foo ();

Hence to handle mixed spaces+tabs, we need some way to model "visual
columns".  I think this is doable (at the cost of adding a field to
cpp_token, and to c_token/cp_token), provided we can assume, per-buffer,
either
  (i) a consistent value for tabs in terms of spaces, or
  (ii) that we don't have mixed tabs/spaces in this buffer

An issue here is how to determine (i), or if it's OK to default to 8 and
have a command-line option (param?) to override it? (though what about,
say, each header file?)

Thoughts on this, and on the patch?

Thanks

Dave


[-- Attachment #2: misleading-indentation-v1.patch --]
[-- Type: text/x-patch, Size: 35383 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 30a717e..f71a33d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1462,6 +1462,7 @@ OBJS = \
 	var-tracking.o \
 	varasm.o \
 	varpool.o \
+	visual-parser.o \
 	vmsdbgout.o \
 	vtable-verify.o \
 	web.o \
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7d0a2cd..3795087 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -494,6 +494,10 @@ Wmain
 LangEnabledBy(C ObjC C++ ObjC++,Wpedantic, 2, 0)
 ;
 
+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/c-decl.c b/gcc/c/c-decl.c
index 2c41bf2..c4858ba 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -63,6 +63,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "c-family/c-ada-spec.h"
 #include "cilk.h"
+#include "visual-parser.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
@@ -560,6 +561,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 d0d35c5..9304e31 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "plugin.h"
 #include "omp-low.h"
+#include "visual-parser.h"
 
 \f
 /* Initialization routine for this file.  */
@@ -216,7 +217,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?  */
 
@@ -236,6 +236,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:
@@ -5042,11 +5045,13 @@ 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)
 {
   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);
   return c_end_compound_stmt (loc, block, flag_isoc99);
 }
 
@@ -5059,7 +5064,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;
@@ -5081,7 +5086,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);
+    }
   return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
@@ -5090,7 +5099,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);
@@ -5109,7 +5118,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);
+    }
+
   return c_end_compound_stmt (else_loc, block, flag_isoc99);
 }
 
@@ -5124,7 +5138,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;
@@ -5132,23 +5146,25 @@ 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);
   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))
@@ -5195,7 +5211,7 @@ c_parser_switch_statement (c_parser *parser)
   c_start_case (switch_loc, switch_cond_loc, expr);
   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);
   c_finish_case (body);
   if (c_break_label)
     {
@@ -5220,6 +5236,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;
@@ -5239,7 +5256,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);
   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;
@@ -5258,6 +5275,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,
@@ -5269,7 +5287,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);
   c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>");
   new_break = c_break_label;
   c_break_label = save_break;
@@ -5519,7 +5537,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);
   if (is_foreach_statement)
     objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
   else
@@ -11785,7 +11803,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));
   if (c_cont_label)
     {
       tree t = build1 (LABEL_EXPR, void_type_node, c_cont_label);
@@ -14011,6 +14029,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);
 
@@ -14024,6 +14045,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 46e2453..ffbc04b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "parser.h"
 #include "type-utils.h"
 #include "omp-low.h"
+#include "visual-parser.h"
 
 \f
 /* The lexer.  */
@@ -632,6 +633,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);
 
@@ -1030,6 +1036,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++;
@@ -2009,9 +2018,9 @@ static void cp_parser_declaration_statement
   (cp_parser *);
 
 static tree cp_parser_implicitly_scoped_statement
-  (cp_parser *, bool *);
+  (cp_parser *, bool *, location_t);
 static void cp_parser_already_scoped_statement
-  (cp_parser *);
+  (cp_parser *, location_t);
 
 /* Declarations [gram.dcl.dcl] */
 
@@ -9843,7 +9852,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);
 	    parser->in_statement = in_statement;
 
 	    finish_then_clause (statement);
@@ -9853,7 +9863,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))
@@ -9867,7 +9878,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);
 
 		finish_else_clause (statement);
 
@@ -9907,7 +9919,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);
 	    parser->in_switch_statement_p = in_switch_statement_p;
 	    parser->in_statement = in_statement;
 
@@ -10451,6 +10464,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;
@@ -10460,6 +10474,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;
@@ -10483,7 +10499,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);
 	parser->in_statement = in_statement;
 	/* We're done with the while-statement.  */
 	finish_while_stmt (statement);
@@ -10498,7 +10514,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);
 	parser->in_statement = in_statement;
 	finish_do_body (statement);
 	/* Look for the `while' keyword.  */
@@ -10528,7 +10544,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);
 	parser->in_statement = in_statement;
 
 	/* We're done with the for-statement.  */
@@ -10773,7 +10789,8 @@ 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)
 {
   tree statement;
 
@@ -10799,6 +10816,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);
     }
 
   /* Return the statement.  */
@@ -10811,11 +10831,15 @@ 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)
 {
   /* 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);
+    }
   else
     {
       /* Avoid calling cp_parser_compound_statement, so that we
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 886fbb8..0d05674 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "bitmap.h"
 #include "omp-low.h"
+#include "visual-parser.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -386,6 +387,9 @@ add_stmt (tree t)
       STMT_IS_FULL_EXPR_P (t) = stmts_are_full_exprs_p ();
     }
 
+  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..630ebfe
--- /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 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..1531662
--- /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 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-2.c b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
new file mode 100644
index 0000000..a22cd11
--- /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 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..7048225
--- /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 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..7abf379
--- /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 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..cb6d025
--- /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 one, 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..3133d15
--- /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 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..8d1ff00
--- /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 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..e490dec
--- /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 clause, but it is not" } */
+    bar (0);
+    bar (1); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
diff --git a/gcc/visual-parser.c b/gcc/visual-parser.c
new file mode 100644
index 0000000..6181e1e
--- /dev/null
+++ b/gcc/visual-parser.c
@@ -0,0 +1,255 @@
+/* "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 "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 "cgraph.h"
+#include "plugin.h"
+#include "omp-low.h"
+#include "visual-parser.h"
+#include "diagnostic-core.h"
+#include "tree-iterator.h"
+
+/* 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;
+}
+
+/* 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");
+
+  expanded_location xloc = expand_location (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);
+	      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 ())
+		{
+		  m_block_stack.pop ();
+		  on_outdent (loc);
+		  if (get_stack_top ()->get_column () >= xloc.column)
+		    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);
+}
+
+/* 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)
+{
+  /* Mark the most-indented visual block as containing
+     a solo-stmt.  */
+  get_stack_top ()->on_solo_stmt (block, guard_loc);
+}
+
+/* See comment above for visual_parser::on_solo_stmt.
+   Mark the most-indented visual block as (supposedly) only
+   containing a solo statement.  */
+
+void
+visual_block::on_solo_stmt (tree block, location_t guard_loc)
+{
+  tree_stmt_iterator tsi = tsi_start (block);
+  tree stmt = tsi_stmt (tsi);
+
+  m_loc_solo_stmt = EXPR_LOCATION (stmt);
+  m_loc_guard = guard_loc;
+
+  m_exploc_solo_stmt
+    = expand_location_to_spelling_point (m_loc_solo_stmt);
+  m_exploc_guard
+    = expand_location_to_spelling_point (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)
+	{
+	  //inform (m_loc_stmt, "solo statement here");
+	  //inform (m_loc_guard, "visually guarded here");
+	  m_has_solo_stmt = true;
+	}
+}
+
+/* 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_to_spelling_point (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 clause, but it is not");
+    }
+}
diff --git a/gcc/visual-parser.h b/gcc/visual-parser.h
new file mode 100644
index 0000000..295f457
--- /dev/null
+++ b/gcc/visual-parser.h
@@ -0,0 +1,167 @@
+/* "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);
+  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;
+  }
+
+ 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 block, location_t guard_loc);
+  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;
+  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;

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-16 15:08 [RFC stage 1] Proposed new warning: -Wmisleading-indentation David Malcolm
@ 2015-04-16 17:27 ` Mike Stump
  2015-04-21 16:13   ` David Malcolm
  2015-04-16 18:29 ` [RFC stage 1] Proposed new warning: -Wmisleading-indentation Manuel López-Ibáñez
  2015-04-17 14:42 ` Tom Tromey
  2 siblings, 1 reply; 26+ messages in thread
From: Mike Stump @ 2015-04-16 17:27 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

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.

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-16 15:08 [RFC stage 1] Proposed new warning: -Wmisleading-indentation David Malcolm
  2015-04-16 17:27 ` Mike Stump
@ 2015-04-16 18:29 ` Manuel López-Ibáñez
  2015-04-17 14:42 ` Tom Tromey
  2 siblings, 0 replies; 26+ messages in thread
From: Manuel López-Ibáñez @ 2015-04-16 18:29 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 16/04/15 17:01, David Malcolm wrote:
> Attached is a work-in-progress patch for a new
>    -Wmisleading-indentation
> warning I've been experimenting with, for GCC 6.

It sounds very cool...

> (D) tabs vs spaces.  This is probably the biggest can of worms.

I would suggest to be very conservative when warning. In case of doubt, do not 
warn.

> An issue here is how to determine (i), or if it's OK to default to 8 and
> have a command-line option (param?) to override it? (though what about,
> say, each header file?)

In case of a potential warning, you could detect if there is a mix of tab and 
spaces for the lines affected that may make the warning bogus, and not warn in 
that case. That is,

<6 spaces>if (flagC)
<1 tab><space>foo ();
<1 tab><space>bar ();

warns but

<6 spaces>if (flagC)
<8 spaces>foo ();
<2   tabs>bar ();

does not because you don't know how many spaces those 2 tabs take.

Also, in order to detect whether something is a tab or a space, why not simply 
re-open and seek? The input file is probably already in memory. See how 
diagnostic_show_locus in diagnostics.c gets lines from given locations.

Not even Emacs, which is a GNU package, can get tabs vs. spaces right and 
compile-goto-error goes to the wrong column. That seems a can of worms not 
worth opening.


+
+   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.

I don't understand why you need more than three, one for the conditional 
statement, another for the solo statement and another for the next-statement. 
I also don't get why you need to pass the whole block and new_stmt to 
visual_block, don't the locations returned by EXPR_LOCATION suffice?

Given that, the C++ tentative parsing should not be a problem since there is no 
tentative parsing (or there should not be) of if/else/while, and you only care 
about the first token when parsing the other two statements.

@@ -236,6 +236,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);
+

Why you need to save every location? Why not just do this when the parser 
detects an if/else/while? Then,

@@ -5081,7 +5086,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);
+    }
    return c_end_compound_stmt (body_loc, block, flag_isoc99);
  }

You can simply here peek the location of the next token before and after 
c_parser_statement_after_labels (parser). Then warn if they are equal or if 
they are on the same line, no? You may need to skip pragmas.


+   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:

Aren't these bugs (or missing features) in the FE locations? I know that the 
location of the assignment is at '=' because we don't have (yet) locations for 
constants or variables (all binary expressions should have 3 locations!).

In any case, don't you have the location of the token that starts the 
statements? This is all you need as far as I understand.

And why the 'if' points to the open paren? One can easily find the open-paren 
location if needed by seeking in the input buffer.


+	  /* FIXME: what about entirely empty lines???
+	     Presumably they simply don't get tokens.  */

Empty lines no, but preprocessor directives do.

Cheers,

Manuel.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-16 15:08 [RFC stage 1] Proposed new warning: -Wmisleading-indentation David Malcolm
  2015-04-16 17:27 ` Mike Stump
  2015-04-16 18:29 ` [RFC stage 1] Proposed new warning: -Wmisleading-indentation Manuel López-Ibáñez
@ 2015-04-17 14:42 ` Tom Tromey
  2015-04-17 16:12   ` Manuel López-Ibáñez
  2 siblings, 1 reply; 26+ messages in thread
From: Tom Tromey @ 2015-04-17 14:42 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

>>>>> "Dave" == David Malcolm <dmalcolm@redhat.com> writes:

Dave> However within libcpp and gcc, in linemap's expanded_location and in
Dave> diagnostic messages, the "column" numbers are actually 1-based counts of
Dave> *characters*, so the "column" numbers emitted in diagnostics for the
Dave> start of the first token in each line are actually:

FWIW this is actually in violation of the GNU coding standards.  There's
a bug open for it.  However, I was always afraid to change this in cpp,
since presumably it would break existing programs that read gcc's
output.  It's a bad situation because the standard can't be changed,
either, as other programs (e.g., bison and I think Emacs) do follow it
faithfully.

Dave>   (i) a consistent value for tabs in terms of spaces, or

There's already -ftabstop for this, but it isn't per-file.

Tom

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-17 14:42 ` Tom Tromey
@ 2015-04-17 16:12   ` Manuel López-Ibáñez
  2015-04-28 23:13     ` Joseph Myers
  0 siblings, 1 reply; 26+ messages in thread
From: Manuel López-Ibáñez @ 2015-04-17 16:12 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches, tom

On 17/04/15 16:42, Tom Tromey wrote:
>>>>>> "Dave" == David Malcolm <dmalcolm@redhat.com> writes:
>
> Dave> However within libcpp and gcc, in linemap's expanded_location and in
> Dave> diagnostic messages, the "column" numbers are actually 1-based counts of
> Dave> *characters*, so the "column" numbers emitted in diagnostics for the
> Dave> start of the first token in each line are actually:
>
> FWIW this is actually in violation of the GNU coding standards.  There's
> a bug open for it.  However, I was always afraid to change this in cpp,
> since presumably it would break existing programs that read gcc's
> output.  It's a bad situation because the standard can't be changed,
> either, as other programs (e.g., bison and I think Emacs) do follow it
> faithfully.

Which programs rely on precise column numbers given by GCC? They cannot be very 
old neither unused to pain.

This "bug" actually breaks going to an error location in Emacs's compilation 
mode, which is annoying when compiling GCC's code.

> Dave>   (i) a consistent value for tabs in terms of spaces, or
>
> There's already -ftabstop for this, but it isn't per-file.

And it doesn't actually do anything: https://gcc.gnu.org/PR52899

Perhaps it should be fixed to do what was intended and influence how cpp 
transforms tabs as columns?

Also, there is https://gcc.gnu.org/PR49973, which means that non-ascii 
characters may mess up "visual" indentation.

Cheers,

Manuel.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-16 17:27 ` Mike Stump
@ 2015-04-21 16:13   ` David Malcolm
  2015-04-21 16:40     ` Trevor Saunders
                       ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: David Malcolm @ 2015-04-21 16:13 UTC (permalink / raw)
  To: Mike Stump; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 3531 bytes --]

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

[-- Attachment #2: misleading-indentation-v2.patch --]
[-- Type: text/x-patch, Size: 46627 bytes --]

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"
 
 \f
 /* 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"
 
 \f
 /* 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..." } */
+}

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-21 16:13   ` David Malcolm
@ 2015-04-21 16:40     ` Trevor Saunders
  2015-04-21 16:43     ` Manuel López-Ibáñez
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 26+ messages in thread
From: Trevor Saunders @ 2015-04-21 16:40 UTC (permalink / raw)
  To: David Malcolm; +Cc: Mike Stump, gcc-patches

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 <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.

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 <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"
>  
>  \f
>  /* 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"
>  
>  \f
>  /* 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..." } */
> +}

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-21 16:13   ` David Malcolm
  2015-04-21 16:40     ` Trevor Saunders
@ 2015-04-21 16:43     ` Manuel López-Ibáñez
  2015-04-21 17:05     ` Mike Stump
  2015-04-21 18:14     ` Manuel López-Ibáñez
  3 siblings, 0 replies; 26+ messages in thread
From: Manuel López-Ibáñez @ 2015-04-21 16:43 UTC (permalink / raw)
  To: David Malcolm, Mike Stump; +Cc: gcc-patches

On 21/04/15 18:07, David Malcolm wrote:
> On Thu, 2015-04-16 at 10:26 -0700, Mike Stump wrote:
>> 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.

GCC already warns for the above:

test.c:3:9: warning: suggest braces around empty body in an ‘if’ statement 
[-Wempty-body]
    if (a);
          ^

Cheers,

	Manuel.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-21 16:13   ` David Malcolm
  2015-04-21 16:40     ` Trevor Saunders
  2015-04-21 16:43     ` Manuel López-Ibáñez
@ 2015-04-21 17:05     ` Mike Stump
  2015-04-21 18:14     ` Manuel López-Ibáñez
  3 siblings, 0 replies; 26+ messages in thread
From: Mike Stump @ 2015-04-21 17:05 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Apr 21, 2015, at 9:07 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> 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.

Ok.  I don’t have any problem with that.  Going for the high value only makes the problem space smaller, more likely to implement and do a good job and avoids false positives and all sorts of what ifs that the other class would expose you to.

I like your work and your plan.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-21 16:13   ` David Malcolm
                       ` (2 preceding siblings ...)
  2015-04-21 17:05     ` Mike Stump
@ 2015-04-21 18:14     ` Manuel López-Ibáñez
  2015-04-21 23:35       ` David Malcolm
  2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
  3 siblings, 2 replies; 26+ messages in thread
From: Manuel López-Ibáñez @ 2015-04-21 18:14 UTC (permalink / raw)
  To: David Malcolm, Mike Stump; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1157 bytes --]

On 21/04/15 18:07, David Malcolm wrote:
>
> 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 still do not understand why you need so much complexity as I explained here: 
https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00830.html

The attached patch passes all your tests except Wmisleading-indentation-3.c, 
which warns only once instead of two times (it doesn't seem a big loss to me), 
and Wmisleading-indentation-7.c which I did not bother to implement but it is 
straightforward application of the if-case to the else-case.

Perhaps I'm missing something that is not reflected in your tests?

BTW, the start-up cost of GCC is not negligible, thus grouping similar 
testcases in a single file may pay off in the long term. Many small files also 
tend to slow down VC tools. It also makes harder to see what is tested and what 
is missing.

Cheers,

Manuel.

[-- Attachment #2: misleading-indentation.diff --]
[-- Type: text/x-patch, Size: 5615 bytes --]

Index: c/c-parser.c
===================================================================
--- c/c-parser.c	(revision 222087)
+++ c/c-parser.c	(working copy)
@@ -5174,20 +5174,34 @@ c_parser_c99_block_statement (c_parser *
   location_t loc = c_parser_peek_token (parser)->location;
   c_parser_statement (parser);
   return c_end_compound_stmt (loc, block, flag_isoc99);
 }
 
+static void
+warn_for_misleading_indentation (location_t guard_loc, location_t body_loc, location_t next_stmt_loc,
+			     const char *s)
+{
+  if (LOCATION_FILE (next_stmt_loc) == LOCATION_FILE (body_loc)
+      && (LOCATION_LINE (next_stmt_loc) == LOCATION_LINE (body_loc)
+	  || (LOCATION_LINE (next_stmt_loc) > LOCATION_LINE (body_loc)
+	      && LOCATION_COLUMN (next_stmt_loc) == LOCATION_COLUMN (body_loc))))
+    if (warning_at (next_stmt_loc, OPT_Wmisleading_indentation,
+		    "statement is indented as if it were guarded by..."))
+      inform (guard_loc,
+	      "...this %qs clause, but it is not", s);
+}
+
 /* Parse the body of an if statement.  This is just parsing a
    statement but (a) it is a block in C99, (b) we track whether the
    body is an if statement for the sake of -Wparentheses warnings, (c)
    we handle an empty body specially for the sake of -Wempty-body
    warnings, and (d) we call parser_compound_statement directly
    because c_parser_statement_after_labels resets
    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;
   c_parser_all_labels (parser);
   *if_p = c_parser_next_token_is_keyword (parser, RID_IF);
@@ -5201,11 +5215,16 @@ c_parser_if_body (c_parser *parser, bool
 		    "suggest braces around empty body in an %<if%> statement");
     }
   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 (!c_parser_next_token_is_keyword (parser, RID_ELSE))
+	warn_for_misleading_indentation (if_loc, body_loc, c_parser_peek_token (parser)->location, "if");
+    }
+
   return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
 /* Parse the else body of an if statement.  This is just parsing a
    statement but (a) it is a block in C99, (b) we handle an empty body
@@ -5248,10 +5267,11 @@ c_parser_if_statement (c_parser *parser)
   tree first_body, second_body;
   bool in_if_block;
   tree if_stmt;
 
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
+  location_t 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 = c_parser_paren_condition (parser);
   if (flag_cilkplus && contains_cilk_spawn_stmt (cond))
@@ -5259,11 +5279,11 @@ c_parser_if_statement (c_parser *parser)
       error_at (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))
     {
       c_parser_consume_token (parser);
       second_body = c_parser_else_body (parser);
@@ -5344,10 +5364,11 @@ static void
 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_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 = c_parser_paren_condition (parser);
   if (check_no_cilk (cond,
@@ -5360,11 +5381,17 @@ c_parser_while_statement (c_parser *pars
 		   annot_expr_ivdep_kind));
   save_break = c_break_label;
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (while_loc, body_loc, c_parser_peek_token (parser)->location, "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;
   c_cont_label = save_cont;
 }
@@ -5638,11 +5665,17 @@ c_parser_for_statement (c_parser *parser
     }
   save_break = c_break_label;
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (for_loc, body_loc, c_parser_peek_token (parser)->location, "for");
+
   if (is_foreach_statement)
     objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
   else
     c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
   add_stmt (c_end_compound_stmt (loc, block, flag_isoc99 || c_dialect_objc ()));

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-21 18:14     ` Manuel López-Ibáñez
@ 2015-04-21 23:35       ` David Malcolm
  2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
  1 sibling, 0 replies; 26+ messages in thread
From: David Malcolm @ 2015-04-21 23:35 UTC (permalink / raw)
  To: Manuel López-Ibáñez; +Cc: Mike Stump, gcc-patches

On Tue, 2015-04-21 at 20:14 +0200, Manuel López-Ibáñez wrote:
> On 21/04/15 18:07, David Malcolm wrote:
> >
> > 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 still do not understand why you need so much complexity as I explained here: 
> https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00830.html
> 
> The attached patch passes all your tests except Wmisleading-indentation-3.c, 
> which warns only once instead of two times (it doesn't seem a big loss to me), 
> and Wmisleading-indentation-7.c which I did not bother to implement but it is 
> straightforward application of the if-case to the else-case.

Aha!   Thanks.  Your approach is much simpler, and likely much faster.

> Perhaps I'm missing something that is not reflected in your tests?

No, mostly just my lack of expertise on the frontend :)

> BTW, the start-up cost of GCC is not negligible, thus grouping similar 
> testcases in a single file may pay off in the long term. Many small files also 
> tend to slow down VC tools. It also makes harder to see what is tested and what 
> is missing.

OK.  I'll finish up your version of the patch, and consolidate the
testcases.

Thanks
Dave

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC stage 1] Proposed new warning: -Wmisleading-indentation
  2015-04-17 16:12   ` Manuel López-Ibáñez
@ 2015-04-28 23:13     ` Joseph Myers
  0 siblings, 0 replies; 26+ messages in thread
From: Joseph Myers @ 2015-04-28 23:13 UTC (permalink / raw)
  To: Manuel López-Ibáñez; +Cc: David Malcolm, gcc-patches, tom

[-- Attachment #1: Type: text/plain, Size: 1204 bytes --]

On Fri, 17 Apr 2015, Manuel López-Ibáñez wrote:

> On 17/04/15 16:42, Tom Tromey wrote:
> > > > > > > "Dave" == David Malcolm <dmalcolm@redhat.com> writes:
> > 
> > Dave> However within libcpp and gcc, in linemap's expanded_location and in
> > Dave> diagnostic messages, the "column" numbers are actually 1-based counts
> > of
> > Dave> *characters*, so the "column" numbers emitted in diagnostics for the
> > Dave> start of the first token in each line are actually:
> > 
> > FWIW this is actually in violation of the GNU coding standards.  There's
> > a bug open for it.  However, I was always afraid to change this in cpp,
> > since presumably it would break existing programs that read gcc's
> > output.  It's a bad situation because the standard can't be changed,
> > either, as other programs (e.g., bison and I think Emacs) do follow it
> > faithfully.
> 
> Which programs rely on precise column numbers given by GCC? They cannot be
> very old neither unused to pain.

I suspect quite a few GCC testcases for column numbers (and hardcoding the 
expected column numbers) will need updating if this bug is fixed, but I 
agree it should be fixed.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/3] Implement -Wmisleading-indentation (v4)
  2015-04-21 18:14     ` Manuel López-Ibáñez
  2015-04-21 23:35       ` David Malcolm
@ 2015-04-28 23:50       ` David Malcolm
  2015-04-28 23:52         ` [PATCH 2/3] Fix spurious semicolons David Malcolm
                           ` (2 more replies)
  1 sibling, 3 replies; 26+ messages in thread
From: David Malcolm @ 2015-04-28 23:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This is an updated implementation of the proposed
-Wmisleading-indentation warning.

Changes since last patch:

* I've rewritten it to use Manuel's approach from
    https://gcc.gnu.org/ml/gcc-patches/2015-04/msg01225.html
  I took Manuel's proposed patch for the C frontend and generalized
  it to also work on the C++ frontend.  I moved the logic into a new
  file: c-family/c-indentation.c.

* I've consolidated the testcases into 3 files.

* I've added the option to -Wall, and added enough smarts to the
  code so that it can bootstrap our own source tree without any false
  positives.  See the testcases for examples of what I ran into.

Successfully bootstrapped&regrtested the combination of this and
the following two patches on x86_64-unknown-linux-gnu (Fedora 20),
with:
  43 new "PASS" results in gcc.sum
  129 new "PASS" results in g++.sum
for the new test cases (relative to a control build of r222248).

How does this look?

The only remaining known wart here is in the new testcase
c-c++-common/Wmisleading-indentation-2.c, which uses a #line
directive to refer to a .md file.  Ideally it ought to be able to
actually locate the .md file during compilation, since that case is
trickier to handle than a simple file-not-found.  Hence in theory we
could have some logic in a .exp to handle copying up the .md from the
srcdir to the testdir.  That said, the behavior is outwardly
invisible in both cases: a false positive is not emitted.

gcc/ChangeLog:
	* Makefile.in (C_COMMON_OBJS): Add c-family/c-indentation.o.

gcc/c-family/ChangeLog:
	* c-common.h (warn_for_misleading_indentation): New prototype.
	* c-indentation.c: New file.
	* c.opt (Wmisleading-indentation): New option.

gcc/c/ChangeLog:
	* c-parser.c (c_parser_if_body): Add param "if_loc", use it
	to add a call to warn_for_misleading_indentation.
	(c_parser_else_body): Likewise, adding param "else_loc".
	(c_parser_if_statement): Check for misleading indentation.
	(c_parser_while_statement): Likewise.
	(c_parser_for_statement): Likewise.

gcc/cp/ChangeLog:
	* parser.c (cp_parser_selection_statement): Add location and
	guard_kind arguments to calls to
	cp_parser_implicitly_scoped_statement.
	(cp_parser_iteration_statement): Likewise for calls to
	cp_parser_already_scoped_statement.
	(cp_parser_implicitly_scoped_statement): Add "guard_loc" and
	"guard_kind" params; use them to warn for misleading
	indentation.
	(cp_parser_already_scoped_statement): Likewise.

gcc/ChangeLog:
	* doc/invoke.texi (Warning Options): Add -Wmisleading-indentation.
	(-Wall): Add -Wmisleading-indentation.
	(-Wmisleading-indentation): New option.

gcc/testsuite/ChangeLog:
	* c-c++-common/Wmisleading-indentation.c: New testcase.
	* c-c++-common/Wmisleading-indentation-2.c: New testcase.
	* c-c++-common/Wmisleading-indentation-2.md: New file.

libcpp/ChangeLog:
	* directives.c (do_line): Set seen_line_directive on line_table.
	(do_linemarker): Likewise.
	* include/line-map.h (struct line_maps): Add new field
	"seen_line_directive".
---
 gcc/Makefile.in                                    |   4 +-
 gcc/c-family/c-common.h                            |   8 +
 gcc/c-family/c-indentation.c                       | 384 ++++++++++++++++++
 gcc/c-family/c.opt                                 |   4 +
 gcc/c/c-parser.c                                   |  51 ++-
 gcc/cp/parser.c                                    |  51 ++-
 gcc/doc/invoke.texi                                |  40 ++
 .../c-c++-common/Wmisleading-indentation-2.c       |  56 +++
 .../c-c++-common/Wmisleading-indentation-2.md      |  46 +++
 .../c-c++-common/Wmisleading-indentation.c         | 431 +++++++++++++++++++++
 libcpp/directives.c                                |   6 +-
 libcpp/include/line-map.h                          |   3 +
 12 files changed, 1060 insertions(+), 24 deletions(-)
 create mode 100644 gcc/c-family/c-indentation.c
 create mode 100644 gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
 create mode 100644 gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md
 create mode 100644 gcc/testsuite/c-c++-common/Wmisleading-indentation.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 80c91f0..69564fc 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1138,8 +1138,8 @@ c-family-warn = $(STRICT_WARN)
 
 # Language-specific object files shared by all C-family front ends.
 C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \
-  c-family/c-format.o c-family/c-gimplify.o c-family/c-lex.o \
-  c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o \
+  c-family/c-format.o c-family/c-gimplify.o c-family/c-indentation.o \
+  c-family/c-lex.o c-family/c-omp.o c-family/c-opts.o c-family/c-pch.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 \
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index cabf452..2ac5ff2 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1437,4 +1437,12 @@ extern bool contains_cilk_spawn_stmt (tree);
 extern tree cilk_for_number_of_iterations (tree);
 extern bool check_no_cilk (tree, const char *, const char *,
 		           location_t loc = UNKNOWN_LOCATION);
+/* In c-indentation.c.  */
+extern void
+warn_for_misleading_indentation (location_t guard_loc,
+				 location_t body_loc,
+				 location_t next_stmt_loc,
+				 enum cpp_ttype next_tok_type,
+				 const char *guard_kind);
+
 #endif /* ! GCC_C_COMMON_H */
diff --git a/gcc/c-family/c-indentation.c b/gcc/c-family/c-indentation.c
new file mode 100644
index 0000000..94565f6
--- /dev/null
+++ b/gcc/c-family/c-indentation.c
@@ -0,0 +1,384 @@
+/* Implementation of -Wmisleading-indentation
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+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"
+#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 "stor-layout.h"
+#include "input.h"
+#include "c-common.h"
+
+extern cpp_options *cpp_opts;
+
+/* Convert libcpp's notion of a column (a 1-based char count) to
+   the "visual column" (0-based column, respecting tabs), by reading the
+   relevant line.
+   Returns true if a conversion was possible, writing the result to OUT,
+   otherwise returns false.  */
+
+static bool
+get_visual_column (expanded_location exploc, unsigned int *out)
+{
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+  if (!line)
+    return false;
+  unsigned int vis_column = 0;
+  for (int i = 1; i < exploc.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++;
+    }
+
+  *out = vis_column;
+  return true;
+}
+
+/* Is the token at LOC the first non-whitespace on its line?
+   Helper function for should_warn_for_misleading_indentation.  */
+
+static bool
+is_first_nonwhitespace_on_line (expanded_location exploc)
+{
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+
+   /* If we can't determine it, return false so that we don't issue a
+      warning.  This is sometimes the case for input files
+      containing #line directives, and these are often for autogenerated
+      sources (e.g. from .md files), where it's not clear that it's
+      meaningful to look at indentation.  */
+  if (!line)
+    return false;
+
+  for (int i = 1; i < exploc.column; i++)
+    {
+      unsigned char ch = line[i - 1];
+      if (!ISSPACE (ch))
+	return false;
+    }
+  return true;
+}
+
+/* Does the given source line appear to contain a #if directive?
+   (or #ifdef/#ifndef).  Ignore the possibility of it being inside a
+   comment, for simplicity.
+   Helper function for detect_preprocessor_logic.  */
+
+static bool
+line_contains_hash_if (const char *file, int line_num)
+{
+  expanded_location exploc;
+  exploc.file = file;
+  exploc.line = line_num;
+  exploc.column = 1;
+
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+  if (!line)
+    return false;
+
+  int idx;
+
+  /* Skip leading whitespace.  */
+  for (idx = 0; idx < line_len; idx++)
+    if (!ISSPACE (line[idx]))
+      break;
+  if (idx == line_len)
+    return false;
+
+  /* Require a '#' character.  */
+  if (line[idx] != '#')
+    return false;
+  idx++;
+
+  /* Skip whitespace.  */
+  while (idx < line_len)
+    {
+      if (!ISSPACE (line[idx]))
+	break;
+      idx++;
+    }
+
+  /* Match #if/#ifdef/#ifndef.  */
+  if (idx + 2 <= line_len)
+    if (line[idx] == 'i')
+      if (line[idx + 1] == 'f')
+	return true;
+
+  return false;
+}
+
+
+/* Determine if there is preprocessor logic between
+   BODY_EXPLOC and NEXT_STMT_EXPLOC, to ensure that we don't
+   issue a warning for cases like this:
+
+	if (flagA)
+	  foo ();
+	  ^ BODY_EXPLOC
+      #if SOME_CONDITION_THAT_DOES_NOT_HOLD
+	if (flagB)
+      #endif
+	  bar ();
+	  ^ NEXT_STMT_EXPLOC
+
+   despite "bar ();" being visually aligned below "foo ();" and
+   being (as far as the parser sees) the next token.
+
+   Return true if such logic is detected.  */
+
+static bool
+detect_preprocessor_logic (expanded_location body_exploc,
+			   expanded_location next_stmt_exploc)
+{
+  gcc_assert (next_stmt_exploc.file == body_exploc.file);
+  gcc_assert (next_stmt_exploc.line > body_exploc.line);
+
+  if (next_stmt_exploc.line - body_exploc.line < 4)
+    return false;
+
+  /* Is there a #if/#ifdef/#ifndef directive somewhere in the lines
+     between the given locations?
+
+     This is something of a layering violation, but by necessity,
+     given the nature of what we're testing for.  For example,
+     in theory we could be fooled by a #if within a comment, but
+     it's unlikely to matter.  */
+  for (int line = body_exploc.line + 1; line < next_stmt_exploc.line; line++)
+    if (line_contains_hash_if (body_exploc.file, line))
+      return true;
+
+  /* Not found.  */
+  return false;
+}
+
+
+/* Helper function for warn_for_misleading_indentation; see
+   description of that function below.  */
+
+static bool
+should_warn_for_misleading_indentation (location_t guard_loc,
+					location_t body_loc,
+					location_t next_stmt_loc,
+					enum cpp_ttype next_tok_type)
+{
+  /* Don't attempt to compare the indentation of BODY_LOC and NEXT_STMT_LOC
+     if either are within macros.  */
+  if (linemap_location_from_macro_expansion_p (line_table, body_loc)
+      || linemap_location_from_macro_expansion_p (line_table, next_stmt_loc))
+    return false;
+
+  /* Don't attempt to compare indentation if #line or # 44 "file"-style
+     directives are present, suggesting generated code.
+
+     All bets are off if these are present: the file that the #line
+     directive could have an entirely different coding layout to C/C++
+     (e.g. .md files).
+
+     To determine if a #line is present, in theory we could look for a
+     map with reason == LC_RENAME_VERBATIM.  However, if there has
+     subsequently been a long line requiring a column number larger than
+     that representable by the original LC_RENAME_VERBATIM map, then
+     we'll have a map with reason LC_RENAME.
+     Rather than attempting to search all of the maps for a
+     LC_RENAME_VERBATIM, instead we have libcpp set a flag whenever one
+     is seen, and we check for the flag here.
+  */
+  if (line_table->seen_line_directive)
+    return false;
+
+  if (next_tok_type == CPP_CLOSE_BRACE)
+    return false;
+
+  /* Don't warn here about spurious semicolons.  */
+  if (next_tok_type == CPP_SEMICOLON)
+    return false;
+
+  expanded_location body_exploc
+    = expand_location_to_spelling_point (body_loc);
+  expanded_location next_stmt_exploc
+    = expand_location_to_spelling_point (next_stmt_loc);
+
+  /* They must be in the same file.  */
+  if (next_stmt_exploc.file != body_exploc.file)
+    return false;
+
+  /* If NEXT_STMT_LOC and BODY_LOC are on the same line, consider
+     the location of the guard.
+
+     Cases where we want to issue a warning:
+
+       if (flag)
+         foo ();  bar ();
+                  ^ WARN HERE
+
+       if (flag) foo (); bar ();
+                         ^ WARN HERE
+
+     Cases where we don't want to issue a warning:
+
+       various_code (); if (flag) foo (); bar (); more_code ();
+                                          ^ DON'T WARN HERE.  */
+  if (next_stmt_exploc.line == body_exploc.line)
+    {
+      expanded_location guard_exploc
+	= expand_location_to_spelling_point (guard_loc);
+      if (guard_exploc.file != body_exploc.file)
+	return true;
+      if (guard_exploc.line < body_exploc.line)
+	/* The guard is on a line before a line that contains both
+	   the body and the next stmt.  */
+	return true;
+      else if (guard_exploc.line == body_exploc.line)
+	{
+	  /* They're all on the same line.  */
+	  gcc_assert (guard_exploc.file == next_stmt_exploc.file);
+	  gcc_assert (guard_exploc.line == next_stmt_exploc.line);
+	  /* Heuristic: only warn if the guard is the first thing
+	     on its line.  */
+	  if (is_first_nonwhitespace_on_line (guard_exploc))
+	    return true;
+	}
+    }
+
+  /* If NEXT_STMT_LOC is on a line after BODY_LOC, consider
+     their relative locations, and of the guard.
+
+     Cases where we want to issue a warning:
+        if (flag)
+          foo ();
+          bar ();
+          ^ WARN HERE
+
+     Cases where we don't want to issue a warning:
+        if (flag)
+        foo ();
+        bar ();
+        ^ DON'T WARN HERE (autogenerated code?)
+
+	if (flagA)
+	  foo ();
+      #if SOME_CONDITION_THAT_DOES_NOT_HOLD
+	if (flagB)
+      #endif
+	  bar ();
+	  ^ DON'T WARN HERE
+  */
+  if (next_stmt_exploc.line > body_exploc.line)
+    {
+      /* Determine if GUARD_LOC and NEXT_STMT_LOC are aligned on the same
+	 "visual column"...  */
+      unsigned int next_stmt_vis_column;
+      unsigned int body_vis_column;
+      /* If we can't determine it, don't issue a warning.  This is sometimes
+	 the case for input files containing #line directives, and these
+	 are often for autogenerated sources (e.g. from .md files), where
+	 it's not clear that it's meaningful to look at indentation.  */
+      if (!get_visual_column (next_stmt_exploc, &next_stmt_vis_column))
+	return false;
+      if (!get_visual_column (body_exploc, &body_vis_column))
+	return false;
+      if (next_stmt_vis_column == body_vis_column)
+	{
+	  /* Don't warn if they aren't aligned on the same column
+	     as the guard itself (suggesting autogenerated code that
+	     doesn't bother indenting at all).  */
+	  expanded_location guard_exploc
+	    = expand_location_to_spelling_point (guard_loc);
+	  unsigned int guard_vis_column;
+	  if (!get_visual_column (guard_exploc, &guard_vis_column))
+	    return false;
+	  if (guard_vis_column == body_vis_column)
+	    return false;
+
+	  /* Don't warn if there is multiline preprocessor logic between
+	     the two statements. */
+	  if (detect_preprocessor_logic (body_exploc, next_stmt_exploc))
+	    return false;
+
+	  /* Otherwise, they are visually aligned: issue a warning.  */
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Called by the C/C++ frontends when we have a guarding statement at
+   GUARD_LOC containing a statement at BODY_LOC, where the block wasn't
+   written using braces, like this:
+
+     if (flag)
+       foo ();
+
+   along with the location of the next token, at NEXT_STMT_LOC,
+   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:
+
+     GUARD_LOC
+     |
+     V
+     if (flag)
+       foo (); <- BODY_LOC
+       bar (); <- NEXT_STMT_LOC
+
+   In the above, "bar ();" isn't guarded by the "if", but
+   is indented to misleadingly suggest that it is in the same
+   block as "foo ();".
+
+   GUARD_KIND identifies the kind of clause e.g. "if", "else" etc.  */
+
+void
+warn_for_misleading_indentation (location_t guard_loc,
+				 location_t body_loc,
+				 location_t next_stmt_loc,
+				 enum cpp_ttype next_tok_type,
+				 const char *guard_kind)
+{
+  if (should_warn_for_misleading_indentation (guard_loc,
+					      body_loc,
+					      next_stmt_loc,
+					      next_tok_type))
+    if (warning_at (next_stmt_loc, OPT_Wmisleading_indentation,
+		    "statement is indented as if it were guarded by..."))
+      inform (guard_loc,
+	      "...this %qs clause, but it is not", guard_kind);
+}
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 983f4a8..98d6445 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 LangEnabledBy(C C++,Wall)
+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/c-parser.c b/gcc/c/c-parser.c
index f5e2ac2c..5703535 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -5185,7 +5185,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 +5203,15 @@ 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 (!c_parser_next_token_is_keyword (parser, RID_ELSE))
+	warn_for_misleading_indentation (if_loc, body_loc,
+					 c_parser_peek_token (parser)->location,
+					 c_parser_peek_token (parser)->type,
+					 "if");
+    }
+
   return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
@@ -5212,9 +5220,9 @@ 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_loc)
 {
-  location_t else_loc = c_parser_peek_token (parser)->location;
+  location_t body_loc = c_parser_peek_token (parser)->location;
   tree block = c_begin_compound_stmt (flag_isoc99);
   c_parser_all_labels (parser);
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
@@ -5227,8 +5235,14 @@ c_parser_else_body (c_parser *parser)
       c_parser_consume_token (parser);
     }
   else
-    c_parser_statement_after_labels (parser);
-  return c_end_compound_stmt (else_loc, block, flag_isoc99);
+    {
+      c_parser_statement_after_labels (parser);
+      warn_for_misleading_indentation (else_loc, body_loc,
+				       c_parser_peek_token (parser)->location,
+				       c_parser_peek_token (parser)->type,
+				       "else");
+    }
+  return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
 /* Parse an if statement (C90 6.6.4, C99 6.8.4).
@@ -5250,6 +5264,7 @@ c_parser_if_statement (c_parser *parser)
   tree if_stmt;
 
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
+  location_t 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;
@@ -5261,12 +5276,13 @@ c_parser_if_statement (c_parser *parser)
     }
   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_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_loc);
     }
   else
     second_body = NULL_TREE;
@@ -5346,6 +5362,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_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 +5379,16 @@ c_parser_while_statement (c_parser *parser, bool ivdep)
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (while_loc, body_loc,
+				   c_parser_peek_token (parser)->location,
+				   c_parser_peek_token (parser)->type,
+				   "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;
@@ -5640,7 +5666,16 @@ c_parser_for_statement (c_parser *parser, bool ivdep)
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (for_loc, body_loc,
+				   c_parser_peek_token (parser)->location,
+				   c_parser_peek_token (parser)->type,
+				   "for");
+
   if (is_foreach_statement)
     objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
   else
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4ea2ca2..73f5da7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2065,9 +2065,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 +10174,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 +10185,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 +10200,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 +10241,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 +10787,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 +10797,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 +10822,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 +10837,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 +10867,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 +11136,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;
 
@@ -11152,9 +11161,18 @@ cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p)
       /* Create a compound-statement.  */
       statement = begin_compound_stmt (0);
       /* Parse the dependent-statement.  */
+      location_t body_loc = cp_lexer_peek_token (parser->lexer)->location;
       cp_parser_statement (parser, NULL_TREE, false, if_p);
       /* Finish the dummy compound-statement.  */
       finish_compound_stmt (statement);
+      cp_token *next_tok = cp_lexer_peek_token (parser->lexer);
+      if (next_tok->keyword != RID_ELSE)
+        {
+          location_t next_stmt_loc = next_tok->location;
+          warn_for_misleading_indentation (guard_loc, body_loc,
+                                           next_stmt_loc, next_tok->type,
+                                           guard_kind);
+        }
     }
 
   /* Return the statement.  */
@@ -11167,11 +11185,20 @@ 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);
+    {
+      location_t body_loc = cp_lexer_peek_token (parser->lexer)->location;
+      cp_parser_statement (parser, NULL_TREE, false, NULL);
+      cp_token *next_tok = cp_lexer_peek_token (parser->lexer);
+      location_t next_stmt_loc = next_tok->location;
+      warn_for_misleading_indentation (guard_loc, body_loc,
+                                       next_stmt_loc, next_tok->type,
+                                       guard_kind);
+    }
   else
     {
       /* Avoid calling cp_parser_compound_statement, so that we
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f5f79b8..e43631f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3433,6 +3433,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wformat   @gol
 -Wmain @r{(only for C/ObjC and unless} @option{-ffreestanding}@r{)}  @gol
 -Wmaybe-uninitialized @gol
+-Wmisleading-indentation @r{(only for C/ObjC)} @gol
 -Wmissing-braces @r{(only for C/ObjC)} @gol
 -Wnonnull  @gol
 -Wopenmp-simd @gol
@@ -3756,6 +3757,45 @@ arguments, two, or three arguments of appropriate types.  This warning
 is enabled by default in C++ and is enabled by either @option{-Wall}
 or @option{-Wpedantic}.
 
+@item -Wmisleading-indentation @r{(C and C++ only)}
+@opindex Wmisleading-indentation
+@opindex Wno-misleading-indentation
+Warn when the indentation of the code does not reflect the block structure.
+Specifically, a warning is issued for @code{if}, @code{else}, @code{while}, and
+@code{for} clauses with a guarded statement that does not use braces,
+followed by an unguarded statement with the same indentation.
+
+In the following example, the call to ``bar'' is misleadingly indented as
+if it were guarded by the ``if'' conditional.
+
+@smallexample
+  if (some_condition ())
+    foo ();
+    bar ();  /* Gotcha: this is not guarded by the "if".  */
+@end smallexample
+
+This warning is enabled by @option{-Wall} in C and C++.
+
+In the case of mixed tabs and spaces, the warning uses the
+@option{-ftabstop=} option to determine if the statements line up
+(defaulting to 8).
+
+The warning is not issued for code involving multiline preprocessor logic
+such as the following example.
+
+@smallexample
+  if (flagA)
+    foo (0);
+#if SOME_CONDITION_THAT_DOES_NOT_HOLD
+  if (flagB)
+#endif
+    foo (1);
+@end smallexample
+
+The warning is not issued after a @code{#line} directive, since this
+typically indicates autogenerated code, and no assumptions can be made
+about the layout of the file that the directive references.
+
 @item -Wmissing-braces
 @opindex Wmissing-braces
 @opindex Wno-missing-braces
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..b4ee700
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
@@ -0,0 +1,56 @@
+/* { dg-options "-Wmisleading-indentation" } */
+/* { dg-do compile } */
+
+/* Based on get_attr_athlon_decode from the generated insn-attrtab.c
+   for x86_64.
+   A #line directive, followed by a very long line to ensure that
+   we're in a fresh line_map.
+
+   This should not generate a misleading indentation warning.
+
+   This needs to be in its own file since -Wmisleading-indentation stops
+   after seeing a #line directive.  */
+void fn ()
+{
+  switch (0)
+    {
+#line 6 "../../../../src/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md"
+    case 0:
+      if (0)
+        {
+	  return;
+        }
+
+    case 1:
+      if (0)
+        {
+	  return;
+        }
+      else
+        {
+	  return;
+        }
+
+      /**********************************************************************************************************************************/
+      if (0)
+        {
+	  return;
+        }
+      else if (0)
+        {
+	  return;
+        }
+      else if (0)
+        {
+	  return;
+        }
+      else
+        {
+	  return;
+        }
+
+    default:
+      return;
+
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md
new file mode 100644
index 0000000..44bc13a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md
@@ -0,0 +1,46 @@
+;; Support file for testcase Wmisleading-indentation.c
+;; Adapted from gcc/config/i386/i386.md
+(define_attr "cpu" "none,pentium,pentiumpro,geode,k6,athlon,k8,core2,nehalem,
+		    atom,slm,generic,amdfam10,bdver1,bdver2,bdver3,bdver4,
+		    btver2,knl"
+  (const (symbol_ref "ix86_schedule")))
+
+;; A basic instruction type.  Refinements due to arguments to be
+;; provided in other attributes.
+(define_attr "type"
+  "other,multi,
+   alu,alu1,negnot,imov,imovx,lea,
+   incdec,ishift,ishiftx,ishift1,rotate,rotatex,rotate1,
+   imul,imulx,idiv,icmp,test,ibr,setcc,icmov,
+   push,pop,call,callv,leave,
+   str,bitmanip,
+   fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,
+   fxch,fistp,fisttp,frndint,
+   sse,ssemov,sseadd,sseadd1,sseiadd,sseiadd1,
+   ssemul,sseimul,ssediv,sselog,sselog1,
+   sseishft,sseishft1,ssecmp,ssecomi,
+   ssecvt,ssecvt1,sseicvt,sseins,
+   sseshuf,sseshuf1,ssemuladd,sse4arg,
+   lwp,mskmov,msklog,
+   mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft,
+   mpxmov,mpxmk,mpxchk,mpxld,mpxst"
+  (const_string "other"))
+
+;; Main data type used by the insn
+(define_attr "mode"
+  "unknown,none,QI,HI,SI,DI,TI,OI,XI,SF,DF,XF,TF,V16SF,V8SF,V4DF,V4SF,
+  V2DF,V2SF,V1DF,V8DF"
+  (const_string "unknown"))
+
+;; The CPU unit operations uses.
+(define_attr "unit" "integer,i387,sse,mmx,unknown"
+  (cond [(eq_attr "type" "fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,
+			  fxch,fistp,fisttp,frndint")
+	   (const_string "i387")
+	 (eq_attr "type" "sse,ssemov,sseadd,sseadd1,sseiadd,sseiadd1,
+			  ssemul,sseimul,ssediv,sselog,sselog1,
+			  sseishft,sseishft1,ssecmp,ssecomi,
+			  ssecvt,ssecvt1,sseicvt,sseins,
+			  sseshuf,sseshuf1,ssemuladd,sse4arg,mskmov")
+	   (const_string "sse")
+	 (const_string "integer")))
diff --git a/gcc/testsuite/c-c++-common/Wmisleading-indentation.c b/gcc/testsuite/c-c++-common/Wmisleading-indentation.c
new file mode 100644
index 0000000..3dbbb8b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wmisleading-indentation.c
@@ -0,0 +1,431 @@
+/* { dg-options "-Wmisleading-indentation -Wall" } */
+/* { dg-do compile } */
+
+extern int foo (int);
+extern int bar (int, int);
+extern int flagA;
+extern int flagB;
+extern int flagC;
+extern int flagD;
+
+int
+fn_1 (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;
+}
+
+int
+fn_2 (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;
+}
+
+int
+fn_3 (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;
+}
+
+void
+fn_4 (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..." } */
+}
+
+void
+fn_5 (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..." } */
+}
+
+/* Based on CVE-2014-1266 aka "goto fail" */
+int fn_6 (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;
+}
+
+int fn_7 (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..." } */
+      t++;
+    }
+  return p + q + r + s + t;
+}
+
+int fn_8 (int a, int b, int c)
+{
+  /* This should *not* be flagged as misleading indentation.  */
+  if (a) return b; else return c;
+}
+
+void fn_9 (int flag)
+{
+  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    foo (0);
+    foo (1); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_10 (int flag)
+{
+  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    if (flag / 2)
+      {
+        foo (0);
+        foo (1);
+      }
+    foo (2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+  foo (3);
+}
+
+void fn_11 (void)
+{
+  if (flagA)
+    if (flagB)
+      if (flagC) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+        foo (0);
+        bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_12 (void)
+{
+  if (flagA)
+    if (flagB) /* { dg-message "5: ...this 'if' clause, but it is not" } */
+      if (flagC)
+        foo (0);
+      bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_13 (void)
+{
+  if (flagA) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    if (flagB)
+      if (flagC)
+        foo (0);
+    bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+#define FOR_EACH(VAR, START, STOP) \
+  for ((VAR) = (START); (VAR) < (STOP); (VAR++)) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+
+void fn_14 (void)
+{
+  int i;
+  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
+    foo (i);
+    bar (i, i); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+#undef FOR_EACH
+
+#define FOR_EACH(VAR, START, STOP) for ((VAR) = (START); (VAR) < (STOP); (VAR++)) /* { dg-message "36: ...this 'for' clause, but it is not" } */
+void fn_15 (void)
+{
+  int i;
+  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
+    foo (i);
+    bar (i, i); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+#undef FOR_EACH
+
+void fn_16_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA)
+      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+        foo (0);
+        foo (1); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_16_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA)
+      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+	foo (0);
+	foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_17_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+    while (flagA)
+      if (flagB)
+        foo (0);
+    foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_17_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+    while (flagA)
+      if (flagB)
+	foo (0);
+    foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_18_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" } */
+      if (flagB)
+        foo (0);
+      foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_18_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" } */
+      if (flagB)
+	foo (0);
+      foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+/* This shouldn't lead to a warning.  */
+int fn_19 (void) { if (flagA) return 1; else return 0; }
+
+/* A deeply-nested mixture of spaces and tabs, adapted from
+   c-c++-common/pr60101.c.
+   This should not lead to a warning.  */
+void
+fn_20 (unsigned int l)
+{
+  unsigned int i;
+
+  for (i = 0; i < 10; i++)
+    {
+      unsigned int n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11;
+
+      for (n0 = 0; n0 < l; n0++)
+	for (n1 = 0; n1 < l; n1++)
+	  for (n2 = 0; n2 < l; n2++)
+	    for (n3 = 0; n3 < l; n3++)
+	      for (n4 = 0; n4 < l; n4++)
+		for (n5 = 0; n5 < l; n5++)
+		  for (n6 = 0; n6 < l; n6++)
+		    for (n7 = 0; n7 < l; n7++)
+		      for (n8 = 0; n8 < l; n8++)
+			for (n9 = 0; n9 < l; n9++)
+			  for (n10 = 0; n10 < l; n10++)
+			    for (n11 = 0; n11 < l; n11++)
+			      {
+				if (flagA)
+				  foo (0);
+				foo (1);
+			      }
+      foo (2);
+    }
+}
+
+/* Another nested mix of tabs and spaces that shouldn't lead to a warning,
+   with a preprocessor directive thrown in for good measure
+   (adapted from libgomp/loop.c: gomp_loop_init).  */
+void fn_21 (void)
+{
+  foo (0);
+  if (flagA)
+    {
+      foo (1);
+
+#if 1
+      {
+	foo (2);
+	if (flagB)
+	  {
+	    if (flagC)
+	      foo (3);
+	    else
+	      foo (4);
+	  }
+	else if (flagD)
+	  foo (5);
+	else
+	  foo (6);
+      }
+#endif
+    }
+}
+
+/* The conditionals within the following macros shouldn't be warned about.
+   Adapted from libgomp/driver.c: gomp_load_plugin_for_device.  */
+int fn_22 (void)
+{
+  int err = 0;
+
+#define DLSYM()							\
+  do									\
+    {									\
+      err = foo (0);							\
+      if (err)								\
+	goto out;							\
+    }									\
+  while (0)
+#define DLSYM_OPT()							\
+  do									\
+    {									\
+      err = foo (1);							\
+      if (err)								\
+        foo (2);							\
+      else								\
+        foo (3);							\
+      foo (4);								\
+    }									\
+  while (0)
+  DLSYM ();
+  DLSYM_OPT ();
+#undef DLSYM
+#undef DLSYM_OPT
+
+ out:
+  return err;
+}
+
+/* This shouldn't be warned about.  */
+void fn_23 (void) { foo (0); foo (1); if (flagA) foo (2); foo (3); foo (4); }
+
+/* Code that simply doesn't bother indenting anywhere (e.g. autogenerated
+   code) shouldn't be warned about.  */
+void fn_24 (void)
+{
+  foo (0);
+  if (flagA)
+  foo (1);
+  foo (2);
+}
+
+/* Adapted from libiberty/regex.c; an example of a conditional in a
+   macro where the successor statement begins with a macro arg:
+
+	    if (num < 0)
+	      num = 0;
+	    num = num * 10 + c - '0';
+	    ^ this successor statement
+
+   and hence "num" has a spelling location at the argument of the
+   macro usage site ("lower_bound"), we want the definition of the
+   parameter ("num") for the indentation comparison to be meaninful.
+
+   This should not generate a misleading indentation warning.  */
+
+# define GET_UNSIGNED_NUMBER(num) \
+  {									\
+    while (flagA)							\
+      {									\
+	if (flagB)						\
+	  {								\
+	    if (num < 0)						\
+	      num = 0;							\
+	    num = num * 10 + c - '0';					\
+	  }								\
+      }									\
+  }
+void fn_25 (int c, int lower_bound, int upper_bound)
+{
+  GET_UNSIGNED_NUMBER (lower_bound);
+}
+#undef GET_UNSIGNED_NUMBER
+
+/* Example adapted from libdecnumber/decNumber.c:decExpOp that shouldn't
+   trigger a warning.  */
+void fn_26 (void)
+{
+  if (flagA) {
+    if (flagB) foo (0); }
+  foo (1);
+}
+
+/* Ensure that we don't get confused by mixed tabs and spaces; the line
+   "foo (1);" has leading spaces before a tab, but this should not
+   lead to a warning from -Wmisleading-indentation.  */
+void fn_27 (void)
+{
+	      if (flagA)
+		foo (0);
+  	      foo (1);
+}
+
+/* Example adapted from gcc/cgraph.h:symtab_node::get_availability of
+   a spurious trailing semicolon that shouldn't generate a warning.  */
+void fn_28 (void)
+{
+  if (flagA)
+    foo (0);
+  else
+    foo (1);;
+}
+
+/* However, other kinds of spurious semicolons can be a problem.  Sadly
+   we don't yet report for the misleading-indented "foo (1);" in the
+   following, due to the spurious semicolon.  */
+void fn_29 (void)
+{
+  if (flagA)
+    if (flagB)
+      foo (0);;
+    foo (1);
+}
+
+/* Adapted from usage site of #ifdef HAVE_cc0.  This should not lead
+   to a warning from -Wmisleading-indentation.  */
+void fn_30 (void)
+{
+  if (flagA)
+    foo (0);
+#if SOME_CONDITION_THAT_DOES_NOT_HOLD
+  if (flagB)
+#endif
+    foo (1);
+}
+
+/* This shouldn't lead to a warning.  */
+void fn_31 (void)
+{
+  if (flagA)
+    foo (0);
+  else if (flagB)
+    foo (1);
+  else if (flagC)
+    foo (2);
+  else
+    foo (3);
+}
diff --git a/libcpp/directives.c b/libcpp/directives.c
index 2a824e6..356ec13 100644
--- a/libcpp/directives.c
+++ b/libcpp/directives.c
@@ -911,7 +911,7 @@ strtolinenum (const uchar *str, size_t len, linenum_type *nump, bool *wrapped)
 static void
 do_line (cpp_reader *pfile)
 {
-  const struct line_maps *line_table = pfile->line_table;
+  struct line_maps *line_table = pfile->line_table;
   const struct line_map *map = LINEMAPS_LAST_ORDINARY_MAP (line_table);
 
   /* skip_rest_of_line() may cause line table to be realloc()ed so note down
@@ -965,6 +965,7 @@ do_line (cpp_reader *pfile)
   skip_rest_of_line (pfile);
   _cpp_do_file_change (pfile, LC_RENAME_VERBATIM, new_file, new_lineno,
 		       map_sysp);
+  line_table->seen_line_directive = true;
 }
 
 /* Interpret the # 44 "file" [flags] notation, which has slightly
@@ -973,7 +974,7 @@ do_line (cpp_reader *pfile)
 static void
 do_linemarker (cpp_reader *pfile)
 {
-  const struct line_maps *line_table = pfile->line_table;
+  struct line_maps *line_table = pfile->line_table;
   const struct line_map *map = LINEMAPS_LAST_ORDINARY_MAP (line_table);
   const cpp_token *token;
   const char *new_file = ORDINARY_MAP_FILE_NAME (map);
@@ -1052,6 +1053,7 @@ do_linemarker (cpp_reader *pfile)
   pfile->line_table->highest_location--;
 
   _cpp_do_file_change (pfile, reason, new_file, new_lineno, new_sysp);
+  line_table->seen_line_directive = true;
 }
 
 /* Arrange the file_change callback.  pfile->line has changed to
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 403d798..d7aa525 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -319,6 +319,9 @@ struct GTY(()) line_maps {
   /* The special location value that is used as spelling location for
      built-in tokens.  */
   source_location builtin_location;
+
+  /* True if we've seen a #line or # 44 "file" directive.  */
+  bool seen_line_directive;
 };
 
 /* Returns the pointer to the memory region where information about
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/3] Fix spurious semicolons
  2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
@ 2015-04-28 23:52         ` David Malcolm
  2015-04-29  0:10           ` Jeff Law
  2015-04-28 23:53         ` [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation David Malcolm
  2015-05-11 21:23         ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) Jeff Law
  2 siblings, 1 reply; 26+ messages in thread
From: David Malcolm @ 2015-04-28 23:52 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

An earlier version of the -Wmisleading-indentation patch issued
warnings for spurious trailing semicolons guarded by a conditional.

Although the latest version of -Wmisleading-indentation no longer
warns about these, I fixed the ones that were reported, thusly.

gcc/ChangeLog:
	* builtins.c (fold_builtin_1): Remove spurious second
	semicolon.
	* cgraph.h (symtab_node::get_availability): Likewise.
	* opts.c (common_handle_option): Remove spurious second semicolon.
	* tree-ssa-loop-ivopts.c (extract_cond_operands): Likewise.
	* tree-ssa-loop-niter.c (derive_constant_upper_bound_ops): Likewise.

gcc/cp/ChangeLog:
	* cp-tree.h (ARGUMENT_PACK_SELECT_ARG): Remove spurious
	trailing semicolon.

gcc/fortran/ChangeLog:
	* options.c (gfc_init_options): Remove spurious second
	semicolon.
	* trans-stmt.c (gfc_trans_allocate): Likewise.
---
 gcc/builtins.c             | 2 +-
 gcc/cgraph.h               | 2 +-
 gcc/cp/cp-tree.h           | 2 +-
 gcc/fortran/options.c      | 2 +-
 gcc/fortran/trans-stmt.c   | 2 +-
 gcc/opts.c                 | 2 +-
 gcc/tree-ssa-loop-ivopts.c | 4 ++--
 gcc/tree-ssa-loop-niter.c  | 2 +-
 8 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 9263777..a05979a 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -9966,7 +9966,7 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
     CASE_FLT_FN (BUILT_IN_CREAL):
       if (validate_arg (arg0, COMPLEX_TYPE)
 	&& TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-	return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));;
+	return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
     break;
 
     CASE_FLT_FN (BUILT_IN_CIMAG):
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 29f65a8..108cc80 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -2992,7 +2992,7 @@ symtab_node::get_availability (void)
   if (is_a <cgraph_node *> (this))
     return dyn_cast <cgraph_node *> (this)->get_availability ();
   else
-    return dyn_cast <varpool_node *> (this)->get_availability ();;
+    return dyn_cast <varpool_node *> (this)->get_availability ();
 }
 
 /* Call calback on symtab node and aliases associated to this node.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 2a904a5..ed2b354 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3073,7 +3073,7 @@ extern void decl_shadowed_for_var_insert (tree, tree);
    ARGUMENT_PACK_SELECT represents. */
 #define ARGUMENT_PACK_SELECT_ARG(NODE)					\
   TREE_VEC_ELT (ARGUMENT_PACK_ARGS (ARGUMENT_PACK_SELECT_FROM_PACK (NODE)), \
-	        ARGUMENT_PACK_SELECT_INDEX (NODE));
+	        ARGUMENT_PACK_SELECT_INDEX (NODE))
 
 /* In a FUNCTION_DECL, the saved language-specific per-function data.  */
 #define DECL_SAVED_FUNCTION_DATA(NODE)			\
diff --git a/gcc/fortran/options.c b/gcc/fortran/options.c
index 1262ccc..5a35803 100644
--- a/gcc/fortran/options.c
+++ b/gcc/fortran/options.c
@@ -115,7 +115,7 @@ gfc_init_options (unsigned int decoded_options_count,
      enabled by default in Fortran.  Ideally, we should express this
      in .opt, but that is not supported yet.  */
   if (!global_options_set.x_cpp_warn_missing_include_dirs)
-    global_options.x_cpp_warn_missing_include_dirs = 1;;
+    global_options.x_cpp_warn_missing_include_dirs = 1;
 
   set_default_std_flags ();
 
diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c
index 91d2a85..64ea0b5 100644
--- a/gcc/fortran/trans-stmt.c
+++ b/gcc/fortran/trans-stmt.c
@@ -5478,7 +5478,7 @@ gfc_trans_allocate (gfc_code * code)
 	    memsz = TYPE_SIZE_UNIT (gfc_typenode_for_spec (&code->ext.alloc.ts));
 	  else
 	    /* Handle size computation of the type declared to alloc.  */
-	    memsz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (se.expr)));;
+	    memsz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (se.expr)));
 
 	  /* Allocate - for non-pointers with re-alloc checking.  */
 	  if (gfc_expr_attr (expr).allocatable)
diff --git a/gcc/opts.c b/gcc/opts.c
index 39c190d..fed5d12 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1386,7 +1386,7 @@ common_handle_option (struct gcc_options *opts,
 	unsigned int i;
 
 	if (lang_mask == CL_DRIVER)
-	  break;;
+	  break;
 
 	undoc_mask = ((opts->x_verbose_flag | opts->x_extra_warnings)
 		      ? 0
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index d71e3a4..44219d2 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -1481,9 +1481,9 @@ extract_cond_operands (struct ivopts_data *data, gimple stmt,
 
 end:
   if (control_var)
-    *control_var = op0;;
+    *control_var = op0;
   if (iv_var)
-    *iv_var = iv0;;
+    *iv_var = iv0;
   if (bound)
     *bound = op1;
   if (iv_bound)
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index fc63825..042f8df 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -2565,7 +2565,7 @@ derive_constant_upper_bound_ops (tree type, tree op0,
 	  cst = -cst;
 	  /* Avoid CST == 0x80000...  */
 	  if (wi::neg_p (cst))
-	    return max;;
+	    return max;
 
 	  /* OP0 + CST.  We need to check that
 	     BND <= MAX (type) - CST.  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation
  2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
  2015-04-28 23:52         ` [PATCH 2/3] Fix spurious semicolons David Malcolm
@ 2015-04-28 23:53         ` David Malcolm
  2015-04-29  0:25           ` Jeff Law
  2015-04-29 12:35           ` Mikael Morin
  2015-05-11 21:23         ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) Jeff Law
  2 siblings, 2 replies; 26+ messages in thread
From: David Malcolm @ 2015-04-28 23:53 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch fixes the places in our tree where -Wmisleading-indentation
correctly identifies code that's misleadingly indented.

In particular, a couple of places in
  gcc/cp/parser.c:cp_parser_asm_definition
appeared to be missing braces, both of the form:

  if (complicated_multiline_condition ())
    some_var_not_otherwise_set = some_value ();

    if (some_var_not_otherwise_set == error_mark_node)
       some_flag = true;

where the second "if" is misleadingly indented as if guarded by
the first.  Given the logic, it was clear that these ought to have
been so guarded, so I changed these to read:

 if (complicated_multiline_condition ())
   {
     some_var_not_otherwise_set = some_value ();

     if (some_var_not_otherwise_set == error_mark_node)
       some_flag = true;
   }

gcc/ChangeLog:
	* auto-profile.c (afdo_find_equiv_class): Fix indentation so
	that it reflects the block structure.
	(afdo_propagate_edge): Likewise.
	(afdo_calculate_branch_prob): Likewise.
	(afdo_annotate_cfg): Likewise.
	* cfgcleanup.c (equal_different_set_p): Likewise.
	(try_crossjump_to_edge): Likewise.
	* cgraph.c (cgraph_node::verify_node): Likewise.
	* cgraphunit.c (expand_all_functions): Likewise.
	* config/i386/i386.c (ix86_expand_copysign): Likewise.
	(exact_dependency_1): Likewise.
	* dwarf2asm.c (dw2_output_indirect_constants): Likewise.
	* dwarf2out.c (tree_add_const_value_attribute_for_decl): Likewise.
	* gensupport.c (process_define_subst): Likewise.
	* lto-wrapper.c (merge_and_complain): Likewise.
	* tree-if-conv.c (if_convertible_bb_p): Likewise.
	* tree-ssa-loop-prefetch.c (find_or_create_group): Likewise.
	* tree-ssa-tail-merge.c (gsi_advance_fw_nondebug_nonlocal): Likewise.
	* tree-vect-data-refs.c (vect_grouped_load_supported): Likewise.
	* tree-vect-loop.c (vectorizable_reduction): Likewise.
	* tree-vect-slp.c (vect_transform_slp_perm_load): Likewise.
	* tree-vect-stmts.c (vectorizable_shift): Likewise.
	* tree-vrp.c (vrp_finalize): Likewise.
	* tree.c (variably_modified_type_p): Likewise.

gcc/cp/ChangeLog:
	* parser.c (cp_parser_asm_definition): Only test for
        error_mark_node if "outputs" was just set.
	(cp_parser_asm_definition): Likewise for "inputs".

gcc/fortran/ChangeLog:
	* expr.c (check_inquiry): Fix indentation so that it reflects the
	block structure.
	* interface.c (compare_parameter): Likewise.
	* parse.c (parse_oacc_structured_block): Likewise.
	* target-memory.c (expr_to_char): Likewise.
	* trans-types.c (gfc_init_kinds): Likewise.

libcpp/ChangeLog:
	* pch.c (cpp_valid_state): Fix indentation so that it reflects the
	block structure.
---
 gcc/auto-profile.c           | 52 ++++++++++++++++++++++----------------------
 gcc/cfgcleanup.c             |  6 ++---
 gcc/cgraph.c                 | 10 ++++-----
 gcc/cgraphunit.c             | 16 +++++++-------
 gcc/config/i386/i386.c       |  4 ++--
 gcc/cp/parser.c              | 18 ++++++++-------
 gcc/dwarf2asm.c              |  4 ++--
 gcc/dwarf2out.c              | 12 +++++-----
 gcc/fortran/expr.c           |  4 ++--
 gcc/fortran/interface.c      |  2 +-
 gcc/fortran/parse.c          |  2 +-
 gcc/fortran/target-memory.c  |  4 ++--
 gcc/fortran/trans-types.c    | 12 +++++-----
 gcc/gensupport.c             | 11 +++++-----
 gcc/lto-wrapper.c            | 14 ++++++------
 gcc/tree-if-conv.c           |  2 +-
 gcc/tree-ssa-loop-prefetch.c |  4 ++--
 gcc/tree-ssa-tail-merge.c    |  2 +-
 gcc/tree-vect-data-refs.c    |  2 +-
 gcc/tree-vect-loop.c         |  2 +-
 gcc/tree-vect-slp.c          |  2 +-
 gcc/tree-vect-stmts.c        |  2 +-
 gcc/tree-vrp.c               | 12 +++++-----
 gcc/tree.c                   |  2 +-
 libcpp/pch.c                 |  2 +-
 25 files changed, 103 insertions(+), 100 deletions(-)

diff --git a/gcc/auto-profile.c b/gcc/auto-profile.c
index ba2d5ab..55dd8d1 100644
--- a/gcc/auto-profile.c
+++ b/gcc/auto-profile.c
@@ -1143,28 +1143,28 @@ afdo_find_equiv_class (bb_set *annotated_bb)
     bb->aux = bb;
     dom_bbs = get_dominated_by (CDI_DOMINATORS, bb);
     FOR_EACH_VEC_ELT (dom_bbs, i, bb1)
-    if (bb1->aux == NULL && dominated_by_p (CDI_POST_DOMINATORS, bb, bb1)
-        && bb1->loop_father == bb->loop_father)
-      {
-        bb1->aux = bb;
-        if (bb1->count > bb->count && is_bb_annotated (bb1, *annotated_bb))
-          {
-            bb->count = bb1->count;
-            set_bb_annotated (bb, annotated_bb);
-          }
-      }
+      if (bb1->aux == NULL && dominated_by_p (CDI_POST_DOMINATORS, bb, bb1)
+	  && bb1->loop_father == bb->loop_father)
+	{
+	  bb1->aux = bb;
+	  if (bb1->count > bb->count && is_bb_annotated (bb1, *annotated_bb))
+	    {
+	      bb->count = bb1->count;
+	      set_bb_annotated (bb, annotated_bb);
+	    }
+	}
     dom_bbs = get_dominated_by (CDI_POST_DOMINATORS, bb);
     FOR_EACH_VEC_ELT (dom_bbs, i, bb1)
-    if (bb1->aux == NULL && dominated_by_p (CDI_DOMINATORS, bb, bb1)
-        && bb1->loop_father == bb->loop_father)
-      {
-        bb1->aux = bb;
-        if (bb1->count > bb->count && is_bb_annotated (bb1, *annotated_bb))
-          {
-            bb->count = bb1->count;
-            set_bb_annotated (bb, annotated_bb);
-          }
-      }
+      if (bb1->aux == NULL && dominated_by_p (CDI_DOMINATORS, bb, bb1)
+	  && bb1->loop_father == bb->loop_father)
+	{
+	  bb1->aux = bb;
+	  if (bb1->count > bb->count && is_bb_annotated (bb1, *annotated_bb))
+	    {
+	      bb->count = bb1->count;
+	      set_bb_annotated (bb, annotated_bb);
+	    }
+	}
   }
 }
 
@@ -1191,10 +1191,10 @@ afdo_propagate_edge (bool is_succ, bb_set *annotated_bb,
     gcov_type total_known_count = 0;
 
     FOR_EACH_EDGE (e, ei, is_succ ? bb->succs : bb->preds)
-    if (!is_edge_annotated (e, *annotated_edge))
-      num_unknown_edge++, unknown_edge = e;
-    else
-      total_known_count += e->count;
+      if (!is_edge_annotated (e, *annotated_edge))
+	num_unknown_edge++, unknown_edge = e;
+      else
+	total_known_count += e->count;
 
     if (num_unknown_edge == 0)
       {
@@ -1404,7 +1404,7 @@ afdo_calculate_branch_prob (bb_set *annotated_bb, edge_set *annotated_edge)
     edge_iterator ei;
 
     FOR_EACH_EDGE (e, ei, bb->succs)
-    e->count = (double)bb->count * e->probability / REG_BR_PROB_BASE;
+      e->count = (double)bb->count * e->probability / REG_BR_PROB_BASE;
     bb->aux = NULL;
   }
 
@@ -1502,7 +1502,7 @@ afdo_annotate_cfg (const stmt_set &promoted_stmts)
 
     bb->count = 0;
     FOR_EACH_EDGE (e, ei, bb->succs)
-    e->count = 0;
+      e->count = 0;
 
     if (afdo_set_bb_count (bb, promoted_stmts))
       set_bb_annotated (bb, &annotated_bb);
diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c
index cee152e..53a28e5 100644
--- a/gcc/cfgcleanup.c
+++ b/gcc/cfgcleanup.c
@@ -1032,7 +1032,7 @@ equal_different_set_p (rtx p1, rtx s1, rtx p2, rtx s2)
           ? rtx_renumbered_equal_p (e1, e2) : rtx_equal_p (e1, e2))
         continue;
 
-        return false;
+      return false;
     }
 
   return true;
@@ -2001,7 +2001,7 @@ try_crossjump_to_edge (int mode, edge e1, edge e2,
      If we have tablejumps in the end of SRC1 and SRC2
      they have been already compared for equivalence in outgoing_edges_match ()
      so replace the references to TABLE1 by references to TABLE2.  */
-    {
+  {
       rtx label1, label2;
       rtx_jump_table_data *table1, *table2;
 
@@ -2021,7 +2021,7 @@ try_crossjump_to_edge (int mode, edge e1, edge e2,
 		replace_label_in_insn (insn, label1, label2, true);
 	    }
 	}
-    }
+  }
 
   /* Avoid splitting if possible.  We must always split when SRC2 has
      EH predecessor edges, or we may end up with basic blocks with both
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 85531c8..7f83ccc 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -2955,11 +2955,11 @@ cgraph_node::verify_node (void)
 	  }
 	else
 	  ref_found = true;
-	if (!ref_found)
-	  {
-	    error ("Analyzed alias has no reference");
-	    error_found = true;
-	  }
+      if (!ref_found)
+	{
+	  error ("Analyzed alias has no reference");
+	  error_found = true;
+	}
     }
 
   /* Check instrumented version reference.  */
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 2315ba8..a3f73a5 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -2019,14 +2019,14 @@ expand_all_functions (void)
 
       if (node->process)
 	{
-     expanded_func_count++;
-     if(node->tp_first_run)
-       profiled_func_count++;
-
-    if (symtab->dump_file)
-	  fprintf (symtab->dump_file,
-		   "Time profile order in expand_all_functions:%s:%d\n",
-		   node->asm_name (), node->tp_first_run);
+	  expanded_func_count++;
+	  if(node->tp_first_run)
+	    profiled_func_count++;
+
+	  if (symtab->dump_file)
+	    fprintf (symtab->dump_file,
+		     "Time profile order in expand_all_functions:%s:%d\n",
+		     node->asm_name (), node->tp_first_run);
 	  node->process = 0;
 	  node->expand ();
 	}
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 74332f3..0249e7f 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -19619,7 +19619,7 @@ ix86_expand_copysign (rtx operands[])
       else
 	copysign_insn = gen_copysigntf3_const;
 
-	emit_insn (copysign_insn (dest, op0, op1, mask));
+      emit_insn (copysign_insn (dest, op0, op1, mask));
     }
   else
     {
@@ -26128,7 +26128,7 @@ exact_dependency_1 (rtx addr, rtx insn)
 	  for (j = 0; j < XVECLEN (insn, i); j++)
 	    if (exact_dependency_1 (addr, XVECEXP (insn, i, j)))
 	      return true;
-	    break;
+	  break;
 	}
     }
   return false;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 73f5da7..daa4360 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -16774,10 +16774,11 @@ cp_parser_asm_definition (cp_parser* parser)
 	      && cp_lexer_next_token_is_not (parser->lexer,
 					     CPP_CLOSE_PAREN)
 	      && !goto_p)
-	    outputs = cp_parser_asm_operand_list (parser);
-
-	    if (outputs == error_mark_node)
-	      invalid_outputs_p = true;
+            {
+              outputs = cp_parser_asm_operand_list (parser);
+              if (outputs == error_mark_node)
+                invalid_outputs_p = true;
+            }
 	}
       /* If the next token is `::', there are no outputs, and the
 	 next token is the beginning of the inputs.  */
@@ -16798,10 +16799,11 @@ cp_parser_asm_definition (cp_parser* parser)
 					     CPP_SCOPE)
 	      && cp_lexer_next_token_is_not (parser->lexer,
 					     CPP_CLOSE_PAREN))
-	    inputs = cp_parser_asm_operand_list (parser);
-
-	    if (inputs == error_mark_node)
-	      invalid_inputs_p = true;
+            {
+              inputs = cp_parser_asm_operand_list (parser);
+              if (inputs == error_mark_node)
+                invalid_inputs_p = true;
+            }
 	}
       else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
 	/* The clobbers are coming next.  */
diff --git a/gcc/dwarf2asm.c b/gcc/dwarf2asm.c
index b817aaf..ba1b543 100644
--- a/gcc/dwarf2asm.c
+++ b/gcc/dwarf2asm.c
@@ -931,9 +931,9 @@ dw2_output_indirect_constants (void)
        iter != indirect_pool->end (); ++iter)
     temp.quick_push (*iter);
 
-    temp.qsort (compare_strings);
+  temp.qsort (compare_strings);
 
-    for (unsigned int i = 0; i < temp.length (); i++)
+  for (unsigned int i = 0; i < temp.length (); i++)
     dw2_output_indirect_constant_1 (temp[i].first, temp[i].second);
 }
 
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index a04e6f6..06d18fe 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -16321,12 +16321,12 @@ tree_add_const_value_attribute_for_decl (dw_die_ref var_die, tree decl)
 	  && !TREE_STATIC (decl)))
     return false;
 
-    if (TREE_READONLY (decl)
-	&& ! TREE_THIS_VOLATILE (decl)
-	&& DECL_INITIAL (decl))
-      /* OK */;
-    else
-      return false;
+  if (TREE_READONLY (decl)
+      && ! TREE_THIS_VOLATILE (decl)
+      && DECL_INITIAL (decl))
+    /* OK */;
+  else
+    return false;
 
   /* Don't add DW_AT_const_value if abstract origin already has one.  */
   if (get_AT (var_die, DW_AT_const_value))
diff --git a/gcc/fortran/expr.c b/gcc/fortran/expr.c
index ab6f7a5..3673bfd 100644
--- a/gcc/fortran/expr.c
+++ b/gcc/fortran/expr.c
@@ -2297,8 +2297,8 @@ check_inquiry (gfc_expr *e, int not_restricted)
 	if (strcmp (functions[i], name) == 0)
 	  break;
 
-	if (functions[i] == NULL)
-	  return MATCH_ERROR;
+      if (functions[i] == NULL)
+	return MATCH_ERROR;
     }
 
   /* At this point we have an inquiry function with a variable argument.  The
diff --git a/gcc/fortran/interface.c b/gcc/fortran/interface.c
index 320eb01..0d29bde 100644
--- a/gcc/fortran/interface.c
+++ b/gcc/fortran/interface.c
@@ -2169,7 +2169,7 @@ compare_parameter (gfc_symbol *formal, gfc_expr *actual,
 	    gfc_error ("Passing coarray at %L to allocatable, noncoarray, "
 		       "INTENT(OUT) dummy argument %qs", &actual->where,
 		       formal->name);
-	    return 0;
+	  return 0;
 	}
       else if (warn_surprising && where && formal->attr.intent != INTENT_IN)
 	gfc_warning (OPT_Wsurprising,
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 2c7c554..30e4eab 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
 	unexpected_eof ();
       else if (st != acc_end_st)
 	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
-	reject_statement ();
+      reject_statement ();
     }
   while (st != acc_end_st);
 
diff --git a/gcc/fortran/target-memory.c b/gcc/fortran/target-memory.c
index 4d63636..187682a 100644
--- a/gcc/fortran/target-memory.c
+++ b/gcc/fortran/target-memory.c
@@ -671,8 +671,8 @@ expr_to_char (gfc_expr *e, unsigned char *data, unsigned char *chk, size_t len)
 	  gcc_assert (cmp && cmp->backend_decl);
 	  if (!c->expr)
 	    continue;
-	    ptr = TREE_INT_CST_LOW(DECL_FIELD_OFFSET(cmp->backend_decl))
-			+ TREE_INT_CST_LOW(DECL_FIELD_BIT_OFFSET(cmp->backend_decl))/8;
+	  ptr = TREE_INT_CST_LOW(DECL_FIELD_OFFSET(cmp->backend_decl))
+	    + TREE_INT_CST_LOW(DECL_FIELD_BIT_OFFSET(cmp->backend_decl))/8;
 	  expr_to_char (c->expr, &data[ptr], &chk[ptr], len);
 	}
       return len;
diff --git a/gcc/fortran/trans-types.c b/gcc/fortran/trans-types.c
index 0ad8ac2..41ffbaa 100644
--- a/gcc/fortran/trans-types.c
+++ b/gcc/fortran/trans-types.c
@@ -438,10 +438,10 @@ gfc_init_kinds (void)
       /* Only let float, double, long double and __float128 go through.
 	 Runtime support for others is not provided, so they would be
 	 useless.  */
-	if (!targetm.libgcc_floating_mode_supported_p ((machine_mode)
+      if (!targetm.libgcc_floating_mode_supported_p ((machine_mode)
 						       mode))
-	  continue;
-	if (mode != TYPE_MODE (float_type_node)
+	continue;
+      if (mode != TYPE_MODE (float_type_node)
 	    && (mode != TYPE_MODE (double_type_node))
 	    && (mode != TYPE_MODE (long_double_type_node))
 #if defined(HAVE_TFmode) && defined(ENABLE_LIBQUADMATH_SUPPORT)
@@ -587,7 +587,7 @@ gfc_init_kinds (void)
 	gfc_fatal_error ("REAL(KIND=4) is not available for "
 			 "%<-freal-8-real-4%> option");
 
-	gfc_default_double_kind = 4;
+      gfc_default_double_kind = 4;
     }
   else if (flag_real8_kind == 10 )
     {
@@ -595,7 +595,7 @@ gfc_init_kinds (void)
 	gfc_fatal_error ("REAL(KIND=10) is not available for "
 			 "%<-freal-8-real-10%> option");
 
-	gfc_default_double_kind = 10;
+      gfc_default_double_kind = 10;
     }
   else if (flag_real8_kind == 16 )
     {
@@ -603,7 +603,7 @@ gfc_init_kinds (void)
 	gfc_fatal_error ("REAL(KIND=10) is not available for "
 			 "%<-freal-8-real-16%> option");
 
-	gfc_default_double_kind = 16;
+      gfc_default_double_kind = 16;
     }
   else if (saw_r4 && saw_r8)
     gfc_default_double_kind = 8;
diff --git a/gcc/gensupport.c b/gcc/gensupport.c
index 26a09bc..7d025b6 100644
--- a/gcc/gensupport.c
+++ b/gcc/gensupport.c
@@ -2227,11 +2227,12 @@ process_define_subst (void)
 	if (strcmp (XSTR (elem->data, 0), XSTR (elem_attr->data, 1)) == 0)
 	    goto found;
 
-	error_with_line (elem->lineno,
-			 "%s: `define_subst' must have at least one "
-			 "corresponding `define_subst_attr'",
-			 XSTR (elem->data, 0));
-	return;
+      error_with_line (elem->lineno,
+		       "%s: `define_subst' must have at least one "
+		       "corresponding `define_subst_attr'",
+		       XSTR (elem->data, 0));
+      return;
+
       found:
 	continue;
     }
diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c
index 404cb68..123ffc1 100644
--- a/gcc/lto-wrapper.c
+++ b/gcc/lto-wrapper.c
@@ -303,13 +303,13 @@ merge_and_complain (struct cl_decoded_option **decoded_options,
 	  for (j = 0; j < *decoded_options_count; ++j)
 	    if ((*decoded_options)[j].opt_index == foption->opt_index)
 	      break;
-	    if (j == *decoded_options_count)
-	      append_option (decoded_options, decoded_options_count, foption);
-	    else if (foption->value != (*decoded_options)[j].value)
-	      fatal_error (input_location,
-			   "Option %s not used consistently in all LTO input"
-			   " files", foption->orig_option_with_args_text);
-	    break;
+	  if (j == *decoded_options_count)
+	    append_option (decoded_options, decoded_options_count, foption);
+	  else if (foption->value != (*decoded_options)[j].value)
+	    fatal_error (input_location,
+			 "Option %s not used consistently in all LTO input"
+			 " files", foption->orig_option_with_args_text);
+	  break;
 
 	case OPT_O:
 	case OPT_Ofast:
diff --git a/gcc/tree-if-conv.c b/gcc/tree-if-conv.c
index 400ee01..49ff458 100644
--- a/gcc/tree-if-conv.c
+++ b/gcc/tree-if-conv.c
@@ -1087,7 +1087,7 @@ if_convertible_bb_p (struct loop *loop, basic_block bb, basic_block exit_bb)
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
 	fprintf (dump_file, "only critical predecessors\n");
-	return false;
+      return false;
     }
 
   return true;
diff --git a/gcc/tree-ssa-loop-prefetch.c b/gcc/tree-ssa-loop-prefetch.c
index 10e156e..a948d68 100644
--- a/gcc/tree-ssa-loop-prefetch.c
+++ b/gcc/tree-ssa-loop-prefetch.c
@@ -368,8 +368,8 @@ find_or_create_group (struct mem_ref_group **groups, tree base, tree step)
 
       /* If step is an integer constant, keep the list of groups sorted
          by decreasing step.  */
-        if (cst_and_fits_in_hwi ((*groups)->step) && cst_and_fits_in_hwi (step)
-            && int_cst_value ((*groups)->step) < int_cst_value (step))
+      if (cst_and_fits_in_hwi ((*groups)->step) && cst_and_fits_in_hwi (step)
+	  && int_cst_value ((*groups)->step) < int_cst_value (step))
 	break;
     }
 
diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c
index 4afe94e..ee6e7b2 100644
--- a/gcc/tree-ssa-tail-merge.c
+++ b/gcc/tree-ssa-tail-merge.c
@@ -379,7 +379,7 @@ gsi_advance_fw_nondebug_nonlocal (gimple_stmt_iterator *gsi)
       stmt = gsi_stmt (*gsi);
       if (!stmt_local_def (stmt))
 	return;
-	gsi_next_nondebug (gsi);
+      gsi_next_nondebug (gsi);
     }
 }
 
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 3913862..2abea1a 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -5019,7 +5019,7 @@ vect_grouped_load_supported (tree vectype, unsigned HOST_WIDE_INT count)
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "shuffle of 3 loads is not supported by"
 				     " target\n");
-		    return false;
+		  return false;
 		}
 	      for (i = 0, j = 0; i < nelt; i++)
 		if (3 * i + k < 2 * nelt)
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 88ef251..49bf518 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -5057,7 +5057,7 @@ vectorizable_reduction (gimple stmt, gimple_stmt_iterator *gsi,
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported condition in reduction\n");
 
-            return false;
+	  return false;
         }
     }
   else
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 73ab24e..a2ab398 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -3107,7 +3107,7 @@ vect_transform_slp_perm_load (slp_tree node, vec<tree> dr_chain,
      we need the second and the third vectors: {b1,c1,a2,b2} and
      {c2,a3,b3,c3}.  */
 
-    {
+  {
       scalar_index = 0;
       index = 0;
       vect_stmts_counter = 0;
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index de35508..a20fcc6 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -4445,7 +4445,7 @@ vectorizable_shift (gimple stmt, gimple_stmt_iterator *gsi,
                         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                          "unusable type for last operand in"
                                          " vector/vector shift/rotate.\n");
-			return false;
+		      return false;
 		    }
 		  if (vec_stmt && !slp_node)
 		    {
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index e7ab23c..c7d243c 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -10258,12 +10258,12 @@ vrp_finalize (void)
 	  || (vr_value[i]->type == VR_UNDEFINED))
 	continue;
 
-	if ((TREE_CODE (vr_value[i]->min) == INTEGER_CST)
-	    && (TREE_CODE (vr_value[i]->max) == INTEGER_CST)
-	    && (vr_value[i]->type == VR_RANGE
-		|| vr_value[i]->type == VR_ANTI_RANGE))
-	  set_range_info (name, vr_value[i]->type, vr_value[i]->min,
-			  vr_value[i]->max);
+      if ((TREE_CODE (vr_value[i]->min) == INTEGER_CST)
+	  && (TREE_CODE (vr_value[i]->max) == INTEGER_CST)
+	  && (vr_value[i]->type == VR_RANGE
+	      || vr_value[i]->type == VR_ANTI_RANGE))
+	set_range_info (name, vr_value[i]->type, vr_value[i]->min,
+			vr_value[i]->max);
       }
 
   /* Free allocated memory.  */
diff --git a/gcc/tree.c b/gcc/tree.c
index 01860af..114d5b3 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -8927,7 +8927,7 @@ variably_modified_type_p (tree type, tree fn)
 	    if (TREE_CODE (type) == QUAL_UNION_TYPE)
 	      RETURN_TRUE_IF_VAR (DECL_QUALIFIER (t));
 	  }
-	break;
+      break;
 
     case ARRAY_TYPE:
       /* Do not call ourselves to avoid infinite recursion.  This is
diff --git a/libcpp/pch.c b/libcpp/pch.c
index eb2d30b..ed59565 100644
--- a/libcpp/pch.c
+++ b/libcpp/pch.c
@@ -713,7 +713,7 @@ cpp_valid_state (cpp_reader *r, const char *name, int fd)
 	cpp_warning_syshdr (r, CPP_W_INVALID_PCH,
 		            "%s: not used because `__COUNTER__' is invalid",
 		            name);
-	goto fail;
+      goto fail;
     }
 
   /* We win!  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/3] Fix spurious semicolons
  2015-04-28 23:52         ` [PATCH 2/3] Fix spurious semicolons David Malcolm
@ 2015-04-29  0:10           ` Jeff Law
  0 siblings, 0 replies; 26+ messages in thread
From: Jeff Law @ 2015-04-29  0:10 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 04/28/2015 06:02 PM, David Malcolm wrote:
> An earlier version of the -Wmisleading-indentation patch issued
> warnings for spurious trailing semicolons guarded by a conditional.
>
> Although the latest version of -Wmisleading-indentation no longer
> warns about these, I fixed the ones that were reported, thusly.
>
> gcc/ChangeLog:
> 	* builtins.c (fold_builtin_1): Remove spurious second
> 	semicolon.
> 	* cgraph.h (symtab_node::get_availability): Likewise.
> 	* opts.c (common_handle_option): Remove spurious second semicolon.
> 	* tree-ssa-loop-ivopts.c (extract_cond_operands): Likewise.
> 	* tree-ssa-loop-niter.c (derive_constant_upper_bound_ops): Likewise.
>
> gcc/cp/ChangeLog:
> 	* cp-tree.h (ARGUMENT_PACK_SELECT_ARG): Remove spurious
> 	trailing semicolon.
>
> gcc/fortran/ChangeLog:
> 	* options.c (gfc_init_options): Remove spurious second
> 	semicolon.
> 	* trans-stmt.c (gfc_trans_allocate): Likewise.
This falls under the obvious rule :-)

Please install.

jeff

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation
  2015-04-28 23:53         ` [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation David Malcolm
@ 2015-04-29  0:25           ` Jeff Law
  2015-04-29 12:35           ` Mikael Morin
  1 sibling, 0 replies; 26+ messages in thread
From: Jeff Law @ 2015-04-29  0:25 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 04/28/2015 06:02 PM, David Malcolm wrote:
> This patch fixes the places in our tree where -Wmisleading-indentation
> correctly identifies code that's misleadingly indented.
>
> In particular, a couple of places in
>    gcc/cp/parser.c:cp_parser_asm_definition
> appeared to be missing braces, both of the form:
>
>    if (complicated_multiline_condition ())
>      some_var_not_otherwise_set = some_value ();
>
>      if (some_var_not_otherwise_set == error_mark_node)
>         some_flag = true;
>
> where the second "if" is misleadingly indented as if guarded by
> the first.  Given the logic, it was clear that these ought to have
> been so guarded, so I changed these to read:
>
>   if (complicated_multiline_condition ())
>     {
>       some_var_not_otherwise_set = some_value ();
>
>       if (some_var_not_otherwise_set == error_mark_node)
>         some_flag = true;
>     }
>
> gcc/ChangeLog:
> 	* auto-profile.c (afdo_find_equiv_class): Fix indentation so
> 	that it reflects the block structure.
> 	(afdo_propagate_edge): Likewise.
> 	(afdo_calculate_branch_prob): Likewise.
> 	(afdo_annotate_cfg): Likewise.
> 	* cfgcleanup.c (equal_different_set_p): Likewise.
> 	(try_crossjump_to_edge): Likewise.
> 	* cgraph.c (cgraph_node::verify_node): Likewise.
> 	* cgraphunit.c (expand_all_functions): Likewise.
> 	* config/i386/i386.c (ix86_expand_copysign): Likewise.
> 	(exact_dependency_1): Likewise.
> 	* dwarf2asm.c (dw2_output_indirect_constants): Likewise.
> 	* dwarf2out.c (tree_add_const_value_attribute_for_decl): Likewise.
> 	* gensupport.c (process_define_subst): Likewise.
> 	* lto-wrapper.c (merge_and_complain): Likewise.
> 	* tree-if-conv.c (if_convertible_bb_p): Likewise.
> 	* tree-ssa-loop-prefetch.c (find_or_create_group): Likewise.
> 	* tree-ssa-tail-merge.c (gsi_advance_fw_nondebug_nonlocal): Likewise.
> 	* tree-vect-data-refs.c (vect_grouped_load_supported): Likewise.
> 	* tree-vect-loop.c (vectorizable_reduction): Likewise.
> 	* tree-vect-slp.c (vect_transform_slp_perm_load): Likewise.
> 	* tree-vect-stmts.c (vectorizable_shift): Likewise.
> 	* tree-vrp.c (vrp_finalize): Likewise.
> 	* tree.c (variably_modified_type_p): Likewise.
>
> gcc/cp/ChangeLog:
> 	* parser.c (cp_parser_asm_definition): Only test for
>          error_mark_node if "outputs" was just set.
> 	(cp_parser_asm_definition): Likewise for "inputs".
>
> gcc/fortran/ChangeLog:
> 	* expr.c (check_inquiry): Fix indentation so that it reflects the
> 	block structure.
> 	* interface.c (compare_parameter): Likewise.
> 	* parse.c (parse_oacc_structured_block): Likewise.
> 	* target-memory.c (expr_to_char): Likewise.
> 	* trans-types.c (gfc_init_kinds): Likewise.
>
> libcpp/ChangeLog:
> 	* pch.c (cpp_valid_state): Fix indentation so that it reflects the
> 	block structure.
OK.
jeff

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation
  2015-04-28 23:53         ` [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation David Malcolm
  2015-04-29  0:25           ` Jeff Law
@ 2015-04-29 12:35           ` Mikael Morin
  2015-05-05 19:44             ` David Malcolm
  1 sibling, 1 reply; 26+ messages in thread
From: Mikael Morin @ 2015-04-29 12:35 UTC (permalink / raw)
  To: David Malcolm, gcc-patches, thomas

Hello,

Le 29/04/2015 02:02, David Malcolm a écrit :
> diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
> index 2c7c554..30e4eab 100644
> --- a/gcc/fortran/parse.c
> +++ b/gcc/fortran/parse.c
> @@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
>  	unexpected_eof ();
>        else if (st != acc_end_st)
>  	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> -	reject_statement ();
> +      reject_statement ();
>      }
>    while (st != acc_end_st);
>  
I think this one is a bug; there should be braces around 'gfc_error' and
'reject_statement'.
At least that's the pattern in 'parse_oacc_loop', and how the
'unexpected_statement' function is used.
Author CC'ed in any case.

Mikael

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation
  2015-04-29 12:35           ` Mikael Morin
@ 2015-05-05 19:44             ` David Malcolm
  2015-05-06 11:38               ` Fix logic error in Fortran OpenACC parsing (was: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation) Thomas Schwinge
  0 siblings, 1 reply; 26+ messages in thread
From: David Malcolm @ 2015-05-05 19:44 UTC (permalink / raw)
  To: Mikael Morin; +Cc: gcc-patches, thomas

On Wed, 2015-04-29 at 14:10 +0200, Mikael Morin wrote:
> Hello,
> 
> Le 29/04/2015 02:02, David Malcolm a écrit :
> > diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
> > index 2c7c554..30e4eab 100644
> > --- a/gcc/fortran/parse.c
> > +++ b/gcc/fortran/parse.c
> > @@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
> >  	unexpected_eof ();
> >        else if (st != acc_end_st)
> >  	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> > -	reject_statement ();
> > +      reject_statement ();
> >      }
> >    while (st != acc_end_st);
> >  
> I think this one is a bug; there should be braces around 'gfc_error' and
> 'reject_statement'.
> At least that's the pattern in 'parse_oacc_loop', and how the
> 'unexpected_statement' function is used.
> Author CC'ed in any case.

Thanks.

FWIW, Jeff had approved that patch, so I've committed the patch to trunk
(as r222823), making the indentation reflect the block structure.

Thomas:  should the
      reject_statement ();
call in the above be guarded by the
     else if (st != acc_end_st)
clause?

Thanks
Dave


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Fix logic error in Fortran OpenACC parsing (was: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation)
  2015-05-05 19:44             ` David Malcolm
@ 2015-05-06 11:38               ` Thomas Schwinge
  2015-05-08 11:24                 ` Fix logic error in Fortran OpenACC parsing Ilmir Usmanov
  0 siblings, 1 reply; 26+ messages in thread
From: Thomas Schwinge @ 2015-05-06 11:38 UTC (permalink / raw)
  To: fortran, Tobias Burnus
  Cc: gcc-patches, David Malcolm, Mikael Morin, Ilmir Usmanov

[-- Attachment #1: Type: text/plain, Size: 3313 bytes --]

Hi!

On Tue, 5 May 2015 15:38:03 -0400, David Malcolm <dmalcolm@redhat.com> wrote:
> On Wed, 2015-04-29 at 14:10 +0200, Mikael Morin wrote:
> > Le 29/04/2015 02:02, David Malcolm a écrit :
> > > diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
> > > index 2c7c554..30e4eab 100644
> > > --- a/gcc/fortran/parse.c
> > > +++ b/gcc/fortran/parse.c
> > > @@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
> > >  	unexpected_eof ();
> > >        else if (st != acc_end_st)
> > >  	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> > > -	reject_statement ();
> > > +      reject_statement ();
> > >      }
> > >    while (st != acc_end_st);
> > >  
> > I think this one is a bug; there should be braces around 'gfc_error' and
> > 'reject_statement'.

Mikael, thanks for noticing this, and, David, nice
-Wmisleading-indentation patch set!

> > At least that's the pattern in 'parse_oacc_loop', and how the
> > 'unexpected_statement' function is used.

> FWIW, Jeff had approved that patch, so I've committed the patch to trunk
> (as r222823), making the indentation reflect the block structure.
> 
> Thomas:  should the
>       reject_statement ();
> call in the above be guarded by the
>      else if (st != acc_end_st)
> clause?

Indeed, this seems to be a bug that has been introduced very early in the
OpenACC Fortran front end development -- see how the
parse_oacc_structured_block function evolved in the patches posted in
<http://news.gmane.org/find-root.php?message_id=%3C52E1595D.9000007%40samsung.com%3E>
and following (Ilmir, CCed "just in case").  I also see that the
corresponding OpenMP code, parse_omp_structured_block, just calls
unexpected_statement, which Ilmir's initial patch also did, but at some
point, he then changed this to the current code: gfc_error followed by
reject_statement, as cited above -- I would guess for the reason to get a
better error message?  (Tobias, should this thus also be done for OpenMP,
and/or extend unexpected_statement accordingly?)

And then, I'm a bit confused: is it "OK" that despite this presumed logic
error, which affects all (?) valid executions of this parsing code, we're
not running into any issues with the OpenACC Fortran front end test
cases?

OK for trunk?

commit 068eebfa63b2b4c8849ed5fd2c9d0a130586dfb0
Author: Thomas Schwinge <thomas@codesourcery.com>
Date:   Wed May 6 13:18:18 2015 +0200

    Fix logic error in Fortran OpenACC parsing
    
    	gcc/fortran/
    	* parse.c (parse_oacc_structured_block): Fix logic error.
    	Reported by Mikael Morin <mikael.morin@sfr.fr>.
---
 gcc/fortran/parse.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git gcc/fortran/parse.c gcc/fortran/parse.c
index 30e4eab..e977498 100644
--- gcc/fortran/parse.c
+++ gcc/fortran/parse.c
@@ -4282,8 +4282,10 @@ parse_oacc_structured_block (gfc_statement acc_st)
       if (st == ST_NONE)
 	unexpected_eof ();
       else if (st != acc_end_st)
-	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
-      reject_statement ();
+	{
+	  gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
+	  reject_statement ();
+	}
     }
   while (st != acc_end_st);
 


Grüße,
 Thomas

[-- Attachment #2: Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Fix logic error in Fortran OpenACC parsing
  2015-05-06 11:38               ` Fix logic error in Fortran OpenACC parsing (was: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation) Thomas Schwinge
@ 2015-05-08 11:24                 ` Ilmir Usmanov
  2015-07-27 14:37                   ` Thomas Schwinge
  0 siblings, 1 reply; 26+ messages in thread
From: Ilmir Usmanov @ 2015-05-08 11:24 UTC (permalink / raw)
  To: Thomas Schwinge
  Cc: fortran, Tobias Burnus, gcc-patches, David Malcolm, Mikael Morin

Hi!

On 06.05.2015 14:38, Thomas Schwinge wrote:
> Hi!
>
> On Tue, 5 May 2015 15:38:03 -0400, David Malcolm <dmalcolm@redhat.com> wrote:
>> On Wed, 2015-04-29 at 14:10 +0200, Mikael Morin wrote:
>>> Le 29/04/2015 02:02, David Malcolm a écrit :
>>>> diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
>>>> index 2c7c554..30e4eab 100644
>>>> --- a/gcc/fortran/parse.c
>>>> +++ b/gcc/fortran/parse.c
>>>> @@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
>>>>   	unexpected_eof ();
>>>>         else if (st != acc_end_st)
>>>>   	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
>>>> -	reject_statement ();
>>>> +      reject_statement ();
>>>>       }
>>>>     while (st != acc_end_st);
>>>>   
>>> I think this one is a bug; there should be braces around 'gfc_error' and
>>> 'reject_statement'.
If 'st' is 'acc_end_st', as it shall be, the statement is rejected. So, 
this is a bug.

>
>>> At least that's the pattern in 'parse_oacc_loop', and how the
>>> 'unexpected_statement' function is used.
>> FWIW, Jeff had approved that patch, so I've committed the patch to trunk
>> (as r222823), making the indentation reflect the block structure.
>>
>> Thomas:  should the
>>        reject_statement ();
>> call in the above be guarded by the
>>       else if (st != acc_end_st)
>> clause?
> Indeed, this seems to be a bug that has been introduced very early in the
> OpenACC Fortran front end development -- see how the
> parse_oacc_structured_block function evolved in the patches posted in
> <http://news.gmane.org/find-root.php?message_id=%3C52E1595D.9000007%40samsung.com%3E>
> and following (Ilmir, CCed "just in case").  I also see that the
> corresponding OpenMP code, parse_omp_structured_block, just calls
> unexpected_statement, which Ilmir's initial patch also did, but at some
> point, he then changed this to the current code: gfc_error followed by
> reject_statement, as cited above -- I would guess for the reason to get a
> better error message?  (Tobias, should this thus also be done for OpenMP,
> and/or extend unexpected_statement accordingly?)
That's true.
I've checked abandoned openacc-1_0-branch and I used 
unexpected_statement there (there still odd *_acc_* naming presents 
instead of new-and-shiny *_oacc_* one), but, as you mentioned, I've 
changed this for better error reporting... and introduced the bug.

>
> And then, I'm a bit confused: is it "OK" that despite this presumed logic
> error, which affects all (?) valid executions of this parsing code, we're
> not running into any issues with the OpenACC Fortran front end test
> cases?
I think, this is OK, since this is an !$ACC END _smth_ statement and it 
shall not present in the AST. So, it is abandoned later anyway ;)  (if I 
remember correctly, during gfc_clear_new_st call). Although the bug does 
not affect the logic, it is still a bug.

> OK for trunk?
 From my point of view, OK.

>
> commit 068eebfa63b2b4c8849ed5fd2c9d0a130586dfb0
> Author: Thomas Schwinge <thomas@codesourcery.com>
> Date:   Wed May 6 13:18:18 2015 +0200
>
>      Fix logic error in Fortran OpenACC parsing
>      
>      	gcc/fortran/
>      	* parse.c (parse_oacc_structured_block): Fix logic error.
>      	Reported by Mikael Morin <mikael.morin@sfr.fr>.
> ---
>   gcc/fortran/parse.c |    6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git gcc/fortran/parse.c gcc/fortran/parse.c
> index 30e4eab..e977498 100644
> --- gcc/fortran/parse.c
> +++ gcc/fortran/parse.c
> @@ -4282,8 +4282,10 @@ parse_oacc_structured_block (gfc_statement acc_st)
>         if (st == ST_NONE)
>   	unexpected_eof ();
>         else if (st != acc_end_st)
> -	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> -      reject_statement ();
> +	{
> +	  gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> +	  reject_statement ();
> +	}
>       }
>     while (st != acc_end_st);
>   
>
>
> Grüße,
>   Thomas
-- 
Ilmir.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4)
  2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
  2015-04-28 23:52         ` [PATCH 2/3] Fix spurious semicolons David Malcolm
  2015-04-28 23:53         ` [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation David Malcolm
@ 2015-05-11 21:23         ` Jeff Law
  2015-05-12 21:28           ` David Malcolm
  2 siblings, 1 reply; 26+ messages in thread
From: Jeff Law @ 2015-05-11 21:23 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 04/28/2015 06:02 PM, David Malcolm wrote:
> This is an updated implementation of the proposed
> -Wmisleading-indentation warning.
>
> Changes since last patch:
>
> * I've rewritten it to use Manuel's approach from
>      https://gcc.gnu.org/ml/gcc-patches/2015-04/msg01225.html
>    I took Manuel's proposed patch for the C frontend and generalized
>    it to also work on the C++ frontend.  I moved the logic into a new
>    file: c-family/c-indentation.c.
>
> * I've consolidated the testcases into 3 files.
>
> * I've added the option to -Wall, and added enough smarts to the
>    code so that it can bootstrap our own source tree without any false
>    positives.  See the testcases for examples of what I ran into.
I'm very hesitant of enabling this with -Wall without seeing what it 
does on other codebases, particularly those which are not based in a GNU 
formatting style.

For example, what does it to with KNF that's highly used in the BSD 
world or the  Linux kernel style?  I suspect we'll do the right thing 
because the major difference isn't whether or not to indent, but where 
the braces are put.  But some testing on the BSD or Linux kernel would 
go a long way to making me more comfortable with -Wall inclusion.

Also note that a bootstrap doesn't test C all that well anymore since 
GCC itself is mostly compiled with a C++ compiler.  But we're probably 
getting enough coverage to ensure we haven't totally mucked up sometihng 
in the runtime libraries.


> The only remaining known wart here is in the new testcase
> c-c++-common/Wmisleading-indentation-2.c, which uses a #line
> directive to refer to a .md file.  Ideally it ought to be able to
> actually locate the .md file during compilation, since that case is
> trickier to handle than a simple file-not-found.  Hence in theory we
> could have some logic in a .exp to handle copying up the .md from the
> srcdir to the testdir.  That said, the behavior is outwardly
> invisible in both cases: a false positive is not emitted.
Also bear in mind that the GCC testsuite is support to be able to be run 
on an installed tree.  Which brings up the larger issue, namely that 
whatever file generate the #line statements may not be available at the 
time you actually compile the code.  At best you could go look for it 
and use it if found and fallback to some sensible behaviour if not found.


I wonder if some of the cases where you're not working, such as

if (...)
bar;
com;

Ought to be warned for using various -Wmisleading-indentation=X options.

Your call if you want to tackle that in the future.


>
> gcc/ChangeLog:
> 	* Makefile.in (C_COMMON_OBJS): Add c-family/c-indentation.o.
>
> gcc/c-family/ChangeLog:
> 	* c-common.h (warn_for_misleading_indentation): New prototype.
> 	* c-indentation.c: New file.
> 	* c.opt (Wmisleading-indentation): New option.
>
> gcc/c/ChangeLog:
> 	* c-parser.c (c_parser_if_body): Add param "if_loc", use it
> 	to add a call to warn_for_misleading_indentation.
> 	(c_parser_else_body): Likewise, adding param "else_loc".
> 	(c_parser_if_statement): Check for misleading indentation.
> 	(c_parser_while_statement): Likewise.
> 	(c_parser_for_statement): Likewise.
>
> gcc/cp/ChangeLog:
> 	* parser.c (cp_parser_selection_statement): Add location and
> 	guard_kind arguments to calls to
> 	cp_parser_implicitly_scoped_statement.
> 	(cp_parser_iteration_statement): Likewise for calls to
> 	cp_parser_already_scoped_statement.
> 	(cp_parser_implicitly_scoped_statement): Add "guard_loc" and
> 	"guard_kind" params; use them to warn for misleading
> 	indentation.
> 	(cp_parser_already_scoped_statement): Likewise.
>
> gcc/ChangeLog:
> 	* doc/invoke.texi (Warning Options): Add -Wmisleading-indentation.
> 	(-Wall): Add -Wmisleading-indentation.
> 	(-Wmisleading-indentation): New option.
>
> gcc/testsuite/ChangeLog:
> 	* c-c++-common/Wmisleading-indentation.c: New testcase.
> 	* c-c++-common/Wmisleading-indentation-2.c: New testcase.
> 	* c-c++-common/Wmisleading-indentation-2.md: New file.
>
> libcpp/ChangeLog:
> 	* directives.c (do_line): Set seen_line_directive on line_table.
> 	(do_linemarker): Likewise.
> 	* include/line-map.h (struct line_maps): Add new field
> 	"seen_line_directive".
OK if you take it out of -Wall for now.  We can revisit it in -Wall 
separately :-)

jeff




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4)
  2015-05-11 21:23         ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) Jeff Law
@ 2015-05-12 21:28           ` David Malcolm
  2015-05-12 21:47             ` David Malcolm
  0 siblings, 1 reply; 26+ messages in thread
From: David Malcolm @ 2015-05-12 21:28 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 6522 bytes --]

On Mon, 2015-05-11 at 15:23 -0600, Jeff Law wrote:
> On 04/28/2015 06:02 PM, David Malcolm wrote:
> > This is an updated implementation of the proposed
> > -Wmisleading-indentation warning.
> >
> > Changes since last patch:
> >
> > * I've rewritten it to use Manuel's approach from
> >      https://gcc.gnu.org/ml/gcc-patches/2015-04/msg01225.html
> >    I took Manuel's proposed patch for the C frontend and generalized
> >    it to also work on the C++ frontend.  I moved the logic into a new
> >    file: c-family/c-indentation.c.
> >
> > * I've consolidated the testcases into 3 files.
> >
> > * I've added the option to -Wall, and added enough smarts to the
> >    code so that it can bootstrap our own source tree without any false
> >    positives.  See the testcases for examples of what I ran into.
> I'm very hesitant of enabling this with -Wall without seeing what it 
> does on other codebases, particularly those which are not based in a GNU 
> formatting style.
> 
> For example, what does it to with KNF that's highly used in the BSD 
> world or the  Linux kernel style?  I suspect we'll do the right thing 
> because the major difference isn't whether or not to indent, but where 
> the braces are put.  But some testing on the BSD or Linux kernel would 
> go a long way to making me more comfortable with -Wall inclusion.
> 
> Also note that a bootstrap doesn't test C all that well anymore since 
> GCC itself is mostly compiled with a C++ compiler.  But we're probably 
> getting enough coverage to ensure we haven't totally mucked up sometihng 
> in the runtime libraries.
> 
> 
> > The only remaining known wart here is in the new testcase
> > c-c++-common/Wmisleading-indentation-2.c, which uses a #line
> > directive to refer to a .md file.  Ideally it ought to be able to
> > actually locate the .md file during compilation, since that case is
> > trickier to handle than a simple file-not-found.  Hence in theory we
> > could have some logic in a .exp to handle copying up the .md from the
> > srcdir to the testdir.  That said, the behavior is outwardly
> > invisible in both cases: a false positive is not emitted.
> Also bear in mind that the GCC testsuite is support to be able to be run 
> on an installed tree.  Which brings up the larger issue, namely that 
> whatever file generate the #line statements may not be available at the 
> time you actually compile the code.  At best you could go look for it 
> and use it if found and fallback to some sensible behaviour if not found.

I don't think we can support #line with this warning: in theory you
could have a generated file that is full of #line directives that map
nicely to the indentation of some underlying higher-level source file,
but we can't rely on that: as you say, the file might not be readable by
us.  In the case I ran into, the generated insn-attrtab.c had
intermittent #line directives pointing into the .md file, and
indentation warnings were being emitted at lines of C++ code at some
remove from the the #line lines; the indentation was effectively
meaningless.

I guess the distinction here is between generated code vs hand-written
code; I want to warn about misleading indentation in the latter, but I
don't think people care so much about the former.

Is there a decent way for the compiler to know (or be told) that it's
dealing with autogenerated vs hand-written code?

> I wonder if some of the cases where you're not working, such as
> 
> if (...)
> bar;
> com;
> 
> Ought to be warned for using various -Wmisleading-indentation=X options.
> 
> Your call if you want to tackle that in the future.
> 
> 
> >
> > gcc/ChangeLog:
> > 	* Makefile.in (C_COMMON_OBJS): Add c-family/c-indentation.o.
> >
> > gcc/c-family/ChangeLog:
> > 	* c-common.h (warn_for_misleading_indentation): New prototype.
> > 	* c-indentation.c: New file.
> > 	* c.opt (Wmisleading-indentation): New option.
> >
> > gcc/c/ChangeLog:
> > 	* c-parser.c (c_parser_if_body): Add param "if_loc", use it
> > 	to add a call to warn_for_misleading_indentation.
> > 	(c_parser_else_body): Likewise, adding param "else_loc".
> > 	(c_parser_if_statement): Check for misleading indentation.
> > 	(c_parser_while_statement): Likewise.
> > 	(c_parser_for_statement): Likewise.
> >
> > gcc/cp/ChangeLog:
> > 	* parser.c (cp_parser_selection_statement): Add location and
> > 	guard_kind arguments to calls to
> > 	cp_parser_implicitly_scoped_statement.
> > 	(cp_parser_iteration_statement): Likewise for calls to
> > 	cp_parser_already_scoped_statement.
> > 	(cp_parser_implicitly_scoped_statement): Add "guard_loc" and
> > 	"guard_kind" params; use them to warn for misleading
> > 	indentation.
> > 	(cp_parser_already_scoped_statement): Likewise.
> >
> > gcc/ChangeLog:
> > 	* doc/invoke.texi (Warning Options): Add -Wmisleading-indentation.
> > 	(-Wall): Add -Wmisleading-indentation.
> > 	(-Wmisleading-indentation): New option.
> >
> > gcc/testsuite/ChangeLog:
> > 	* c-c++-common/Wmisleading-indentation.c: New testcase.
> > 	* c-c++-common/Wmisleading-indentation-2.c: New testcase.
> > 	* c-c++-common/Wmisleading-indentation-2.md: New file.
> >
> > libcpp/ChangeLog:
> > 	* directives.c (do_line): Set seen_line_directive on line_table.
> > 	(do_linemarker): Likewise.
> > 	* include/line-map.h (struct line_maps): Add new field
> > 	"seen_line_directive".
> OK if you take it out of -Wall for now.  We can revisit it in -Wall 
> separately :-)

Thanks; I've removed the new warning from -Wall, making the appropriate
trivial doc changes, and committed it to trunk (r223098; attached).

I plan to try testing it on the linux kernel, and some other bodies of
code.

I'm thinking about scaling things up, by dusting off the
"mock-with-analysis" tool I wrote for my PyCon 2013 talk, which
simplifies the job of running a modified compiler on hundreds of srpm
rebuilds and harvesting the warnings and corresponding sources into a
database:
https://github.com/fedora-static-analysis/mock-with-analysis

I like the -Wmisleading-indentation=X idea.  One idea I considered was
suppressing the warning if there's an entirely blank line between the
clauses, so if we have:


   if (cond)
      foo ();

      bar ();

we could not warn at lower values of X (perhaps space vs tab issues?).

That said I suspect that the best course of action here is to try the
warning on some significant proportion of, say, Fedora 22, and go
through any warnings it emits to decide which are true, which are false,
and from the latter, tweak the logic accordingly.

Dave

[-- Attachment #2: r223098.patch --]
[-- Type: text/x-patch, Size: 46093 bytes --]

Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 223097)
+++ gcc/doc/invoke.texi	(revision 223098)
@@ -262,7 +262,8 @@
 -Wno-int-to-pointer-cast -Wno-invalid-offsetof @gol
 -Winvalid-pch -Wlarger-than=@var{len}  -Wunsafe-loop-optimizations @gol
 -Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
--Wmain -Wmaybe-uninitialized -Wmemset-transposed-args -Wmissing-braces @gol
+-Wmain -Wmaybe-uninitialized -Wmemset-transposed-args @gol
+-Wmisleading-indentation -Wmissing-braces @gol
 -Wmissing-field-initializers -Wmissing-include-dirs @gol
 -Wno-multichar  -Wnonnull  -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
 -Wodr  -Wno-overflow  -Wopenmp-simd @gol
@@ -3766,6 +3767,45 @@
 is enabled by default in C++ and is enabled by either @option{-Wall}
 or @option{-Wpedantic}.
 
+@item -Wmisleading-indentation @r{(C and C++ only)}
+@opindex Wmisleading-indentation
+@opindex Wno-misleading-indentation
+Warn when the indentation of the code does not reflect the block structure.
+Specifically, a warning is issued for @code{if}, @code{else}, @code{while}, and
+@code{for} clauses with a guarded statement that does not use braces,
+followed by an unguarded statement with the same indentation.
+
+This warning is disabled by default.
+
+In the following example, the call to ``bar'' is misleadingly indented as
+if it were guarded by the ``if'' conditional.
+
+@smallexample
+  if (some_condition ())
+    foo ();
+    bar ();  /* Gotcha: this is not guarded by the "if".  */
+@end smallexample
+
+In the case of mixed tabs and spaces, the warning uses the
+@option{-ftabstop=} option to determine if the statements line up
+(defaulting to 8).
+
+The warning is not issued for code involving multiline preprocessor logic
+such as the following example.
+
+@smallexample
+  if (flagA)
+    foo (0);
+#if SOME_CONDITION_THAT_DOES_NOT_HOLD
+  if (flagB)
+#endif
+    foo (1);
+@end smallexample
+
+The warning is not issued after a @code{#line} directive, since this
+typically indicates autogenerated code, and no assumptions can be made
+about the layout of the file that the directive references.
+
 @item -Wmissing-braces
 @opindex Wmissing-braces
 @opindex Wno-missing-braces
Index: gcc/c-family/c-indentation.c
===================================================================
--- gcc/c-family/c-indentation.c	(revision 0)
+++ gcc/c-family/c-indentation.c	(revision 223098)
@@ -0,0 +1,384 @@
+/* Implementation of -Wmisleading-indentation
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+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"
+#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 "stor-layout.h"
+#include "input.h"
+#include "c-common.h"
+
+extern cpp_options *cpp_opts;
+
+/* Convert libcpp's notion of a column (a 1-based char count) to
+   the "visual column" (0-based column, respecting tabs), by reading the
+   relevant line.
+   Returns true if a conversion was possible, writing the result to OUT,
+   otherwise returns false.  */
+
+static bool
+get_visual_column (expanded_location exploc, unsigned int *out)
+{
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+  if (!line)
+    return false;
+  unsigned int vis_column = 0;
+  for (int i = 1; i < exploc.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++;
+    }
+
+  *out = vis_column;
+  return true;
+}
+
+/* Is the token at LOC the first non-whitespace on its line?
+   Helper function for should_warn_for_misleading_indentation.  */
+
+static bool
+is_first_nonwhitespace_on_line (expanded_location exploc)
+{
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+
+   /* If we can't determine it, return false so that we don't issue a
+      warning.  This is sometimes the case for input files
+      containing #line directives, and these are often for autogenerated
+      sources (e.g. from .md files), where it's not clear that it's
+      meaningful to look at indentation.  */
+  if (!line)
+    return false;
+
+  for (int i = 1; i < exploc.column; i++)
+    {
+      unsigned char ch = line[i - 1];
+      if (!ISSPACE (ch))
+	return false;
+    }
+  return true;
+}
+
+/* Does the given source line appear to contain a #if directive?
+   (or #ifdef/#ifndef).  Ignore the possibility of it being inside a
+   comment, for simplicity.
+   Helper function for detect_preprocessor_logic.  */
+
+static bool
+line_contains_hash_if (const char *file, int line_num)
+{
+  expanded_location exploc;
+  exploc.file = file;
+  exploc.line = line_num;
+  exploc.column = 1;
+
+  int line_len;
+  const char *line = location_get_source_line (exploc, &line_len);
+  if (!line)
+    return false;
+
+  int idx;
+
+  /* Skip leading whitespace.  */
+  for (idx = 0; idx < line_len; idx++)
+    if (!ISSPACE (line[idx]))
+      break;
+  if (idx == line_len)
+    return false;
+
+  /* Require a '#' character.  */
+  if (line[idx] != '#')
+    return false;
+  idx++;
+
+  /* Skip whitespace.  */
+  while (idx < line_len)
+    {
+      if (!ISSPACE (line[idx]))
+	break;
+      idx++;
+    }
+
+  /* Match #if/#ifdef/#ifndef.  */
+  if (idx + 2 <= line_len)
+    if (line[idx] == 'i')
+      if (line[idx + 1] == 'f')
+	return true;
+
+  return false;
+}
+
+
+/* Determine if there is preprocessor logic between
+   BODY_EXPLOC and NEXT_STMT_EXPLOC, to ensure that we don't
+   issue a warning for cases like this:
+
+	if (flagA)
+	  foo ();
+	  ^ BODY_EXPLOC
+      #if SOME_CONDITION_THAT_DOES_NOT_HOLD
+	if (flagB)
+      #endif
+	  bar ();
+	  ^ NEXT_STMT_EXPLOC
+
+   despite "bar ();" being visually aligned below "foo ();" and
+   being (as far as the parser sees) the next token.
+
+   Return true if such logic is detected.  */
+
+static bool
+detect_preprocessor_logic (expanded_location body_exploc,
+			   expanded_location next_stmt_exploc)
+{
+  gcc_assert (next_stmt_exploc.file == body_exploc.file);
+  gcc_assert (next_stmt_exploc.line > body_exploc.line);
+
+  if (next_stmt_exploc.line - body_exploc.line < 4)
+    return false;
+
+  /* Is there a #if/#ifdef/#ifndef directive somewhere in the lines
+     between the given locations?
+
+     This is something of a layering violation, but by necessity,
+     given the nature of what we're testing for.  For example,
+     in theory we could be fooled by a #if within a comment, but
+     it's unlikely to matter.  */
+  for (int line = body_exploc.line + 1; line < next_stmt_exploc.line; line++)
+    if (line_contains_hash_if (body_exploc.file, line))
+      return true;
+
+  /* Not found.  */
+  return false;
+}
+
+
+/* Helper function for warn_for_misleading_indentation; see
+   description of that function below.  */
+
+static bool
+should_warn_for_misleading_indentation (location_t guard_loc,
+					location_t body_loc,
+					location_t next_stmt_loc,
+					enum cpp_ttype next_tok_type)
+{
+  /* Don't attempt to compare the indentation of BODY_LOC and NEXT_STMT_LOC
+     if either are within macros.  */
+  if (linemap_location_from_macro_expansion_p (line_table, body_loc)
+      || linemap_location_from_macro_expansion_p (line_table, next_stmt_loc))
+    return false;
+
+  /* Don't attempt to compare indentation if #line or # 44 "file"-style
+     directives are present, suggesting generated code.
+
+     All bets are off if these are present: the file that the #line
+     directive could have an entirely different coding layout to C/C++
+     (e.g. .md files).
+
+     To determine if a #line is present, in theory we could look for a
+     map with reason == LC_RENAME_VERBATIM.  However, if there has
+     subsequently been a long line requiring a column number larger than
+     that representable by the original LC_RENAME_VERBATIM map, then
+     we'll have a map with reason LC_RENAME.
+     Rather than attempting to search all of the maps for a
+     LC_RENAME_VERBATIM, instead we have libcpp set a flag whenever one
+     is seen, and we check for the flag here.
+  */
+  if (line_table->seen_line_directive)
+    return false;
+
+  if (next_tok_type == CPP_CLOSE_BRACE)
+    return false;
+
+  /* Don't warn here about spurious semicolons.  */
+  if (next_tok_type == CPP_SEMICOLON)
+    return false;
+
+  expanded_location body_exploc
+    = expand_location_to_spelling_point (body_loc);
+  expanded_location next_stmt_exploc
+    = expand_location_to_spelling_point (next_stmt_loc);
+
+  /* They must be in the same file.  */
+  if (next_stmt_exploc.file != body_exploc.file)
+    return false;
+
+  /* If NEXT_STMT_LOC and BODY_LOC are on the same line, consider
+     the location of the guard.
+
+     Cases where we want to issue a warning:
+
+       if (flag)
+         foo ();  bar ();
+                  ^ WARN HERE
+
+       if (flag) foo (); bar ();
+                         ^ WARN HERE
+
+     Cases where we don't want to issue a warning:
+
+       various_code (); if (flag) foo (); bar (); more_code ();
+                                          ^ DON'T WARN HERE.  */
+  if (next_stmt_exploc.line == body_exploc.line)
+    {
+      expanded_location guard_exploc
+	= expand_location_to_spelling_point (guard_loc);
+      if (guard_exploc.file != body_exploc.file)
+	return true;
+      if (guard_exploc.line < body_exploc.line)
+	/* The guard is on a line before a line that contains both
+	   the body and the next stmt.  */
+	return true;
+      else if (guard_exploc.line == body_exploc.line)
+	{
+	  /* They're all on the same line.  */
+	  gcc_assert (guard_exploc.file == next_stmt_exploc.file);
+	  gcc_assert (guard_exploc.line == next_stmt_exploc.line);
+	  /* Heuristic: only warn if the guard is the first thing
+	     on its line.  */
+	  if (is_first_nonwhitespace_on_line (guard_exploc))
+	    return true;
+	}
+    }
+
+  /* If NEXT_STMT_LOC is on a line after BODY_LOC, consider
+     their relative locations, and of the guard.
+
+     Cases where we want to issue a warning:
+        if (flag)
+          foo ();
+          bar ();
+          ^ WARN HERE
+
+     Cases where we don't want to issue a warning:
+        if (flag)
+        foo ();
+        bar ();
+        ^ DON'T WARN HERE (autogenerated code?)
+
+	if (flagA)
+	  foo ();
+      #if SOME_CONDITION_THAT_DOES_NOT_HOLD
+	if (flagB)
+      #endif
+	  bar ();
+	  ^ DON'T WARN HERE
+  */
+  if (next_stmt_exploc.line > body_exploc.line)
+    {
+      /* Determine if GUARD_LOC and NEXT_STMT_LOC are aligned on the same
+	 "visual column"...  */
+      unsigned int next_stmt_vis_column;
+      unsigned int body_vis_column;
+      /* If we can't determine it, don't issue a warning.  This is sometimes
+	 the case for input files containing #line directives, and these
+	 are often for autogenerated sources (e.g. from .md files), where
+	 it's not clear that it's meaningful to look at indentation.  */
+      if (!get_visual_column (next_stmt_exploc, &next_stmt_vis_column))
+	return false;
+      if (!get_visual_column (body_exploc, &body_vis_column))
+	return false;
+      if (next_stmt_vis_column == body_vis_column)
+	{
+	  /* Don't warn if they aren't aligned on the same column
+	     as the guard itself (suggesting autogenerated code that
+	     doesn't bother indenting at all).  */
+	  expanded_location guard_exploc
+	    = expand_location_to_spelling_point (guard_loc);
+	  unsigned int guard_vis_column;
+	  if (!get_visual_column (guard_exploc, &guard_vis_column))
+	    return false;
+	  if (guard_vis_column == body_vis_column)
+	    return false;
+
+	  /* Don't warn if there is multiline preprocessor logic between
+	     the two statements. */
+	  if (detect_preprocessor_logic (body_exploc, next_stmt_exploc))
+	    return false;
+
+	  /* Otherwise, they are visually aligned: issue a warning.  */
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Called by the C/C++ frontends when we have a guarding statement at
+   GUARD_LOC containing a statement at BODY_LOC, where the block wasn't
+   written using braces, like this:
+
+     if (flag)
+       foo ();
+
+   along with the location of the next token, at NEXT_STMT_LOC,
+   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:
+
+     GUARD_LOC
+     |
+     V
+     if (flag)
+       foo (); <- BODY_LOC
+       bar (); <- NEXT_STMT_LOC
+
+   In the above, "bar ();" isn't guarded by the "if", but
+   is indented to misleadingly suggest that it is in the same
+   block as "foo ();".
+
+   GUARD_KIND identifies the kind of clause e.g. "if", "else" etc.  */
+
+void
+warn_for_misleading_indentation (location_t guard_loc,
+				 location_t body_loc,
+				 location_t next_stmt_loc,
+				 enum cpp_ttype next_tok_type,
+				 const char *guard_kind)
+{
+  if (should_warn_for_misleading_indentation (guard_loc,
+					      body_loc,
+					      next_stmt_loc,
+					      next_tok_type))
+    if (warning_at (next_stmt_loc, OPT_Wmisleading_indentation,
+		    "statement is indented as if it were guarded by..."))
+      inform (guard_loc,
+	      "...this %qs clause, but it is not", guard_kind);
+}
Index: gcc/c-family/c.opt
===================================================================
--- gcc/c-family/c.opt	(revision 223097)
+++ gcc/c-family/c.opt	(revision 223098)
@@ -553,6 +553,10 @@
 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
Index: gcc/c-family/ChangeLog
===================================================================
--- gcc/c-family/ChangeLog	(revision 223097)
+++ gcc/c-family/ChangeLog	(revision 223098)
@@ -1,3 +1,9 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* c-common.h (warn_for_misleading_indentation): New prototype.
+	* c-indentation.c: New file.
+	* c.opt (Wmisleading-indentation): New option.
+
 2015-05-12  Tom de Vries  <tom@codesourcery.com>
 
 	PR tree-optimization/66010
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 223097)
+++ gcc/c-family/c-common.h	(revision 223098)
@@ -1429,4 +1429,12 @@
 extern tree cilk_for_number_of_iterations (tree);
 extern bool check_no_cilk (tree, const char *, const char *,
 		           location_t loc = UNKNOWN_LOCATION);
+/* In c-indentation.c.  */
+extern void
+warn_for_misleading_indentation (location_t guard_loc,
+				 location_t body_loc,
+				 location_t next_stmt_loc,
+				 enum cpp_ttype next_tok_type,
+				 const char *guard_kind);
+
 #endif /* ! GCC_C_COMMON_H */
Index: gcc/c/ChangeLog
===================================================================
--- gcc/c/ChangeLog	(revision 223097)
+++ gcc/c/ChangeLog	(revision 223098)
@@ -1,3 +1,12 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* c-parser.c (c_parser_if_body): Add param "if_loc", use it
+	to add a call to warn_for_misleading_indentation.
+	(c_parser_else_body): Likewise, adding param "else_loc".
+	(c_parser_if_statement): Check for misleading indentation.
+	(c_parser_while_statement): Likewise.
+	(c_parser_for_statement): Likewise.
+
 2015-05-08  Marek Polacek  <polacek@redhat.com>
 
 	PR c/64918
Index: gcc/c/c-parser.c
===================================================================
--- gcc/c/c-parser.c	(revision 223097)
+++ gcc/c/c-parser.c	(revision 223098)
@@ -5185,7 +5185,7 @@
    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 +5203,15 @@
   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 (!c_parser_next_token_is_keyword (parser, RID_ELSE))
+	warn_for_misleading_indentation (if_loc, body_loc,
+					 c_parser_peek_token (parser)->location,
+					 c_parser_peek_token (parser)->type,
+					 "if");
+    }
+
   return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
@@ -5212,9 +5220,9 @@
    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_loc)
 {
-  location_t else_loc = c_parser_peek_token (parser)->location;
+  location_t body_loc = c_parser_peek_token (parser)->location;
   tree block = c_begin_compound_stmt (flag_isoc99);
   c_parser_all_labels (parser);
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
@@ -5227,8 +5235,14 @@
       c_parser_consume_token (parser);
     }
   else
-    c_parser_statement_after_labels (parser);
-  return c_end_compound_stmt (else_loc, block, flag_isoc99);
+    {
+      c_parser_statement_after_labels (parser);
+      warn_for_misleading_indentation (else_loc, body_loc,
+				       c_parser_peek_token (parser)->location,
+				       c_parser_peek_token (parser)->type,
+				       "else");
+    }
+  return c_end_compound_stmt (body_loc, block, flag_isoc99);
 }
 
 /* Parse an if statement (C90 6.6.4, C99 6.8.4).
@@ -5250,6 +5264,7 @@
   tree if_stmt;
 
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
+  location_t 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;
@@ -5261,12 +5276,13 @@
     }
   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_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_loc);
     }
   else
     second_body = NULL_TREE;
@@ -5346,6 +5362,7 @@
   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_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 +5379,16 @@
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (while_loc, body_loc,
+				   c_parser_peek_token (parser)->location,
+				   c_parser_peek_token (parser)->type,
+				   "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;
@@ -5640,7 +5666,16 @@
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
   c_cont_label = NULL_TREE;
+
+  location_t body_loc = UNKNOWN_LOCATION;
+  if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE)
+    body_loc = c_parser_peek_token (parser)->location;
   body = c_parser_c99_block_statement (parser);
+  warn_for_misleading_indentation (for_loc, body_loc,
+				   c_parser_peek_token (parser)->location,
+				   c_parser_peek_token (parser)->type,
+				   "for");
+
   if (is_foreach_statement)
     objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
   else
Index: gcc/ChangeLog
===================================================================
--- gcc/ChangeLog	(revision 223097)
+++ gcc/ChangeLog	(revision 223098)
@@ -1,3 +1,9 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* doc/invoke.texi (Warning Options): Add -Wmisleading-indentation.
+	(-Wmisleading-indentation): New option.
+	* Makefile.in (C_COMMON_OBJS): Add c-family/c-indentation.o.
+
 2015-05-12  Uros Bizjak  <ubizjak@gmail.com>
 
 	* config/alpha/alpha.h (TARGET_SUPPORTS_WIDE_INT): New define.
Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 223097)
+++ gcc/testsuite/ChangeLog	(revision 223098)
@@ -1,3 +1,9 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* c-c++-common/Wmisleading-indentation.c: New testcase.
+	* c-c++-common/Wmisleading-indentation-2.c: New testcase.
+	* c-c++-common/Wmisleading-indentation-2.md: New file.
+
 2015-05-12 Sandra Loosemore <sandra@codesourcery.com>
 
 	* gcc.target/nios2/nios2-trap-insn.c: Expect "trap" instead of
Index: gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md
===================================================================
--- gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md	(revision 0)
+++ gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md	(revision 223098)
@@ -0,0 +1,46 @@
+;; Support file for testcase Wmisleading-indentation.c
+;; Adapted from gcc/config/i386/i386.md
+(define_attr "cpu" "none,pentium,pentiumpro,geode,k6,athlon,k8,core2,nehalem,
+		    atom,slm,generic,amdfam10,bdver1,bdver2,bdver3,bdver4,
+		    btver2,knl"
+  (const (symbol_ref "ix86_schedule")))
+
+;; A basic instruction type.  Refinements due to arguments to be
+;; provided in other attributes.
+(define_attr "type"
+  "other,multi,
+   alu,alu1,negnot,imov,imovx,lea,
+   incdec,ishift,ishiftx,ishift1,rotate,rotatex,rotate1,
+   imul,imulx,idiv,icmp,test,ibr,setcc,icmov,
+   push,pop,call,callv,leave,
+   str,bitmanip,
+   fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,
+   fxch,fistp,fisttp,frndint,
+   sse,ssemov,sseadd,sseadd1,sseiadd,sseiadd1,
+   ssemul,sseimul,ssediv,sselog,sselog1,
+   sseishft,sseishft1,ssecmp,ssecomi,
+   ssecvt,ssecvt1,sseicvt,sseins,
+   sseshuf,sseshuf1,ssemuladd,sse4arg,
+   lwp,mskmov,msklog,
+   mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft,
+   mpxmov,mpxmk,mpxchk,mpxld,mpxst"
+  (const_string "other"))
+
+;; Main data type used by the insn
+(define_attr "mode"
+  "unknown,none,QI,HI,SI,DI,TI,OI,XI,SF,DF,XF,TF,V16SF,V8SF,V4DF,V4SF,
+  V2DF,V2SF,V1DF,V8DF"
+  (const_string "unknown"))
+
+;; The CPU unit operations uses.
+(define_attr "unit" "integer,i387,sse,mmx,unknown"
+  (cond [(eq_attr "type" "fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,
+			  fxch,fistp,fisttp,frndint")
+	   (const_string "i387")
+	 (eq_attr "type" "sse,ssemov,sseadd,sseadd1,sseiadd,sseiadd1,
+			  ssemul,sseimul,ssediv,sselog,sselog1,
+			  sseishft,sseishft1,ssecmp,ssecomi,
+			  ssecvt,ssecvt1,sseicvt,sseins,
+			  sseshuf,sseshuf1,ssemuladd,sse4arg,mskmov")
+	   (const_string "sse")
+	 (const_string "integer")))
Index: gcc/testsuite/c-c++-common/Wmisleading-indentation.c
===================================================================
--- gcc/testsuite/c-c++-common/Wmisleading-indentation.c	(revision 0)
+++ gcc/testsuite/c-c++-common/Wmisleading-indentation.c	(revision 223098)
@@ -0,0 +1,431 @@
+/* { dg-options "-Wmisleading-indentation -Wall" } */
+/* { dg-do compile } */
+
+extern int foo (int);
+extern int bar (int, int);
+extern int flagA;
+extern int flagB;
+extern int flagC;
+extern int flagD;
+
+int
+fn_1 (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;
+}
+
+int
+fn_2 (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;
+}
+
+int
+fn_3 (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;
+}
+
+void
+fn_4 (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..." } */
+}
+
+void
+fn_5 (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..." } */
+}
+
+/* Based on CVE-2014-1266 aka "goto fail" */
+int fn_6 (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;
+}
+
+int fn_7 (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..." } */
+      t++;
+    }
+  return p + q + r + s + t;
+}
+
+int fn_8 (int a, int b, int c)
+{
+  /* This should *not* be flagged as misleading indentation.  */
+  if (a) return b; else return c;
+}
+
+void fn_9 (int flag)
+{
+  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    foo (0);
+    foo (1); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_10 (int flag)
+{
+  if (flag) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    if (flag / 2)
+      {
+        foo (0);
+        foo (1);
+      }
+    foo (2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+  foo (3);
+}
+
+void fn_11 (void)
+{
+  if (flagA)
+    if (flagB)
+      if (flagC) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+        foo (0);
+        bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_12 (void)
+{
+  if (flagA)
+    if (flagB) /* { dg-message "5: ...this 'if' clause, but it is not" } */
+      if (flagC)
+        foo (0);
+      bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_13 (void)
+{
+  if (flagA) /* { dg-message "3: ...this 'if' clause, but it is not" } */
+    if (flagB)
+      if (flagC)
+        foo (0);
+    bar (1, 2); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+#define FOR_EACH(VAR, START, STOP) \
+  for ((VAR) = (START); (VAR) < (STOP); (VAR++)) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+
+void fn_14 (void)
+{
+  int i;
+  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
+    foo (i);
+    bar (i, i); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+#undef FOR_EACH
+
+#define FOR_EACH(VAR, START, STOP) for ((VAR) = (START); (VAR) < (STOP); (VAR++)) /* { dg-message "36: ...this 'for' clause, but it is not" } */
+void fn_15 (void)
+{
+  int i;
+  FOR_EACH (i, 0, 10) /* { dg-message "3: in expansion of macro" } */
+    foo (i);
+    bar (i, i); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+#undef FOR_EACH
+
+void fn_16_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA)
+      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+        foo (0);
+        foo (1); /* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_16_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA)
+      if (flagB) /* { dg-message "7: ...this 'if' clause, but it is not" } */
+	foo (0);
+	foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_17_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+    while (flagA)
+      if (flagB)
+        foo (0);
+    foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_17_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++) /* { dg-message "3: ...this 'for' clause, but it is not" } */
+    while (flagA)
+      if (flagB)
+	foo (0);
+    foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_18_spaces (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" } */
+      if (flagB)
+        foo (0);
+      foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+void fn_18_tabs (void)
+{
+  int i;
+  for (i = 0; i < 10; i++)
+    while (flagA) /* { dg-message "5: ...this 'while' clause, but it is not" } */
+      if (flagB)
+	foo (0);
+      foo (1);/* { dg-warning "statement is indented as if it were guarded by..." } */
+}
+
+/* This shouldn't lead to a warning.  */
+int fn_19 (void) { if (flagA) return 1; else return 0; }
+
+/* A deeply-nested mixture of spaces and tabs, adapted from
+   c-c++-common/pr60101.c.
+   This should not lead to a warning.  */
+void
+fn_20 (unsigned int l)
+{
+  unsigned int i;
+
+  for (i = 0; i < 10; i++)
+    {
+      unsigned int n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11;
+
+      for (n0 = 0; n0 < l; n0++)
+	for (n1 = 0; n1 < l; n1++)
+	  for (n2 = 0; n2 < l; n2++)
+	    for (n3 = 0; n3 < l; n3++)
+	      for (n4 = 0; n4 < l; n4++)
+		for (n5 = 0; n5 < l; n5++)
+		  for (n6 = 0; n6 < l; n6++)
+		    for (n7 = 0; n7 < l; n7++)
+		      for (n8 = 0; n8 < l; n8++)
+			for (n9 = 0; n9 < l; n9++)
+			  for (n10 = 0; n10 < l; n10++)
+			    for (n11 = 0; n11 < l; n11++)
+			      {
+				if (flagA)
+				  foo (0);
+				foo (1);
+			      }
+      foo (2);
+    }
+}
+
+/* Another nested mix of tabs and spaces that shouldn't lead to a warning,
+   with a preprocessor directive thrown in for good measure
+   (adapted from libgomp/loop.c: gomp_loop_init).  */
+void fn_21 (void)
+{
+  foo (0);
+  if (flagA)
+    {
+      foo (1);
+
+#if 1
+      {
+	foo (2);
+	if (flagB)
+	  {
+	    if (flagC)
+	      foo (3);
+	    else
+	      foo (4);
+	  }
+	else if (flagD)
+	  foo (5);
+	else
+	  foo (6);
+      }
+#endif
+    }
+}
+
+/* The conditionals within the following macros shouldn't be warned about.
+   Adapted from libgomp/driver.c: gomp_load_plugin_for_device.  */
+int fn_22 (void)
+{
+  int err = 0;
+
+#define DLSYM()							\
+  do									\
+    {									\
+      err = foo (0);							\
+      if (err)								\
+	goto out;							\
+    }									\
+  while (0)
+#define DLSYM_OPT()							\
+  do									\
+    {									\
+      err = foo (1);							\
+      if (err)								\
+        foo (2);							\
+      else								\
+        foo (3);							\
+      foo (4);								\
+    }									\
+  while (0)
+  DLSYM ();
+  DLSYM_OPT ();
+#undef DLSYM
+#undef DLSYM_OPT
+
+ out:
+  return err;
+}
+
+/* This shouldn't be warned about.  */
+void fn_23 (void) { foo (0); foo (1); if (flagA) foo (2); foo (3); foo (4); }
+
+/* Code that simply doesn't bother indenting anywhere (e.g. autogenerated
+   code) shouldn't be warned about.  */
+void fn_24 (void)
+{
+  foo (0);
+  if (flagA)
+  foo (1);
+  foo (2);
+}
+
+/* Adapted from libiberty/regex.c; an example of a conditional in a
+   macro where the successor statement begins with a macro arg:
+
+	    if (num < 0)
+	      num = 0;
+	    num = num * 10 + c - '0';
+	    ^ this successor statement
+
+   and hence "num" has a spelling location at the argument of the
+   macro usage site ("lower_bound"), we want the definition of the
+   parameter ("num") for the indentation comparison to be meaninful.
+
+   This should not generate a misleading indentation warning.  */
+
+# define GET_UNSIGNED_NUMBER(num) \
+  {									\
+    while (flagA)							\
+      {									\
+	if (flagB)						\
+	  {								\
+	    if (num < 0)						\
+	      num = 0;							\
+	    num = num * 10 + c - '0';					\
+	  }								\
+      }									\
+  }
+void fn_25 (int c, int lower_bound, int upper_bound)
+{
+  GET_UNSIGNED_NUMBER (lower_bound);
+}
+#undef GET_UNSIGNED_NUMBER
+
+/* Example adapted from libdecnumber/decNumber.c:decExpOp that shouldn't
+   trigger a warning.  */
+void fn_26 (void)
+{
+  if (flagA) {
+    if (flagB) foo (0); }
+  foo (1);
+}
+
+/* Ensure that we don't get confused by mixed tabs and spaces; the line
+   "foo (1);" has leading spaces before a tab, but this should not
+   lead to a warning from -Wmisleading-indentation.  */
+void fn_27 (void)
+{
+	      if (flagA)
+		foo (0);
+  	      foo (1);
+}
+
+/* Example adapted from gcc/cgraph.h:symtab_node::get_availability of
+   a spurious trailing semicolon that shouldn't generate a warning.  */
+void fn_28 (void)
+{
+  if (flagA)
+    foo (0);
+  else
+    foo (1);;
+}
+
+/* However, other kinds of spurious semicolons can be a problem.  Sadly
+   we don't yet report for the misleading-indented "foo (1);" in the
+   following, due to the spurious semicolon.  */
+void fn_29 (void)
+{
+  if (flagA)
+    if (flagB)
+      foo (0);;
+    foo (1);
+}
+
+/* Adapted from usage site of #ifdef HAVE_cc0.  This should not lead
+   to a warning from -Wmisleading-indentation.  */
+void fn_30 (void)
+{
+  if (flagA)
+    foo (0);
+#if SOME_CONDITION_THAT_DOES_NOT_HOLD
+  if (flagB)
+#endif
+    foo (1);
+}
+
+/* This shouldn't lead to a warning.  */
+void fn_31 (void)
+{
+  if (flagA)
+    foo (0);
+  else if (flagB)
+    foo (1);
+  else if (flagC)
+    foo (2);
+  else
+    foo (3);
+}
Index: gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c
===================================================================
--- gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c	(revision 0)
+++ gcc/testsuite/c-c++-common/Wmisleading-indentation-2.c	(revision 223098)
@@ -0,0 +1,56 @@
+/* { dg-options "-Wmisleading-indentation" } */
+/* { dg-do compile } */
+
+/* Based on get_attr_athlon_decode from the generated insn-attrtab.c
+   for x86_64.
+   A #line directive, followed by a very long line to ensure that
+   we're in a fresh line_map.
+
+   This should not generate a misleading indentation warning.
+
+   This needs to be in its own file since -Wmisleading-indentation stops
+   after seeing a #line directive.  */
+void fn ()
+{
+  switch (0)
+    {
+#line 6 "../../../../src/gcc/testsuite/c-c++-common/Wmisleading-indentation-2.md"
+    case 0:
+      if (0)
+        {
+	  return;
+        }
+
+    case 1:
+      if (0)
+        {
+	  return;
+        }
+      else
+        {
+	  return;
+        }
+
+      /**********************************************************************************************************************************/
+      if (0)
+        {
+	  return;
+        }
+      else if (0)
+        {
+	  return;
+        }
+      else if (0)
+        {
+	  return;
+        }
+      else
+        {
+	  return;
+        }
+
+    default:
+      return;
+
+    }
+}
Index: gcc/cp/ChangeLog
===================================================================
--- gcc/cp/ChangeLog	(revision 223097)
+++ gcc/cp/ChangeLog	(revision 223098)
@@ -1,3 +1,15 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* parser.c (cp_parser_selection_statement): Add location and
+	guard_kind arguments to calls to
+	cp_parser_implicitly_scoped_statement.
+	(cp_parser_iteration_statement): Likewise for calls to
+	cp_parser_already_scoped_statement.
+	(cp_parser_implicitly_scoped_statement): Add "guard_loc" and
+	"guard_kind" params; use them to warn for misleading
+	indentation.
+	(cp_parser_already_scoped_statement): Likewise.
+
 2015-05-11  Jan Hubicka  <hubicka@ucw.cz>
 
 	* class.c (fixup_type_variants): Do not copy TYPE_METHODS
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 223097)
+++ gcc/cp/parser.c	(revision 223098)
@@ -2065,9 +2065,9 @@
   (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 +10174,8 @@
 		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 +10185,8 @@
 						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 +10200,8 @@
 		    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 +10241,8 @@
 	    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 +10787,7 @@
 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 +10797,8 @@
   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 +10822,7 @@
 	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 +10837,7 @@
 	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 +10867,7 @@
 
 	/* 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 +11136,9 @@
    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;
 
@@ -11152,9 +11161,18 @@
       /* Create a compound-statement.  */
       statement = begin_compound_stmt (0);
       /* Parse the dependent-statement.  */
+      location_t body_loc = cp_lexer_peek_token (parser->lexer)->location;
       cp_parser_statement (parser, NULL_TREE, false, if_p);
       /* Finish the dummy compound-statement.  */
       finish_compound_stmt (statement);
+      cp_token *next_tok = cp_lexer_peek_token (parser->lexer);
+      if (next_tok->keyword != RID_ELSE)
+        {
+          location_t next_stmt_loc = next_tok->location;
+          warn_for_misleading_indentation (guard_loc, body_loc,
+                                           next_stmt_loc, next_tok->type,
+                                           guard_kind);
+        }
     }
 
   /* Return the statement.  */
@@ -11167,11 +11185,20 @@
    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);
+    {
+      location_t body_loc = cp_lexer_peek_token (parser->lexer)->location;
+      cp_parser_statement (parser, NULL_TREE, false, NULL);
+      cp_token *next_tok = cp_lexer_peek_token (parser->lexer);
+      location_t next_stmt_loc = next_tok->location;
+      warn_for_misleading_indentation (guard_loc, body_loc,
+                                       next_stmt_loc, next_tok->type,
+                                       guard_kind);
+    }
   else
     {
       /* Avoid calling cp_parser_compound_statement, so that we
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 223097)
+++ gcc/Makefile.in	(revision 223098)
@@ -1145,8 +1145,8 @@
 
 # Language-specific object files shared by all C-family front ends.
 C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \
-  c-family/c-format.o c-family/c-gimplify.o c-family/c-lex.o \
-  c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o \
+  c-family/c-format.o c-family/c-gimplify.o c-family/c-indentation.o \
+  c-family/c-lex.o c-family/c-omp.o c-family/c-opts.o c-family/c-pch.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 \
Index: libcpp/directives.c
===================================================================
--- libcpp/directives.c	(revision 223097)
+++ libcpp/directives.c	(revision 223098)
@@ -911,7 +911,7 @@
 static void
 do_line (cpp_reader *pfile)
 {
-  const struct line_maps *line_table = pfile->line_table;
+  struct line_maps *line_table = pfile->line_table;
   const struct line_map *map = LINEMAPS_LAST_ORDINARY_MAP (line_table);
 
   /* skip_rest_of_line() may cause line table to be realloc()ed so note down
@@ -965,6 +965,7 @@
   skip_rest_of_line (pfile);
   _cpp_do_file_change (pfile, LC_RENAME_VERBATIM, new_file, new_lineno,
 		       map_sysp);
+  line_table->seen_line_directive = true;
 }
 
 /* Interpret the # 44 "file" [flags] notation, which has slightly
@@ -973,7 +974,7 @@
 static void
 do_linemarker (cpp_reader *pfile)
 {
-  const struct line_maps *line_table = pfile->line_table;
+  struct line_maps *line_table = pfile->line_table;
   const struct line_map *map = LINEMAPS_LAST_ORDINARY_MAP (line_table);
   const cpp_token *token;
   const char *new_file = ORDINARY_MAP_FILE_NAME (map);
@@ -1052,6 +1053,7 @@
   pfile->line_table->highest_location--;
 
   _cpp_do_file_change (pfile, reason, new_file, new_lineno, new_sysp);
+  line_table->seen_line_directive = true;
 }
 
 /* Arrange the file_change callback.  pfile->line has changed to
Index: libcpp/include/line-map.h
===================================================================
--- libcpp/include/line-map.h	(revision 223097)
+++ libcpp/include/line-map.h	(revision 223098)
@@ -386,6 +386,9 @@
   /* The special location value that is used as spelling location for
      built-in tokens.  */
   source_location builtin_location;
+
+  /* True if we've seen a #line or # 44 "file" directive.  */
+  bool seen_line_directive;
 };
 
 /* Returns the pointer to the memory region where information about
Index: libcpp/ChangeLog
===================================================================
--- libcpp/ChangeLog	(revision 223097)
+++ libcpp/ChangeLog	(revision 223098)
@@ -1,3 +1,10 @@
+2015-05-12  David Malcolm  <dmalcolm@redhat.com>
+
+	* directives.c (do_line): Set seen_line_directive on line_table.
+	(do_linemarker): Likewise.
+	* include/line-map.h (struct line_maps): Add new field
+	"seen_line_directive".
+
 2015-05-08  Jason Merrill  <jason@redhat.com>
 
 	* include/cpplib.h: Add CPP_W_CXX11_COMPAT.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4)
  2015-05-12 21:28           ` David Malcolm
@ 2015-05-12 21:47             ` David Malcolm
  2015-08-18 14:13               ` PR67192 (Was: Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4)) Andreas Arnez
  0 siblings, 1 reply; 26+ messages in thread
From: David Malcolm @ 2015-05-12 21:47 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches

On Tue, 2015-05-12 at 17:21 -0400, David Malcolm wrote:
[...]
> Thanks; I've removed the new warning from -Wall, making the appropriate
> trivial doc changes, and committed it to trunk (r223098; attached).

...having bootstrapped&regrtested it on x86_64-unknown-linux-gnu
(Fedora 20).

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Fix logic error in Fortran OpenACC parsing
  2015-05-08 11:24                 ` Fix logic error in Fortran OpenACC parsing Ilmir Usmanov
@ 2015-07-27 14:37                   ` Thomas Schwinge
  0 siblings, 0 replies; 26+ messages in thread
From: Thomas Schwinge @ 2015-07-27 14:37 UTC (permalink / raw)
  To: gcc-patches, fortran
  Cc: Tobias Burnus, David Malcolm, Mikael Morin, Ilmir Usmanov

[-- Attachment #1: Type: text/plain, Size: 4852 bytes --]

Hi!

On Fri, 8 May 2015 14:24:15 +0300, Ilmir Usmanov <i.usmanov@samsung.com> wrote:
> On 06.05.2015 14:38, Thomas Schwinge wrote:
> > On Tue, 5 May 2015 15:38:03 -0400, David Malcolm <dmalcolm@redhat.com> wrote:
> >> On Wed, 2015-04-29 at 14:10 +0200, Mikael Morin wrote:
> >>> Le 29/04/2015 02:02, David Malcolm a écrit :
> >>>> diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
> >>>> index 2c7c554..30e4eab 100644
> >>>> --- a/gcc/fortran/parse.c
> >>>> +++ b/gcc/fortran/parse.c
> >>>> @@ -4283,7 +4283,7 @@ parse_oacc_structured_block (gfc_statement acc_st)
> >>>>   	unexpected_eof ();
> >>>>         else if (st != acc_end_st)
> >>>>   	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
> >>>> -	reject_statement ();
> >>>> +      reject_statement ();
> >>>>       }
> >>>>     while (st != acc_end_st);
> >>>>   
> >>> I think this one is a bug; there should be braces around 'gfc_error' and
> >>> 'reject_statement'.
> If 'st' is 'acc_end_st', as it shall be, the statement is rejected. So, 
> this is a bug.
> 
> >
> >>> At least that's the pattern in 'parse_oacc_loop', and how the
> >>> 'unexpected_statement' function is used.
> >> FWIW, Jeff had approved that patch, so I've committed the patch to trunk
> >> (as r222823), making the indentation reflect the block structure.
> >>
> >> Thomas:  should the
> >>        reject_statement ();
> >> call in the above be guarded by the
> >>       else if (st != acc_end_st)
> >> clause?
> > Indeed, this seems to be a bug that has been introduced very early in the
> > OpenACC Fortran front end development -- see how the
> > parse_oacc_structured_block function evolved in the patches posted in
> > <http://news.gmane.org/find-root.php?message_id=%3C52E1595D.9000007%40samsung.com%3E>
> > and following (Ilmir, CCed "just in case").  I also see that the
> > corresponding OpenMP code, parse_omp_structured_block, just calls
> > unexpected_statement, which Ilmir's initial patch also did, but at some
> > point, he then changed this to the current code: gfc_error followed by
> > reject_statement, as cited above -- I would guess for the reason to get a
> > better error message?  (Tobias, should this thus also be done for OpenMP,
> > and/or extend unexpected_statement accordingly?)
> That's true.
> I've checked abandoned openacc-1_0-branch and I used 
> unexpected_statement there (there still odd *_acc_* naming presents 
> instead of new-and-shiny *_oacc_* one), but, as you mentioned, I've 
> changed this for better error reporting... and introduced the bug.
> 
> >
> > And then, I'm a bit confused: is it "OK" that despite this presumed logic
> > error, which affects all (?) valid executions of this parsing code, we're
> > not running into any issues with the OpenACC Fortran front end test
> > cases?
> I think, this is OK, since this is an !$ACC END _smth_ statement and it 
> shall not present in the AST. So, it is abandoned later anyway ;)  (if I 
> remember correctly, during gfc_clear_new_st call). Although the bug does 
> not affect the logic, it is still a bug.
> 
> > OK for trunk?
>  From my point of view, OK.

Thanks for your review!  Nobody else had any comments, so I have now
committed my patch to trunk in r226246:

commit 1ed4ddb2615c807c239e1b3eb214655a4cc87f1a
Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Mon Jul 27 14:26:41 2015 +0000

    Fix logic error in Fortran OpenACC parsing
    
    	gcc/fortran/
    	* parse.c (parse_oacc_structured_block): Fix logic error.
    	Reported by Mikael Morin <mikael.morin@sfr.fr>.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@226246 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/fortran/ChangeLog |    5 +++++
 gcc/fortran/parse.c   |    6 ++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git gcc/fortran/ChangeLog gcc/fortran/ChangeLog
index 0ed6b9b..e5b7681 100644
--- gcc/fortran/ChangeLog
+++ gcc/fortran/ChangeLog
@@ -1,3 +1,8 @@
+2015-07-27  Thomas Schwinge  <thomas@codesourcery.com>
+
+	* parse.c (parse_oacc_structured_block): Fix logic error.
+	Reported by Mikael Morin <mikael.morin@sfr.fr>.
+
 2015-07-24  Mikael Morin  <mikael@gcc.gnu.org>
 
 	PR fortran/64986
diff --git gcc/fortran/parse.c gcc/fortran/parse.c
index 45ad63f..04b4c80 100644
--- gcc/fortran/parse.c
+++ gcc/fortran/parse.c
@@ -4383,8 +4383,10 @@ parse_oacc_structured_block (gfc_statement acc_st)
       if (st == ST_NONE)
 	unexpected_eof ();
       else if (st != acc_end_st)
-	gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
-      reject_statement ();
+	{
+	  gfc_error ("Expecting %s at %C", gfc_ascii_statement (acc_end_st));
+	  reject_statement ();
+	}
     }
   while (st != acc_end_st);
 


Grüße,
 Thomas

[-- Attachment #2: Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* PR67192 (Was: Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4))
  2015-05-12 21:47             ` David Malcolm
@ 2015-08-18 14:13               ` Andreas Arnez
  0 siblings, 0 replies; 26+ messages in thread
From: Andreas Arnez @ 2015-08-18 14:13 UTC (permalink / raw)
  To: David Malcolm; +Cc: Jeff Law, gcc-patches

On Tue, May 12 2015, David Malcolm wrote:

> On Tue, 2015-05-12 at 17:21 -0400, David Malcolm wrote:
> [...]
>> Thanks; I've removed the new warning from -Wall, making the appropriate
>> trivial doc changes, and committed it to trunk (r223098; attached).
>
> ...having bootstrapped&regrtested it on x86_64-unknown-linux-gnu
> (Fedora 20).

This patch introduces a regression that can cause a wrong line number to
be emitted for the backward-goto in a loop.  See:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67192

Some new FAILs in the GDB test suite (when using upstream GCC) are due
to this.  Any chance that this gets fixed soon?

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2015-08-18 14:13 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-16 15:08 [RFC stage 1] Proposed new warning: -Wmisleading-indentation David Malcolm
2015-04-16 17:27 ` Mike Stump
2015-04-21 16:13   ` David Malcolm
2015-04-21 16:40     ` Trevor Saunders
2015-04-21 16:43     ` Manuel López-Ibáñez
2015-04-21 17:05     ` Mike Stump
2015-04-21 18:14     ` Manuel López-Ibáñez
2015-04-21 23:35       ` David Malcolm
2015-04-28 23:50       ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) David Malcolm
2015-04-28 23:52         ` [PATCH 2/3] Fix spurious semicolons David Malcolm
2015-04-29  0:10           ` Jeff Law
2015-04-28 23:53         ` [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation David Malcolm
2015-04-29  0:25           ` Jeff Law
2015-04-29 12:35           ` Mikael Morin
2015-05-05 19:44             ` David Malcolm
2015-05-06 11:38               ` Fix logic error in Fortran OpenACC parsing (was: [PATCH 3/3] Fix indentation issues seen by -Wmisleading-indentation) Thomas Schwinge
2015-05-08 11:24                 ` Fix logic error in Fortran OpenACC parsing Ilmir Usmanov
2015-07-27 14:37                   ` Thomas Schwinge
2015-05-11 21:23         ` [PATCH 1/3] Implement -Wmisleading-indentation (v4) Jeff Law
2015-05-12 21:28           ` David Malcolm
2015-05-12 21:47             ` David Malcolm
2015-08-18 14:13               ` PR67192 (Was: Re: [PATCH 1/3] Implement -Wmisleading-indentation (v4)) Andreas Arnez
2015-04-16 18:29 ` [RFC stage 1] Proposed new warning: -Wmisleading-indentation Manuel López-Ibáñez
2015-04-17 14:42 ` Tom Tromey
2015-04-17 16:12   ` Manuel López-Ibáñez
2015-04-28 23:13     ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).