@@ -5550,8 +5550,17 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
5550
5550
io: The IO factory to use to read the header file. Provided for unittest
5551
5551
injection.
5552
5552
"""
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
+
5555
5564
5556
5565
for linenum in xrange (clean_lines .NumLines ()):
5557
5566
line = clean_lines .elided [linenum ]
@@ -5565,19 +5574,19 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
5565
5574
# (We check only the first match per line; good enough.)
5566
5575
prefix = line [:matched .start ()]
5567
5576
if prefix .endswith ('std::' ) or not prefix .endswith ('::' ):
5568
- required [ '< string>' ] = ( linenum , 'string' )
5577
+ Require ( ' string' , linenum , '< string> ' )
5569
5578
5570
5579
# Ostream is special too -- also non-templatized
5571
5580
matched = _RE_PATTERN_OSTREAM .search (line )
5572
5581
if matched :
5573
5582
if IsSourceFilename (filename ):
5574
- required [ '< ostream>' ] = ( linenum , 'ostream' )
5583
+ Require ( ' ostream' , linenum , '< ostream>' , '<iostream> ' )
5575
5584
else :
5576
- required [ ' <iosfwd>'] = ( linenum , 'ostream' )
5585
+ Require ( 'ostream' , linenum , ' <iosfwd>' , '< ostream>' , '<iostream> ' )
5577
5586
5578
5587
for pattern , template , header in _re_pattern_headers_maybe_templates :
5579
5588
if pattern .search (line ):
5580
- required [ header ] = ( linenum , template )
5589
+ Require ( template , linenum , header )
5581
5590
5582
5591
# The following function is just a speed up, no semantics are changed.
5583
5592
if not '<' in line : # Reduces the cpu time usage by skipping lines.
@@ -5590,7 +5599,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
5590
5599
# (We check only the first match per line; good enough.)
5591
5600
prefix = line [:matched .start ()]
5592
5601
if prefix .endswith ('std::' ) or not prefix .endswith ('::' ):
5593
- required [ header ] = ( linenum , template )
5602
+ Require ( template , linenum , header )
5594
5603
5595
5604
# The policy is that if you #include something in foo.h you don't need to
5596
5605
# 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,
5630
5639
if IsSourceFilename (filename ) and not header_found :
5631
5640
return
5632
5641
5642
+ # Keep track of which headers have been reported already
5643
+ reported = set ()
5644
+
5633
5645
# 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 ],
5638
5667
'build/include_what_you_use' , 4 ,
5639
- 'Add #include ' + required_header_unstripped + ' for ' + template )
5668
+ 'Add #include ' + preferred_header + ' for ' + template +
5669
+ alternatives )
5640
5670
5641
5671
5642
5672
_RE_PATTERN_EXPLICIT_MAKEPAIR = re .compile (r'\bmake_pair\s*<' )
0 commit comments