mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 03:48:18 +00:00
remove CheckForIncludeWhatYouUse
Only checks C++ related headers
This commit is contained in:

committed by
Thiago de Arruda

parent
cfa070edc5
commit
38eaa7ed32
235
clint.py
235
clint.py
@@ -4241,241 +4241,6 @@ def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
_HEADERS_CONTAINING_TEMPLATES = (
|
|
||||||
('<deque>', ('deque',)),
|
|
||||||
('<functional>', ('unary_function', 'binary_function',
|
|
||||||
'plus', 'minus', 'multiplies', 'divides', 'modulus',
|
|
||||||
'negate',
|
|
||||||
'equal_to', 'not_equal_to', 'greater', 'less',
|
|
||||||
'greater_equal', 'less_equal',
|
|
||||||
'logical_and', 'logical_or', 'logical_not',
|
|
||||||
'unary_negate', 'not1', 'binary_negate', 'not2',
|
|
||||||
'bind1st', 'bind2nd',
|
|
||||||
'pointer_to_unary_function',
|
|
||||||
'pointer_to_binary_function',
|
|
||||||
'ptr_fun',
|
|
||||||
'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
|
|
||||||
'mem_fun_ref_t',
|
|
||||||
'const_mem_fun_t', 'const_mem_fun1_t',
|
|
||||||
'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
|
|
||||||
'mem_fun_ref',
|
|
||||||
)),
|
|
||||||
('<limits>', ('numeric_limits',)),
|
|
||||||
('<list>', ('list',)),
|
|
||||||
('<map>', ('map', 'multimap',)),
|
|
||||||
('<memory>', ('allocator',)),
|
|
||||||
('<queue>', ('queue', 'priority_queue',)),
|
|
||||||
('<set>', ('set', 'multiset',)),
|
|
||||||
('<stack>', ('stack',)),
|
|
||||||
('<string>', ('char_traits', 'basic_string',)),
|
|
||||||
('<utility>', ('pair',)),
|
|
||||||
('<vector>', ('vector',)),
|
|
||||||
|
|
||||||
# gcc extensions.
|
|
||||||
# Note: std::hash is their hash, ::hash is our hash
|
|
||||||
('<hash_map>', ('hash_map', 'hash_multimap',)),
|
|
||||||
('<hash_set>', ('hash_set', 'hash_multiset',)),
|
|
||||||
('<slist>', ('slist',)),
|
|
||||||
)
|
|
||||||
|
|
||||||
_RE_PATTERN_STRING = re.compile(r'\bstring\b')
|
|
||||||
|
|
||||||
_re_pattern_algorithm_header = []
|
|
||||||
for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap',
|
|
||||||
'transform'):
|
|
||||||
# Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
|
|
||||||
# type::max().
|
|
||||||
_re_pattern_algorithm_header.append(
|
|
||||||
(re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
|
|
||||||
_template,
|
|
||||||
'<algorithm>'))
|
|
||||||
|
|
||||||
_re_pattern_templates = []
|
|
||||||
for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
|
|
||||||
for _template in _templates:
|
|
||||||
_re_pattern_templates.append(
|
|
||||||
(re.compile(r'(\<|\b)' + _template + r'\s*\<'),
|
|
||||||
_template + '<>',
|
|
||||||
_header))
|
|
||||||
|
|
||||||
|
|
||||||
def FilesBelongToSameModule(filename_cc, filename_h):
|
|
||||||
"""Check if these two filenames belong to the same module.
|
|
||||||
|
|
||||||
The concept of a 'module' here is a as follows:
|
|
||||||
foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
|
|
||||||
same 'module' if they are in the same directory.
|
|
||||||
some/path/public/xyzzy and some/path/internal/xyzzy are also considered
|
|
||||||
to belong to the same module here.
|
|
||||||
|
|
||||||
If the filename_cc contains a longer path than the filename_h, for example,
|
|
||||||
'/absolute/path/to/base/sysinfo.cc', and this file would include
|
|
||||||
'base/sysinfo.h', this function also produces the prefix needed to open the
|
|
||||||
header. This is used by the caller of this function to more robustly open the
|
|
||||||
header file. We don't have access to the real include paths in this context,
|
|
||||||
so we need this guesswork here.
|
|
||||||
|
|
||||||
Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
|
|
||||||
according to this implementation. Because of this, this function gives
|
|
||||||
some false positives. This should be sufficiently rare in practice.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename_cc: is the path for the .cc file
|
|
||||||
filename_h: is the path for the header path
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple with a bool and a string:
|
|
||||||
bool: True if filename_cc and filename_h belong to the same module.
|
|
||||||
string: the additional prefix needed to open the header file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not filename_cc.endswith('.cc'):
|
|
||||||
return (False, '')
|
|
||||||
filename_cc = filename_cc[:-len('.cc')]
|
|
||||||
if filename_cc.endswith('_unittest'):
|
|
||||||
filename_cc = filename_cc[:-len('_unittest')]
|
|
||||||
elif filename_cc.endswith('_test'):
|
|
||||||
filename_cc = filename_cc[:-len('_test')]
|
|
||||||
filename_cc = filename_cc.replace('/public/', '/')
|
|
||||||
filename_cc = filename_cc.replace('/internal/', '/')
|
|
||||||
|
|
||||||
if not filename_h.endswith('.h'):
|
|
||||||
return (False, '')
|
|
||||||
filename_h = filename_h[:-len('.h')]
|
|
||||||
if filename_h.endswith('-inl'):
|
|
||||||
filename_h = filename_h[:-len('-inl')]
|
|
||||||
filename_h = filename_h.replace('/public/', '/')
|
|
||||||
filename_h = filename_h.replace('/internal/', '/')
|
|
||||||
|
|
||||||
files_belong_to_same_module = filename_cc.endswith(filename_h)
|
|
||||||
common_path = ''
|
|
||||||
if files_belong_to_same_module:
|
|
||||||
common_path = filename_cc[:-len(filename_h)]
|
|
||||||
return files_belong_to_same_module, common_path
|
|
||||||
|
|
||||||
|
|
||||||
def UpdateIncludeState(filename, include_state, io=codecs):
|
|
||||||
"""Fill up the include_state with new includes found from the file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename: the name of the header to read.
|
|
||||||
include_state: an _IncludeState instance in which the headers are inserted.
|
|
||||||
io: The io factory to use to read the file. Provided for testability.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if a header was succesfully added. False otherwise.
|
|
||||||
"""
|
|
||||||
headerfile = None
|
|
||||||
try:
|
|
||||||
headerfile = io.open(filename, 'r', 'utf8', 'replace')
|
|
||||||
except IOError:
|
|
||||||
return False
|
|
||||||
linenum = 0
|
|
||||||
for line in headerfile:
|
|
||||||
linenum += 1
|
|
||||||
clean_line = CleanseComments(line)
|
|
||||||
match = _RE_PATTERN_INCLUDE.search(clean_line)
|
|
||||||
if match:
|
|
||||||
include = match.group(2)
|
|
||||||
# The value formatting is cute, but not really used right now.
|
|
||||||
# What matters here is that the key is in include_state.
|
|
||||||
include_state.setdefault(include, '%s:%d' % (filename, linenum))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
|
|
||||||
io=codecs):
|
|
||||||
"""Reports for missing stl includes.
|
|
||||||
|
|
||||||
This function will output warnings to make sure you are including the headers
|
|
||||||
necessary for the stl containers and functions that you use. We only give one
|
|
||||||
reason to include a header. For example, if you use both equal_to<> and
|
|
||||||
less<> in a .h file, only one (the latter in the file) of these will be
|
|
||||||
reported as a reason to include the <functional>.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename: The name of the current file.
|
|
||||||
clean_lines: A CleansedLines instance containing the file.
|
|
||||||
include_state: An _IncludeState instance.
|
|
||||||
error: The function to call with any errors found.
|
|
||||||
io: The IO factory to use to read the header file. Provided for unittest
|
|
||||||
injection.
|
|
||||||
"""
|
|
||||||
required = {} # A map of header name to linenumber and the template entity.
|
|
||||||
# Example of required: { '<functional>': (1219, 'less<>') }
|
|
||||||
|
|
||||||
for linenum in range(clean_lines.NumLines()):
|
|
||||||
line = clean_lines.elided[linenum]
|
|
||||||
if not line or line[0] == '#':
|
|
||||||
continue
|
|
||||||
|
|
||||||
# String is special -- it is a non-templatized type in STL.
|
|
||||||
matched = _RE_PATTERN_STRING.search(line)
|
|
||||||
if matched:
|
|
||||||
# Don't warn about strings in non-STL namespaces:
|
|
||||||
# (We check only the first match per line; good enough.)
|
|
||||||
prefix = line[:matched.start()]
|
|
||||||
if prefix.endswith('std::') or not prefix.endswith('::'):
|
|
||||||
required['<string>'] = (linenum, 'string')
|
|
||||||
|
|
||||||
for pattern, template, header in _re_pattern_algorithm_header:
|
|
||||||
if pattern.search(line):
|
|
||||||
required[header] = (linenum, template)
|
|
||||||
|
|
||||||
# The following function is just a speed up, no semantics are changed.
|
|
||||||
if not '<' in line: # Reduces the cpu time usage by skipping lines.
|
|
||||||
continue
|
|
||||||
|
|
||||||
for pattern, template, header in _re_pattern_templates:
|
|
||||||
if pattern.search(line):
|
|
||||||
required[header] = (linenum, template)
|
|
||||||
|
|
||||||
# The policy is that if you #include something in foo.h you don't need to
|
|
||||||
# include it again in foo.cc. Here, we will look at possible includes.
|
|
||||||
# Let's copy the include_state so it is only messed up within this function.
|
|
||||||
include_state = include_state.copy()
|
|
||||||
|
|
||||||
# Did we find the header for this file (if any) and succesfully load it?
|
|
||||||
header_found = False
|
|
||||||
|
|
||||||
# Use the absolute path so that matching works properly.
|
|
||||||
abs_filename = FileInfo(filename).FullName()
|
|
||||||
|
|
||||||
# For Emacs's flymake.
|
|
||||||
# If cpplint is invoked from Emacs's flymake, a temporary file is generated
|
|
||||||
# by flymake and that file name might end with '_flymake.cc'. In that case,
|
|
||||||
# restore original file name here so that the corresponding header file can be
|
|
||||||
# found.
|
|
||||||
# e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
|
|
||||||
# instead of 'foo_flymake.h'
|
|
||||||
abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
|
|
||||||
|
|
||||||
# include_state is modified during iteration, so we iterate over a copy of
|
|
||||||
# the keys.
|
|
||||||
header_keys = list(include_state.keys())
|
|
||||||
for header in header_keys:
|
|
||||||
(same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
|
|
||||||
fullpath = common_path + header
|
|
||||||
if same_module and UpdateIncludeState(fullpath, include_state, io):
|
|
||||||
header_found = True
|
|
||||||
|
|
||||||
# If we can't find the header file for a .cc, assume it's because we don't
|
|
||||||
# know where to look. In that case we'll give up as we're not sure they
|
|
||||||
# didn't include it in the .h file.
|
|
||||||
# TODO(unknown): Do a better job of finding .h files so we are confident that
|
|
||||||
# not having the .h file means there isn't one.
|
|
||||||
if filename.endswith('.cc') and not header_found:
|
|
||||||
return
|
|
||||||
|
|
||||||
# All the lines have been processed, report the errors found.
|
|
||||||
for required_header_unstripped in required:
|
|
||||||
template = required[required_header_unstripped][1]
|
|
||||||
if required_header_unstripped.strip('<>"') not in include_state:
|
|
||||||
error(filename, required[required_header_unstripped][0],
|
|
||||||
'build/include_what_you_use', 4,
|
|
||||||
'Add #include ' + required_header_unstripped + ' for ' + template)
|
|
||||||
|
|
||||||
|
|
||||||
def ProcessLine(filename, file_extension, clean_lines, line,
|
def ProcessLine(filename, file_extension, clean_lines, line,
|
||||||
include_state, function_state, nesting_state, error,
|
include_state, function_state, nesting_state, error,
|
||||||
extra_check_functions=[]):
|
extra_check_functions=[]):
|
||||||
|
Reference in New Issue
Block a user