Add a concept of 'barrier' #includes that we will not sort

around.  This concept is intended for fragile #includes that
require something else to be #included before them or they
don't run properly.  By setting them as a barrier include, we
guarantee no include will be reordered from before it to after
it.

For now, the only barrier #includes are <linux/foo>.
Sometimes you need to #include linux includes in a particular
order for things to compile; we don't want fix_includes to
mess with that.

Note that each barrier include is treated separately -- if we
see two <linux/foo> #includes next to each other, we will
leave them each alone, we won't treat them as a group and
consider sorting them.

R=dsturtevant
DELTA=83  (74 added, 0 deleted, 9 changed)


Revision created by MOE tool push_codebase.
MOE_MIGRATION=1346
This commit is contained in:
csilvers+iwyu 2011-04-12 04:59:54 +00:00
parent 3183022744
commit 7d2240f212
2 changed files with 83 additions and 9 deletions

View File

@ -126,6 +126,13 @@ _LINE_TYPES = [_COMMENT_LINE_RE, _BLANK_LINE_RE,
_INCLUDE_RE, _FORWARD_DECLARE_RE,
]
# A regexp matching #include lines that should not be sorted -- that
# is, we should never reorganize the code so an #include that used to
# come before this line now comes after, or vice versa. This can be
# used for 'fragile' #includes that require other #includes to happen
# before them to function properly.
_NOSORT_INCLUDES = re.compile(r'^\s*#\s*include\s+(<linux/)')
class FixIncludesError(Exception):
pass
@ -661,6 +668,15 @@ def _CalculateMoveSpans(file_lines, forward_declare_spans):
file_lines[i].move_span = (span_begin, span_end)
def _IsNosortInclude(file_lines, line_range):
"""Returns true iff some line in [line_range[0], line_range[1]) is _NOSORT."""
for line_number in apply(xrange, line_range):
if (not file_lines[line_number].deleted and
_NOSORT_INCLUDES.search(file_lines[line_number].line)):
return True
return False
def _LinesAreAllBlank(file_lines, start_line, end_line):
"""Returns true iff all lines in [start_line, end_line) are blank/deleted."""
for line_number in xrange(start_line, end_line):
@ -682,10 +698,17 @@ def _CalculateReorderSpans(file_lines):
and namespaces freely inside a reorder span.
Calculating reorder_span is easy: they're just the union of
continguous move-spans (with perhaps blank lines and comments
contiguous move-spans (with perhaps blank lines and comments
thrown in), because move-spans share the 'no actual code'
requirement.
There's one exception: if any move-span matches the _NOSORT_INCLUDES
regexp, it means that we should consider that move-span to be a
'barrier': nothing should get reordered from one side of that
move-span to the other. (This is used for #includes that depend on
other #includes being before them to function properly.) We do that
by putting them into their own reorder span.
For lines of type _INCLUDE_RE or _FORWARD_DECLARE_RE, the reorder
span is set to the tuple [start_of_span, end_of_span). All other
lines have an arbitrary value for the reorder span.
@ -701,14 +724,19 @@ def _CalculateReorderSpans(file_lines):
i = 0
while i < len(sorted_move_spans):
reorder_span_start = sorted_move_spans[i][0]
# Add in the next move span if we're connected to it only by blank lines.
while i < len(sorted_move_spans) - 1:
move_span_end = sorted_move_spans[i][1]
next_move_span_start = sorted_move_spans[i+1][0]
if _LinesAreAllBlank(file_lines, move_span_end, next_move_span_start):
i += 1
else:
break
# If we're a 'nosort' include, we're always in a reorder span of
# our own. Otherwise, add in the next move span if we're
# connected to it only by blank lines.
if not _IsNosortInclude(file_lines, sorted_move_spans[i]):
while i < len(sorted_move_spans) - 1:
move_span_end = sorted_move_spans[i][1]
next_move_span_start = sorted_move_spans[i+1][0]
if (_LinesAreAllBlank(file_lines, move_span_end, next_move_span_start)
and not _IsNosortInclude(file_lines, sorted_move_spans[i+1])):
i += 1
else:
break
reorder_span_end = sorted_move_spans[i][1]
# We'll map every line in the span to the span-extent.
for line_number in xrange(reorder_span_start, reorder_span_end):

View File

@ -2204,6 +2204,52 @@ int main() { return 0; }
self.assertListEqual([], self.actual_after_contents)
self.assertEqual(0, num_files_modified)
def testNosortIncludes(self):
"""Tests that we correctly sort 'around' _NOSORT_INCLUDES."""
infile = """\
// Copyright 2010
#include <linux/a_stay_top.h>
#include <stdlib.h> ///-
#include <linux/can_sort_around_this_deleted_include> ///-
#include <stdio.h>
///+#include <stdlib.h>
#include "user/include.h"
///+#include "user/new_include.h"
#include <linux/c_stay_second.h>
#include <linux/b_stay_third.h>
#include <ctype.h>
#include <cpp_include>
///+#include <new_cpp_include>
#include <linux/d_stay_fourth.h>
int main() { return 0; }
"""
iwyu_output = """\
nosort_includes.h should add these lines:
#include "user/new_include.h"
#include <new_cpp_include>
nosort_includes.h should remove these lines:
- #include <linux/can_sort_around_this_deleted_include> // lines 5-5
The full include-list for nosort_includes.h:
#include "user/include.h"
#include "user/new_include.h"
#include <cpp_include>
#include <ctype.h>
#include <linux/a_stay_top.h>
#include <linux/b_stay_third.h>
#include <linux/c_stay_second.h>
#include <linux/d_stay_fourth.h>
#include <new_cpp_include>
#include <stdio.h>
#include <stdlib.h>
---
"""
self.RegisterFileContents({'nosort_includes.h': infile})
self.ProcessAndTest(iwyu_output)
def testSortingProjectIncludesAuto(self):
"""Check that project includes can be sorted separately."""
infile = """\