Commit 64b23c13 by David Malcolm Committed by David Malcolm

PR c/68187: fix overzealous -Wmisleading-indentation (comment #1)

gcc/c-family/ChangeLog:
	PR c/68187
	* c-indentation.c (get_visual_column): Move code to determine next
	tab stop to...
	(next_tab_stop): ...this new function.
	(line_contains_hash_if): Delete function.
	(detect_preprocessor_logic): Delete function.
	(get_first_nws_vis_column): New function.
	(detect_intervening_unindent): New function.
	(should_warn_for_misleading_indentation): Replace call to
	detect_preprocessor_logic with a call to
	detect_intervening_unindent.

gcc/testsuite/ChangeLog:
	PR c/68187
	* c-c++-common/Wmisleading-indentation.c (fn_42_a): New test
	function.
	(fn_42_b): Likewise.
	(fn_42_c): Likewise.

From-SVN: r233972
parent 729526f5
2016-03-04 David Malcolm <dmalcolm@redhat.com> 2016-03-04 David Malcolm <dmalcolm@redhat.com>
PR c/68187 PR c/68187
* c-indentation.c (get_visual_column): Move code to determine next
tab stop to...
(next_tab_stop): ...this new function.
(line_contains_hash_if): Delete function.
(detect_preprocessor_logic): Delete function.
(get_first_nws_vis_column): New function.
(detect_intervening_unindent): New function.
(should_warn_for_misleading_indentation): Replace call to
detect_preprocessor_logic with a call to
detect_intervening_unindent.
2016-03-04 David Malcolm <dmalcolm@redhat.com>
PR c/68187
* c-indentation.c (should_warn_for_misleading_indentation): When * c-indentation.c (should_warn_for_misleading_indentation): When
suppressing warnings about cases where the guard and body are on suppressing warnings about cases where the guard and body are on
the same column, only use the first non-whitespace column in place the same column, only use the first non-whitespace column in place
......
...@@ -26,6 +26,16 @@ along with GCC; see the file COPYING3. If not see ...@@ -26,6 +26,16 @@ along with GCC; see the file COPYING3. If not see
extern cpp_options *cpp_opts; extern cpp_options *cpp_opts;
/* Round up VIS_COLUMN to nearest tab stop. */
static unsigned int
next_tab_stop (unsigned int vis_column)
{
const unsigned int tab_width = cpp_opts->tabstop;
vis_column = ((vis_column + tab_width) / tab_width) * tab_width;
return vis_column;
}
/* Convert libcpp's notion of a column (a 1-based char count) to /* Convert libcpp's notion of a column (a 1-based char count) to
the "visual column" (0-based column, respecting tabs), by reading the the "visual column" (0-based column, respecting tabs), by reading the
relevant line. relevant line.
...@@ -77,11 +87,7 @@ get_visual_column (expanded_location exploc, location_t loc, ...@@ -77,11 +87,7 @@ get_visual_column (expanded_location exploc, location_t loc,
} }
if (ch == '\t') if (ch == '\t')
{ vis_column = next_tab_stop (vis_column);
/* 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 else
vis_column++; vis_column++;
} }
...@@ -93,54 +99,49 @@ get_visual_column (expanded_location exploc, location_t loc, ...@@ -93,54 +99,49 @@ get_visual_column (expanded_location exploc, location_t loc,
return true; return true;
} }
/* Does the given source line appear to contain a #if directive? /* Attempt to determine the first non-whitespace character in line LINE_NUM
(or #ifdef/#ifndef). Ignore the possibility of it being inside a of source line FILE.
comment, for simplicity.
Helper function for detect_preprocessor_logic. */ If this is possible, return true and write its "visual column" to
*FIRST_NWS.
Otherwise, return false, leaving *FIRST_NWS untouched. */
static bool static bool
line_contains_hash_if (const char *file, int line_num) get_first_nws_vis_column (const char *file, int line_num,
unsigned int *first_nws)
{ {
gcc_assert (first_nws);
int line_len; int line_len;
const char *line = location_get_source_line (file, line_num, &line_len); const char *line = location_get_source_line (file, line_num, &line_len);
if (!line) if (!line)
return false; return false;
unsigned int vis_column = 0;
for (int i = 1; i < line_len; i++)
{
unsigned char ch = line[i - 1];
int idx; if (!ISSPACE (ch))
/* 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])) *first_nws = vis_column;
break; return true;
idx++;
} }
/* Match #if/#ifdef/#ifndef. */ if (ch == '\t')
if (idx + 2 <= line_len) vis_column = next_tab_stop (vis_column);
if (line[idx] == 'i') else
if (line[idx + 1] == 'f') vis_column++;
return true; }
/* No non-whitespace characters found. */
return false; return false;
} }
/* Determine if there is an unindent/outdent between
/* Determine if there is preprocessor logic between
BODY_EXPLOC and NEXT_STMT_EXPLOC, to ensure that we don't BODY_EXPLOC and NEXT_STMT_EXPLOC, to ensure that we don't
issue a warning for cases like this: issue a warning for cases like the following:
(1) Preprocessor logic
if (flagA) if (flagA)
foo (); foo ();
...@@ -151,31 +152,47 @@ line_contains_hash_if (const char *file, int line_num) ...@@ -151,31 +152,47 @@ line_contains_hash_if (const char *file, int line_num)
bar (); bar ();
^ NEXT_STMT_EXPLOC ^ NEXT_STMT_EXPLOC
despite "bar ();" being visually aligned below "foo ();" and "bar ();" is visually aligned below "foo ();" and
being (as far as the parser sees) the next token. is (as far as the parser sees) the next token, but
this isn't misleading to a human reader.
Return true if such logic is detected. */ (2) Empty macro with bad indentation
static bool In the following, the
detect_preprocessor_logic (expanded_location body_exploc, "if (i > 0)"
expanded_location next_stmt_exploc) is poorly indented, and ought to be on the same column as
{ "engine_ref_debug(e, 0, -1)"
gcc_assert (next_stmt_exploc.file == body_exploc.file); However, it is not misleadingly indented, due to the presence
gcc_assert (next_stmt_exploc.line > body_exploc.line); of that macro.
if (next_stmt_exploc.line - body_exploc.line < 4) #define engine_ref_debug(X, Y, Z)
return false;
if (locked)
i = foo (0);
else
i = foo (1);
engine_ref_debug(e, 0, -1)
if (i > 0)
return 1;
/* Is there a #if/#ifdef/#ifndef directive somewhere in the lines Return true if such an unindent/outdent is detected. */
between the given locations?
This is something of a layering violation, but by necessity, static bool
given the nature of what we're testing for. For example, detect_intervening_unindent (const char *file,
in theory we could be fooled by a #if within a comment, but int body_line,
it's unlikely to matter. */ int next_stmt_line,
for (int line = body_exploc.line + 1; line < next_stmt_exploc.line; line++) unsigned int vis_column)
if (line_contains_hash_if (body_exploc.file, line)) {
gcc_assert (file);
gcc_assert (next_stmt_line > body_line);
for (int line = body_line + 1; line < next_stmt_line; line++)
{
unsigned int line_vis_column;
if (get_first_nws_vis_column (file, line, &line_vis_column))
if (line_vis_column < vis_column)
return true; return true;
}
/* Not found. */ /* Not found. */
return false; return false;
...@@ -467,9 +484,11 @@ should_warn_for_misleading_indentation (const token_indent_info &guard_tinfo, ...@@ -467,9 +484,11 @@ should_warn_for_misleading_indentation (const token_indent_info &guard_tinfo,
if (body_vis_column <= guard_line_first_nws) if (body_vis_column <= guard_line_first_nws)
return false; return false;
/* Don't warn if there is multiline preprocessor logic between /* Don't warn if there is an unindent between the two statements. */
the two statements. */ int vis_column = MIN (next_stmt_vis_column, body_vis_column);
if (detect_preprocessor_logic (body_exploc, next_stmt_exploc)) if (detect_intervening_unindent (body_exploc.file, body_exploc.line,
next_stmt_exploc.line,
vis_column))
return false; return false;
/* Otherwise, they are visually aligned: issue a warning. */ /* Otherwise, they are visually aligned: issue a warning. */
......
2016-03-04 David Malcolm <dmalcolm@redhat.com> 2016-03-04 David Malcolm <dmalcolm@redhat.com>
PR c/68187 PR c/68187
* c-c++-common/Wmisleading-indentation.c (fn_42_a): New test
function.
(fn_42_b): Likewise.
(fn_42_c): Likewise.
2016-03-04 David Malcolm <dmalcolm@redhat.com>
PR c/68187
* c-c++-common/Wmisleading-indentation.c (fn_40_a): New test * c-c++-common/Wmisleading-indentation.c (fn_40_a): New test
function. function.
(fn_40_b): Likewise. (fn_40_b): Likewise.
......
...@@ -982,3 +982,75 @@ fn_41_b (void) ...@@ -982,3 +982,75 @@ fn_41_b (void)
if (!flagC) if (!flagC)
goto fail; goto fail;
} }
/* In the following, the
"if (i > 0)"
is poorly indented, and ought to be on the same column as
"engine_ref_debug(e, 0, -1)"
However, it is not misleadingly indented, due to the presence
of that macro. Verify that we do not emit a warning about it
not being guarded by the "else" clause above.
Based on an example seen in OpenSSL 1.0.1, which was filed as
PR c/68187 in comment #1, though it's arguably a separate bug to
the one in comment #0. */
int
fn_42_a (int locked)
{
#define engine_ref_debug(X, Y, Z)
int i;
if (locked)
i = foo (0);
else
i = foo (1);
engine_ref_debug(e, 0, -1)
if (i > 0)
return 1;
return 0;
#undef engine_ref_debug
}
/* As above, but the empty macro is at the same indentation level.
This *is* misleading; verify that we do emit a warning about it. */
int
fn_42_b (int locked)
{
#define engine_ref_debug(X, Y, Z)
int i;
if (locked)
i = foo (0);
else /* { dg-message "...this .else. clause" } */
i = foo (1);
engine_ref_debug(e, 0, -1)
if (i > 0) /* { dg-warning "statement is indented" } */
return 1;
return 0;
#undef engine_ref_debug
}
/* As above, but where the body is a semicolon "hidden" by a preceding
comment, where the semicolon is not in the same column as the successor
"if" statement, but the empty macro expansion is at the same indentation
level as the guard.
This is poor indentation, but not misleading; verify that we don't emit a
warning about it. */
int
fn_42_c (int locked, int i)
{
#define engine_ref_debug(X, Y, Z)
if (locked)
/* blah */;
engine_ref_debug(e, 0, -1)
if (i > 0)
return 1;
return 0;
#undef engine_ref_debug
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment