Skip to content

Commit 080c64d

Browse files
authored
Rework the IWYU test to handle types with multiple valid sources (#2633)
1 parent 3057672 commit 080c64d

File tree

1 file changed

+42
-12
lines changed

1 file changed

+42
-12
lines changed

scripts/cpplint.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5550,8 +5550,17 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
55505550
io: The IO factory to use to read the header file. Provided for unittest
55515551
injection.
55525552
"""
5553-
required = {} # A map of header name to linenumber and the template entity.
5554-
# Example of required: { '<functional>': (1219, 'less<>') }
5553+
# A map of entity to a tuple of line number and tuple of headers.
5554+
# Example: { 'less<>': (1219, ('<functional>',)) }
5555+
# Example: { 'ostream': (1234, ('<iosfwd>', '<ostream>', '<iostream>')) }
5556+
required = {}
5557+
5558+
def Require(entity, linenum, *headers):
5559+
"""Adds an entity at the given line, along with a list of possible headers
5560+
in which to find it. The first header is treated as the preferred header.
5561+
"""
5562+
required[entity] = (linenum, headers)
5563+
55555564

55565565
for linenum in xrange(clean_lines.NumLines()):
55575566
line = clean_lines.elided[linenum]
@@ -5565,19 +5574,19 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
55655574
# (We check only the first match per line; good enough.)
55665575
prefix = line[:matched.start()]
55675576
if prefix.endswith('std::') or not prefix.endswith('::'):
5568-
required['<string>'] = (linenum, 'string')
5577+
Require('string', linenum, '<string>')
55695578

55705579
# Ostream is special too -- also non-templatized
55715580
matched = _RE_PATTERN_OSTREAM.search(line)
55725581
if matched:
55735582
if IsSourceFilename(filename):
5574-
required['<ostream>'] = (linenum, 'ostream')
5583+
Require('ostream', linenum, '<ostream>', '<iostream>')
55755584
else:
5576-
required['<iosfwd>'] = (linenum, 'ostream')
5585+
Require('ostream', linenum, '<iosfwd>', '<ostream>', '<iostream>')
55775586

55785587
for pattern, template, header in _re_pattern_headers_maybe_templates:
55795588
if pattern.search(line):
5580-
required[header] = (linenum, template)
5589+
Require(template, linenum, header)
55815590

55825591
# The following function is just a speed up, no semantics are changed.
55835592
if not '<' in line: # Reduces the cpu time usage by skipping lines.
@@ -5590,7 +5599,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
55905599
# (We check only the first match per line; good enough.)
55915600
prefix = line[:matched.start()]
55925601
if prefix.endswith('std::') or not prefix.endswith('::'):
5593-
required[header] = (linenum, template)
5602+
Require(template, linenum, header)
55945603

55955604
# The policy is that if you #include something in foo.h you don't need to
55965605
# include it again in foo.cc. Here, we will look at possible includes.
@@ -5630,13 +5639,34 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
56305639
if IsSourceFilename(filename) and not header_found:
56315640
return
56325641

5642+
# Keep track of which headers have been reported already
5643+
reported = set()
5644+
56335645
# All the lines have been processed, report the errors found.
5634-
for required_header_unstripped in required:
5635-
template = required[required_header_unstripped][1]
5636-
if required_header_unstripped.strip('<>"') not in include_dict:
5637-
error(filename, required[required_header_unstripped][0],
5646+
for template in required:
5647+
line_and_headers = required[template]
5648+
headers = line_and_headers[1]
5649+
found = False
5650+
for required_header_unstripped in headers:
5651+
if required_header_unstripped in reported:
5652+
found = True
5653+
break
5654+
5655+
if required_header_unstripped.strip('<>"') in include_dict:
5656+
found = True
5657+
break
5658+
5659+
if not found:
5660+
preferred_header = headers[0]
5661+
reported.add(preferred_header)
5662+
if len(headers) < 2:
5663+
alternatives = ''
5664+
else:
5665+
alternatives = ' (or ' + ', '.join(headers[1:]) + ')'
5666+
error(filename, line_and_headers[0],
56385667
'build/include_what_you_use', 4,
5639-
'Add #include ' + required_header_unstripped + ' for ' + template)
5668+
'Add #include ' + preferred_header + ' for ' + template +
5669+
alternatives)
56405670

56415671

56425672
_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')

0 commit comments

Comments
 (0)