Skip to content
This repository was archived by the owner on Jul 13, 2019. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 133 additions & 55 deletions cpplint.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import codecs
import copy
import getopt
import itertools
import math # for log
import os
import re
Expand All @@ -52,15 +53,42 @@
import sys
import unicodedata

# if empty, use defaults
_header_extensions = set([])

# if empty, use defaults
_valid_extensions = set([])


# Files with any of these extensions are considered to be
# header files (and will undergo different style checks).
# This set can be extended by using the --headers
# option (also supported in CPPLINT.cfg)
def GetHeaderExtensions():
if not _header_extensions:
return set(['h', 'hpp', 'hxx', 'h++', 'cuh'])
return _header_extensions

# The allowed extensions for file names
# This is set by --extensions flag.
_valid_extensions = set(['c', 'cc', 'cpp', 'cxx', 'c++', 'h', 'hpp', 'hxx',
'h++'])
# This is set by --extensions flag
def GetAllExtensions():
if not _valid_extensions:
return GetHeaderExtensions().union(set(['c', 'cc', 'cpp', 'cxx', 'c++', 'cu']))
return _valid_extensions

def GetNonHeaderExtensions():
return GetAllExtensions().difference(GetHeaderExtensions())


# files with this suffix before the extension will be treated as test files
_test_suffixes = set(['_unittest', '_test', '_regtest'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this configurable using flags / CPPLINT.cfg?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, that may be a nice additional feature, but maybe that would go well into #11


_USAGE = """
Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
[--counting=total|toplevel|detailed] [--root=subdir]
[--linelength=digits]
[--headers=ext1,ext2]
[--extensions=ext1,ext2]
<file> [file] ...

The style guidelines this tries to follow are those in
Expand Down Expand Up @@ -137,7 +165,15 @@
The allowed file extensions that cpplint will check

Examples:
--extensions=hpp,cpp
--extensions=%s

headers=extension,extension,...
The allowed header extensions that cpplint will consider to be header files
(by default, only files with extensions %s
will be assumed to be headers)

Examples:
--headers=%s

cpplint.py supports per-directory configurations specified in CPPLINT.cfg
files. CPPLINT.cfg file can contain a number of key=value pairs.
Expand Down Expand Up @@ -173,7 +209,10 @@
build/include_alpha as well as excludes all .cc from being
processed by linter, in the current directory (where the .cfg
file is located) and all sub-directories.
""" % (list(_valid_extensions))
""" % (list(GetAllExtensions()),
','.join(list(GetAllExtensions())),
GetHeaderExtensions(),
','.join(GetHeaderExtensions()))

# We categorize each error message we print. Here are the categories.
# We want an explicit list so we can list them all in cpplint --filter=.
Expand Down Expand Up @@ -525,6 +564,7 @@ def u(x):
itervalues = dict.values
iteritems = dict.items


def ParseNolintSuppressions(filename, raw_line, linenum, error):
"""Updates the global list of error-suppressions.

Expand Down Expand Up @@ -1074,7 +1114,7 @@ def BaseName(self):
return self.Split()[1]

def Extension(self):
"""File extension - text following the final period."""
"""File extension - text following the final period, includes that period."""
return self.Split()[2]

def NoExtension(self):
Expand All @@ -1083,7 +1123,7 @@ def NoExtension(self):

def IsSource(self):
"""File has a source file extension."""
return self.Extension()[1:] in _valid_extensions
return self.Extension()[1:] in GetAllExtensions()


def _ShouldPrintError(category, confidence, linenum):
Expand Down Expand Up @@ -1798,28 +1838,29 @@ def CheckForHeaderGuard(filename, clean_lines, error):


def CheckHeaderFileIncluded(filename, include_state, error):
"""Logs an error if a .cc file does not include its header."""
"""Logs an error if a source file does not include its header."""

# Do not check test files
if filename.endswith('_test.cc') or filename.endswith('_unittest.cc'):
if _IsTestFilename(filename):
return

fileinfo = FileInfo(filename)
headerfile = filename[0:len(filename) - 2] + 'h'
if not os.path.exists(headerfile):
return
headername = FileInfo(headerfile).RepositoryName()
first_include = 0
for section_list in include_state.include_list:
for f in section_list:
if headername in f[0] or f[0] in headername:
return
if not first_include:
first_include = f[1]
for ext in GetHeaderExtensions():
headerfile = filename[:filename.rfind('.') + 1] + ext
if not os.path.exists(headerfile):
continue
headername = FileInfo(headerfile).RepositoryName()
first_include = None
for section_list in include_state.include_list:
for f in section_list:
if headername in f[0] or f[0] in headername:
return
if not first_include:
first_include = f[1]

error(filename, first_include, 'build/include', 5,
'%s should include its header file %s' % (fileinfo.RepositoryName(),
headername))
error(filename, first_include, 'build/include', 5,
'%s should include its header file %s' % (fileinfo.RepositoryName(),
headername))


def CheckForBadCharacters(filename, lines, error):
Expand Down Expand Up @@ -4460,7 +4501,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,

# Check if the line is a header guard.
is_header_guard = False
if file_extension == 'h':
if file_extension in GetHeaderExtensions():
cppvar = GetHeaderGuardCPPVariable(filename)
if (line.startswith('#ifndef %s' % cppvar) or
line.startswith('#define %s' % cppvar) or
Expand Down Expand Up @@ -4553,8 +4594,11 @@ def _DropCommonSuffixes(filename):
Returns:
The filename with the common suffix removed.
"""
for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
'inl.h', 'impl.h', 'internal.h'):
for suffix in itertools.chain(
('%s.%s' % (test_suffix.lstrip('_'), ext)
for test_suffix, ext in itertools.product(_test_suffixes, GetNonHeaderExtensions())),
('%s.%s' % (suffix, ext)
for suffix, ext in itertools.product(['inl', 'imp', 'internal'], GetHeaderExtensions()))):
if (filename.endswith(suffix) and len(filename) > len(suffix) and
filename[-len(suffix) - 1] in ('-', '_')):
return filename[:-len(suffix) - 1]
Expand All @@ -4570,12 +4614,10 @@ def _IsTestFilename(filename):
Returns:
True if 'filename' looks like a test, False otherwise.
"""
if (filename.endswith('_test.cc') or
filename.endswith('_unittest.cc') or
filename.endswith('_regtest.cc')):
return True
else:
return False
for test_suffix, ext in itertools.product(_test_suffixes, GetNonHeaderExtensions()):
if filename.endswith(test_suffix + '.' + ext):
return True
return False


def _ClassifyInclude(fileinfo, include, is_system):
Expand Down Expand Up @@ -4679,11 +4721,16 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
error(filename, linenum, 'build/include', 4,
'"%s" already included at %s:%s' %
(include, filename, duplicate_line))
elif (include.endswith('.cc') and
return

for extension in GetNonHeaderExtensions():
if (include.endswith('.' + extension) and
os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)):
error(filename, linenum, 'build/include', 4,
'Do not include .cc files from other packages')
elif not _THIRD_PARTY_HEADERS_PATTERN.match(include):
error(filename, linenum, 'build/include', 4,
'Do not include .' + extension + ' files from other packages')
return

if not _THIRD_PARTY_HEADERS_PATTERN.match(include):
include_state.include_list[-1].append((include, linenum))

# We want to ensure that headers appear in the right order:
Expand Down Expand Up @@ -4833,7 +4880,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
CheckGlobalStatic(filename, clean_lines, linenum, error)
CheckPrintf(filename, clean_lines, linenum, error)

if file_extension == 'h':
if file_extension in GetHeaderExtensions():
# TODO(unknown): check that 1-arg constructors are explicit.
# How to tell it's a constructor?
# (handled in CheckForNonStandardConstructs for now)
Expand Down Expand Up @@ -4940,7 +4987,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
# Check for use of unnamed namespaces in header files. Registration
# macros are typically OK, so we allow use of "namespace {" on lines
# that end with backslashes.
if (file_extension == 'h'
if (file_extension in GetHeaderExtensions()
and Search(r'\bnamespace\s*{', line)
and line[-1] != '\\'):
error(filename, linenum, 'build/namespaces', 4,
Expand Down Expand Up @@ -5573,28 +5620,32 @@ def FilesBelongToSameModule(filename_cc, filename_h):
some false positives. This should be sufficiently rare in practice.

Args:
filename_cc: is the path for the .cc file
filename_cc: is the path for the source (e.g. .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.
"""
fileinfo_cc = FileInfo(filename_cc)
if not fileinfo_cc.Extension().lstrip('.') in GetNonHeaderExtensions():
return (False, '')

if not filename_cc.endswith('.cc'):
fileinfo_h = FileInfo(filename_h)
if not fileinfo_h.Extension().lstrip('.') in GetHeaderExtensions():
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[:-(len(fileinfo_cc.Extension()))]
for suffix in _test_suffixes:
if filename_cc.endswith(suffix):
filename_cc = filename_cc[:-len(suffix)]
break

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')]
filename_h = filename_h[:-(len(fileinfo_h.Extension()))]
if filename_h.endswith('-inl'):
filename_h = filename_h[:-len('-inl')]
filename_h = filename_h.replace('/public/', '/')
Expand Down Expand Up @@ -5716,8 +5767,10 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
# 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
if not header_found:
for extension in GetNonHeaderExtensions():
if filename.endswith('.' + extension):
return

# All the lines have been processed, report the errors found.
for required_header_unstripped in required:
Expand Down Expand Up @@ -6056,7 +6109,7 @@ def ProcessFileData(filename, file_extension, lines, error,
RemoveMultiLineComments(filename, lines, error)
clean_lines = CleansedLines(lines)

if file_extension == 'h':
if file_extension in GetHeaderExtensions():
CheckForHeaderGuard(filename, clean_lines, error)

for line in range(clean_lines.NumLines()):
Expand All @@ -6069,7 +6122,7 @@ def ProcessFileData(filename, file_extension, lines, error,
CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)

# Check that the .cc file has included its header if it exists.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change also the comment accordingly

if file_extension == 'cc':
if file_extension in GetNonHeaderExtensions():
CheckHeaderFileIncluded(filename, include_state, error)

# We check here rather than inside ProcessLine so that we see raw
Expand Down Expand Up @@ -6136,6 +6189,24 @@ def ProcessConfigOverrides(filename):
_line_length = int(val)
except ValueError:
sys.stderr.write('Line length must be numeric.')
elif name == 'extensions':
global _valid_extensions
try:
extensions = [ext.strip() for ext in val.split(',')]
_valid_extensions = set(extensions)
except ValueError:
sys.stderr.write('Extensions should be a comma-separated list of values;'
'for example: extensions=hpp,cpp\n'
'This could not be parsed: "%s"' % (val,))
elif name == 'headers':
global _header_extensions
try:
extensions = [ext.strip() for ext in val.split(',')]
_header_extensions = set(extensions)
except ValueError:
sys.stderr.write('Extensions should be a comma-separated list of values;'
'for example: extensions=hpp,cpp\n'
'This could not be parsed: "%s"' % (val,))
else:
sys.stderr.write(
'Invalid configuration option (%s) in file %s\n' %
Expand Down Expand Up @@ -6213,9 +6284,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):

# When reading from stdin, the extension is unknown, so no cpplint tests
# should rely on the extension.
if filename != '-' and file_extension not in _valid_extensions:
if filename != '-' and file_extension not in GetAllExtensions():
sys.stderr.write('Ignoring %s; not a valid file name '
'(%s)\n' % (filename, ', '.join(_valid_extensions)))
'(%s)\n' % (filename, ', '.join(GetAllExtensions())))
else:
ProcessFileData(filename, file_extension, lines, Error,
extra_check_functions)
Expand Down Expand Up @@ -6282,7 +6353,8 @@ def ParseArguments(args):
'filter=',
'root=',
'linelength=',
'extensions='])
'extensions=',
'headers='])
except getopt.GetoptError:
PrintUsage('Invalid arguments.')

Expand Down Expand Up @@ -6323,6 +6395,12 @@ def ParseArguments(args):
_valid_extensions = set(val.split(','))
except ValueError:
PrintUsage('Extensions must be comma seperated list.')
elif opt == '--headers':
global _header_extensions
try:
_header_extensions = set(val.split(','))
except ValueError:
PrintUsage('Extensions must be comma seperated list.')

if not filenames:
PrintUsage('No files were specified.')
Expand Down
Loading