in build-support/iwyu/fix_includes.py [0:0]
def _FirstReorderSpanWith(file_lines, good_reorder_spans, kind, filename,
flags):
"""Returns [start_line,end_line) of 1st reorder_span with a line of kind kind.
This function iterates over all the reorder_spans in file_lines, and
calculates the first one that has a line of the given kind in it.
If no such reorder span is found, it takes the last span of 'lower'
kinds (main-cu kind is lowest, forward-declare is highest). If no
such reorder span is found, it takes the first span of 'higher'
kind, but not considering the forward-declare kind (we don't want to
put an #include with the first forward-declare, because it may be
inside a class or something weird). If there's *still* no match, we
return the first line past leading comments, whitespace, and #ifdef
guard lines. If there's *still* no match, we just insert at
end-of-file.
As a special case, we never return a span for forward-declares that is
after 'contentful' code, even if other forward-declares are there.
For instance:
using Foo::Bar;
class Bang;
We want to make sure to put 'namespace Foo { class Bar; }'
*before* the using line!
kind is one of the following enums, with examples:
_MAIN_CU_INCLUDE_KIND: #include "foo.h" when editing foo.cc
_C_SYSTEM_INCLUDE_KIND: #include <stdio.h>
_CXX_SYSTEM_INCLUDE_KIND: #include <vector>
_NONSYSTEM_INCLUDE_KIND: #include "bar.h"
_PROJECT_INCLUDE_KIND: #include "myproject/quux.h"
_FORWARD_DECLARE_KIND: class Baz;
Arguments:
file_lines: an array of LineInfo objects with .type and
.reorder_span filled in.
good_reorder_spans: a sorted list of reorder_spans to consider
(should not include reorder_spans inside #ifdefs or
namespaces).
kind: one of *_KIND values.
filename: the name of the file that file_lines comes from.
This is passed to _GetLineKind (are we a main-CU #include?)
flags: commandline flags, as parsed by optparse. We use
flags.separate_project_includes to sort the #includes for the
current project separately from other #includes.
Returns:
A pair of line numbers, [start_line, end_line), that is the 'best'
reorder_span in file_lines for the given kind.
"""
assert kind in (_MAIN_CU_INCLUDE_KIND, _C_SYSTEM_INCLUDE_KIND,
_CXX_SYSTEM_INCLUDE_KIND, _NONSYSTEM_INCLUDE_KIND,
_PROJECT_INCLUDE_KIND, _FORWARD_DECLARE_KIND), kind
# Figure out where the first 'contentful' line is (after the first
# 'good' span, so we skip past header guards and the like). Basically,
# the first contentful line is a line not in any reorder span.
for i in range(len(good_reorder_spans) - 1):
if good_reorder_spans[i][1] != good_reorder_spans[i+1][0]:
first_contentful_line = good_reorder_spans[i][1]
break
else: # got to the end of the file without finding a break in the spans
if good_reorder_spans:
first_contentful_line = good_reorder_spans[-1][1]
else:
first_contentful_line = 0
# Let's just find the first and last span for each kind.
first_reorder_spans = {}
last_reorder_spans = {}
for reorder_span in good_reorder_spans:
for line_number in range(*reorder_span):
line_kind = _GetLineKind(file_lines[line_number], filename, flags)
# Ignore forward-declares that come after 'contentful' code; we
# never want to insert new forward-declares there.
if (line_kind == _FORWARD_DECLARE_KIND and
line_number > first_contentful_line):
continue
if line_kind is not None:
first_reorder_spans.setdefault(line_kind, reorder_span)
last_reorder_spans[line_kind] = reorder_span
# Find the first span of our kind.
if kind in first_reorder_spans:
return first_reorder_spans[kind]
# Second choice: last span of the kinds above us:
for backup_kind in range(kind - 1, _MAIN_CU_INCLUDE_KIND - 1, -1):
if backup_kind in last_reorder_spans:
return last_reorder_spans[backup_kind]
# Third choice: first span of the kinds below us, but not counting
# _FORWARD_DECLARE_KIND.
for backup_kind in range(kind + 1, _FORWARD_DECLARE_KIND):
if backup_kind in first_reorder_spans:
return first_reorder_spans[backup_kind]
# There are no reorder-spans at all, or they are only
# _FORWARD_DECLARE spans. Return the first line past the leading
# comments, whitespace, and #ifdef guard lines, or the beginning
# of the _FORWARD_DECLARE span, whichever is smaller.
line_number = 0
seen_header_guard = False
while line_number < len(file_lines):
if file_lines[line_number].deleted:
line_number += 1
elif file_lines[line_number].type == _HEADER_GUARD_RE:
seen_header_guard = True
line_number += 2 # skip over the header guard
elif file_lines[line_number].type == _BLANK_LINE_RE:
line_number += 1
elif file_lines[line_number].type == _PRAGMA_ONCE_LINE_RE:
seen_header_guard = True
line_number += 1
elif (file_lines[line_number].type == _COMMENT_LINE_RE
and not seen_header_guard):
# We put #includes after top-of-file comments. But comments
# inside the header guard are no longer top-of-file comments;
# #includes go before them.
line_number += 1
else:
# If the "first line" we would return is inside the forward-declare
# reorder span, just return that span, rather than creating a new
# span inside the existing one.
if first_reorder_spans:
assert list(first_reorder_spans.keys()) == [_FORWARD_DECLARE_KIND], \
first_reorder_spans
if line_number >= first_reorder_spans[_FORWARD_DECLARE_KIND][0]:
return first_reorder_spans[_FORWARD_DECLARE_KIND]
return (line_number, line_number)
# OK, I guess just insert at the end of the file
return (len(file_lines), len(file_lines))