From dc8d06d2c5e056cefd9abe24247ac8db8973d27f Mon Sep 17 00:00:00 2001 From: sinoding Date: Sun, 1 May 2011 22:31:39 +0800 Subject: [PATCH] new file: ch21 --- ch21/AA | Bin 0 -> 10376 bytes ch21/AA.cc | 38 + ch21/cpplint.py | 3126 +++++++++++++++++++++++++++++++++ ch21/employer | Bin 0 -> 10361 bytes ch21/employer.cc | 38 + ch21/more_fancy | Bin 0 -> 26719 bytes ch21/more_fancy.cc | 51 + ch21/multi_inheritage.cc | 57 + ch21/non_trivial.cc | 112 ++ ch21/pointer_virtual | Bin 0 -> 10486 bytes ch21/pointer_virtual.cc | 30 + ch21/pure_virtual_function | Bin 0 -> 24604 bytes ch21/pure_virtual_function.cc | 39 + ch21/swindow.h | 39 + ch21/weak_inheritance | Bin 0 -> 8568 bytes ch21/weak_inheritance.cc | 43 + 16 files changed, 3573 insertions(+) create mode 100755 ch21/AA create mode 100644 ch21/AA.cc create mode 100755 ch21/cpplint.py create mode 100755 ch21/employer create mode 100644 ch21/employer.cc create mode 100755 ch21/more_fancy create mode 100644 ch21/more_fancy.cc create mode 100644 ch21/multi_inheritage.cc create mode 100644 ch21/non_trivial.cc create mode 100755 ch21/pointer_virtual create mode 100644 ch21/pointer_virtual.cc create mode 100755 ch21/pure_virtual_function create mode 100644 ch21/pure_virtual_function.cc create mode 100644 ch21/swindow.h create mode 100755 ch21/weak_inheritance create mode 100644 ch21/weak_inheritance.cc diff --git a/ch21/AA b/ch21/AA new file mode 100755 index 0000000000000000000000000000000000000000..5547514072abd3f342536c9145509c5b26194da7 GIT binary patch literal 10376 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 NHmbNh2qR~d_!kk|B+38) literal 0 HcmV?d00001 diff --git a/ch21/AA.cc b/ch21/AA.cc new file mode 100644 index 0000000..8f216eb --- /dev/null +++ b/ch21/AA.cc @@ -0,0 +1,38 @@ +#include + +using namespace std; + +class AA { + public: + virtual void something() { + std::cout << "AA::something()" << std::endl; + } +}; + +class BB : public AA { + public: + void something() { + std::cout << "BB::something()" << std::endl; + } +}; + +void byvalue(AA x) { + x.something(); +} + +void byref(AA &x) { + x.something(); +} + +void byporinter(AA *x) { + x->something(); +} + +int main(int argc, char **argv) { + BB b; + byvalue(b); + byref(b); + byporinter(&b); + + return 0; +} diff --git a/ch21/cpplint.py b/ch21/cpplint.py new file mode 100755 index 0000000..4f09042 --- /dev/null +++ b/ch21/cpplint.py @@ -0,0 +1,3126 @@ +#!/usr/bin/python2 +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Here are some issues that I've had people identify in my code during reviews, +# that I think are possible to flag automatically in a lint tool. If these were +# caught by lint, it would save time both for myself and that of my reviewers. +# Most likely, some of these are beyond the scope of the current lint framework, +# but I think it is valuable to retain these wish-list items even if they cannot +# be immediately implemented. +# +# Suggestions +# ----------- +# - Check for no 'explicit' for multi-arg ctor +# - Check for boolean assign RHS in parens +# - Check for ctor initializer-list colon position and spacing +# - Check that if there's a ctor, there should be a dtor +# - Check accessors that return non-pointer member variables are +# declared const +# - Check accessors that return non-const pointer member vars are +# *not* declared const +# - Check for using public includes for testing +# - Check for spaces between brackets in one-line inline method +# - Check for no assert() +# - Check for spaces surrounding operators +# - Check for 0 in pointer context (should be NULL) +# - Check for 0 in char context (should be '\0') +# - Check for camel-case method name conventions for methods +# that are not simple inline getters and setters +# - Check that base classes have virtual destructors +# put " // namespace" after } that closes a namespace, with +# namespace's name after 'namespace' if it is named. +# - Do not indent namespace contents +# - Avoid inlining non-trivial constructors in header files +# include base/basictypes.h if DISALLOW_EVIL_CONSTRUCTORS is used +# - Check for old-school (void) cast for call-sites of functions +# ignored return value +# - Check gUnit usage of anonymous namespace +# - Check for class declaration order (typedefs, consts, enums, +# ctor(s?), dtor, friend declarations, methods, member vars) +# + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] + [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. +""" + +# 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=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +# \ used for clearer layout -- pylint: disable-msg=C6013 +_ERROR_CATEGORIES = [ + 'build/class', + 'build/deprecated', + 'build/endif_comment', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/nolint', + 'readability/streams', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/rtti', + 'runtime/sizeof', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/virtual', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/indent', + 'whitespace/labels', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo' + ] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = [ '-build/include_alpha' ] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a seperate i18n file. + +# Headers that we consider STL headers. +_STL_HEADERS = frozenset([ + 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', + 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', + 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', + 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', + 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', + 'utility', 'vector', 'vector.h', + ]) + + +# Non-STL C++ system headers. +_CPP_HEADERS = frozenset([ + 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', + 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', + 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', + 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', + 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', + 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', + 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream.h', + 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', + 'numeric', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', + 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', + 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', + 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string', + 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', + ]) + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + m = _RE_SUPPRESSION.search(raw_line) + if m: + category = m.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparisson. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + header_path: Header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) + if self._last_header > canonical_header: + return False + self._last_header = canonical_header + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analizing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo: + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN? Try to find a git or hg top level directory by searching up + # from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """Returns true iff confidence >= verbose, category passes + filter and is not NOLINT-suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos] + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 3 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw member contains all the lines without processing. + All these three members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + for linenum in range(len(lines)): + self.lines.append(CleanseComments(lines[linenum])) + elided = self._CollapseStrings(lines[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in '({[': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': endchar = ')' + if startchar == '[': endchar = ']' + if startchar == '{': endchar = '}' + + num_open = line.count(startchar) - line.count(endchar) + while linenum < clean_lines.NumLines() and num_open > 0: + linenum += 1 + line = clean_lines.elided[linenum] + num_open += line.count(startchar) - line.count(endchar) + # OK, now find the endchar that actually got us back to even + endpos = len(line) + while num_open >= 0: + endpos = line.rfind(')', 0, endpos) + num_open -= 1 # chopped off another ) + return (line, linenum, endpos + 1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] "') + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + + fileinfo = FileInfo(filename) + return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_' + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef or not define or ifndef != define: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForUnicodeReplacementCharacters(filename, lines, error): + """Logs an error for each line containing Unicode replacement characters. + + These indicate that either the file contained invalid UTF-8 (likely) + or Unicode replacement characters (which it shouldn't). Note that + it's possible for this to throw off line numbering if the invalid + UTF-8 occurred adjacent to a newline. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. They\'re ' + 'ugly and unnecessary, and you should use concatenation instead".') + + +threading_list = ( + ('asctime(', 'asctime_r('), + ('ctime(', 'ctime_r('), + ('getgrgid(', 'getgrgid_r('), + ('getgrnam(', 'getgrnam_r('), + ('getlogin(', 'getlogin_r('), + ('getpwnam(', 'getpwnam_r('), + ('getpwuid(', 'getpwuid_r('), + ('gmtime(', 'gmtime_r('), + ('localtime(', 'localtime_r('), + ('rand(', 'rand_r('), + ('readdir(', 'readdir_r('), + ('strtok(', 'strtok_r('), + ('ttyname(', 'ttyname_r('), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...) for improved thread safety.') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +class _ClassInfo(object): + """Stores information about a class.""" + + def __init__(self, name, linenum): + self.name = name + self.linenum = linenum + self.seen_open_brace = False + self.is_derived = False + self.virtual_method_linenumber = None + self.has_virtual_destructor = False + self.brace_depth = 0 + + +class _ClassState(object): + """Holds the current state of the parse relating to class declarations. + + It maintains a stack of _ClassInfos representing the parser's guess + as to the current nesting of class declarations. The innermost class + is at the top (back) of the stack. Typically, the stack will either + be empty or have exactly one entry. + """ + + def __init__(self): + self.classinfo_stack = [] + + def CheckFinished(self, filename, error): + """Checks that all classes have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + if self.classinfo_stack: + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + error(filename, self.classinfo_stack[0].linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + self.classinfo_stack[0].name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + class_state, error): + """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Track class entry and exit, and attempt to find cases within the + # class declaration that don't meet the C++ style + # guidelines. Tracking is very dependent on the code matching Google + # style guidelines, but it seems to perform well enough in testing + # to be a worthwhile addition to the checks. + classinfo_stack = class_state.classinfo_stack + # Look for a class declaration + class_decl_match = Match( + r'\s*(template\s*<[\w\s<>,:]*>\s*)?(class|struct)\s+(\w+(::\w+)*)', line) + if class_decl_match: + classinfo_stack.append(_ClassInfo(class_decl_match.group(3), linenum)) + + # Everything else in this function uses the top of the stack if it's + # not empty. + if not classinfo_stack: + return + + classinfo = classinfo_stack[-1] + + # If the opening brace hasn't been seen look for it and also + # parent class declarations. + if not classinfo.seen_open_brace: + # If the line has a ';' in it, assume it's a forward declaration or + # a single-line class declaration, which we won't process. + if line.find(';') != -1: + classinfo_stack.pop() + return + classinfo.seen_open_brace = (line.find('{') != -1) + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', line): + classinfo.is_derived = True + if not classinfo.seen_open_brace: + return # Everything else in this function is for after open brace + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + args = Match(r'(? 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + +def CheckSpacing(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't have too many + blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + raw = clean_lines.raw_lines + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + if IsBlankLine(line): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if (prevbrace != -1 and prev_line[prevbrace:].find('}') == -1 + and prev_line[:prevbrace].find('namespace') == -1): + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the paramters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Blank line at the start of a code block. Is this needed?') + # This doesn't ignore whitespace at the end of a namespace block + # because that is too hard without pairing open/close braces; + # however, a special exception is made for namespace closing + # brackets which have a comment containing "namespace". + # + # Also, ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('namespace') == -1 + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Blank line at the end of a code block. Is this needed?') + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not Match(r'^\s*{ //', line) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # Alas, we can't test < or > because they're legitimately used sans spaces + # (a->b, vector a). The only time we can tell is a < with no >, and + # only if it's not template params list spilling into the next line. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if not match: + # Note that while it seems that the '<[^<]*' term in the following + # regexp could be simplified to '<.*', which would indeed match + # the same class of strings, the [^<] means that searching for the + # regexp takes linear rather than quadratic time. + if not Search(r'<[^<]*,\s*$', line): # template params spill + match = Search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + # We allow no-spaces around << and >> when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + match = Search(r'[^0-9\s](<<|>>)[^0-9\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if not len(match.group(2)) in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or operator) + if Search(r',[^\s]', line): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Except after an opening paren, you should have spaces before your braces. + # And since you should never have braces at the beginning of a line, this is + # an easy test. + if Search(r'[^ (]{', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use { } instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use { } instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use { } instead.') + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, + # which is commonly used to control the lifetime of + # stack-allocated variables. We don't detect this perfectly: we + # just don't complain if the last non-whitespace character on the + # previous non-blank line is ';', ':', '{', or '}'. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if not Search(r'[;:}{]\s*$', prevline): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Braces shouldn't be followed by a ; unless they're defining a struct + # or initializing an array. + # We can't tell in general, but we can for some common cases. + prevlinenum = linenum + while True: + (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) + if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): + line = prevline + line + else: + break + if (Search(r'{.*}\s*;', line) and + line.count('{') == line.count('}') and + not Search(r'struct|class|enum|\s*=\s*{', line)): + error(filename, linenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def ReplaceableCheck(operator, macro, line): + """Determine whether a basic CHECK can be replaced with a more specific one. + + For example suggest using CHECK_EQ instead of CHECK(a == b) and + similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. + + Args: + operator: The C++ operator used in the CHECK. + macro: The CHECK or EXPECT macro being called. + line: The current source line. + + Returns: + True if the CHECK can be replaced with a more specific one. + """ + + # This matches decimal and hex integers, strings, and chars (in that order). + match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' + + # Expression to match two sides of the operator with something that + # looks like a literal, since CHECK(x == iterator) won't compile. + # This means we can't catch all the cases where a more specific + # CHECK is possible, but it's less annoying than dealing with + # extraneous warnings. + match_this = (r'\s*' + macro + r'\((\s*' + + match_constant + r'\s*' + operator + r'[^<>].*|' + r'.*[^<>]' + operator + r'\s*' + match_constant + + r'\s*\))') + + # Don't complain about CHECK(x == NULL) or similar because + # CHECK_EQ(x, NULL) won't compile (requires a cast). + # Also, don't complain about more complex boolean expressions + # involving && or || such as CHECK(a == b || c == d). + return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + raw_lines = clean_lines.raw_lines + current_macro = '' + for macro in _CHECK_MACROS: + if raw_lines[linenum].find(macro) >= 0: + current_macro = macro + break + if not current_macro: + # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + return + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. + for operator in ['==', '!=', '>=', '>', '<=', '<']: + if ReplaceableCheck(operator, current_macro, line): + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[current_macro][operator], + current_macro, operator)) + break + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for c in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(c) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(c): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + error: The function to call with any errors found. + """ + + raw_lines = clean_lines.raw_lines + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for labels + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + # Labels should always be indented at least one space. + elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', + line): + error(filename, linenum, 'whitespace/labels', 4, + 'Labels should always be indented at least one space. ' + 'If this is a member-initializer list in a constructor or ' + 'the base class list in a class definition, the colon should ' + 'be on the following line.') + + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line)): + line_width = GetLineWidth(line) + if line_width > 100: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than 100 characters') + elif line_width > 80: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= 80 characters long') + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 4, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckSpacing(filename, clean_lines, linenum, error) + CheckCheck(filename, clean_lines, linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input 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 + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_stl_h = include in _STL_HEADERS + is_cpp_h = is_stl_h or include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + if not include_state.IsInAlphabeticalOrder(include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + + # Look for any of the stream classes that are part of standard C++. + match = _RE_PATTERN_INCLUDE.match(line) + if match: + include = match.group(2) + if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): + # Many unit tests use cout, so we exempt them. + if not _IsTestFilename(filename): + error(filename, linenum, 'readability/streams', 3, + 'Streams are highly discouraged.') + +def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, + error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Create an extended_line, which is the concatenation of the current and + # next lines, for more effective checking of code that may span more than one + # line. + if linenum + 1 < clean_lines.NumLines(): + extended_line = line + clean_lines.elided[linenum + 1] + else: + extended_line = line + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check for non-const references in functions. This is tricky because & + # is also used to take the address of something. We allow <> for templates, + # (ignoring whatever is between the braces) and : for classes. + # These are complicated re's. They try to capture the following: + # paren (for fn-prototype start), typename, &, varname. For the const + # version, we're willing for const to be before typename or after + # Don't check the implemention on same line. + fnline = line.split('{', 1)[0] + if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > + len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' + r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', + fnline))): + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". + if not Search( + r'(swap|Swap|operator[<>][<>])\s*\(\s*(?:[\w:]|<.*>)+\s*&', + fnline): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer.') + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there + r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + if match: + # gMock methods are defined using some variant of MOCK_METHODx(name, type) + # where type may be float(), int(string), etc. Without context they are + # virtually indistinguishable from int(x) casts. + if (match.group(1) is None and # If new operator, then this isn't a cast + not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + match.group(2)) + + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', + error) + # This doesn't catch all cases. Consider (const char * const)"hello". + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + if Search( + r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + # Make sure it's not a function. + # Function template specialization looks like: "string foo(...". + # Class template definitions look like: "string Foo::Method(...". + if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', + match.group(3)): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + # Check that we're not using RTTI outside of testing code. + if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): + error(filename, linenum, 'runtime/rtti', 5, + 'Do not use dynamic_cast<>. If you need to cast within a class ' + "hierarchy, use static_cast<> to upcast. Google doesn't support " + 'RTTI.') + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + if Search(r'\bsscanf\b', line): + error(filename, linenum, 'runtime/printf', 1, + 'sscanf can be ok, but is slow and can overflow buffers.') + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + match = re.search(r'\b((?:string)?printf)\s*\(([\w.\->()]+)\)', line, re.I) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (match.group(1), match.group(2))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token becasue we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or + # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing + # in the class declaration. + match = Match( + (r'\s*' + r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' + r'\(.*\);$'), + line) + if match and linenum + 1 < clean_lines.NumLines(): + next_line = clean_lines.elided[linenum + 1] + if not Search(r'^\s*};', next_line): + error(filename, linenum, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + + # 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' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, + error): + """Checks for a C-style cast by looking for the pattern. + + This also handles sizeof(type) warnings, due to similarity of content. + + Args: + filename: The name of the current file. + linenum: The number of the line to check. + line: The line of code to check. + raw_line: The raw line of code to check, with comments. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast or static_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + """ + match = Search(pattern, line) + if not match: + return + + # e.g., sizeof(int) + sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) + if sizeof_match: + error(filename, linenum, 'runtime/sizeof', 1, + 'Using sizeof(type). Use sizeof(varname) instead if possible') + return + + remainder = line[match.end(0):] + + # The close paren is for function pointers as arguments to a function. + # eg, void foo(void (*bar)(int)); + # The semicolon check is a more basic function check; also possibly a + # function pointer typedef. + # eg, void foo(int); or void foo(int) const; + # The equals check is for function pointer assignment. + # eg, void *(*foo)(int) = ... + # + # Right now, this will only catch cases where there's a single argument, and + # it's unnamed. It should probably be expanded to check for multiple + # arguments with some unnamed. + function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)))', remainder) + if function_match: + if (not function_match.group(3) or + function_match.group(3) == ';' or + raw_line.find('/*') < 0): + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('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', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator',)), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_ACCEPTED_BUT_NOT_PROMOTED = { + # We can trust with reasonable confidence that map gives us pair<>, too. + 'pair<>': ('map', 'multimap', 'hash_map', 'hash_multimap') +} + +_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(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '')) + +_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 . + + 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: { '': (1219, 'less<>') } + + for linenum in xrange(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. + m = _RE_PATTERN_STRING.search(line) + if m: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:m.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (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 = os.path.abspath(filename) + + # 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. + for header in include_state.keys(): #NOLINT + (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 template in _HEADERS_ACCEPTED_BUT_NOT_PROMOTED: + headers = _HEADERS_ACCEPTED_BUT_NOT_PROMOTED[template] + if [True for header in headers if header in include_state]: + continue + 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, include_state, function_state, + class_state, error): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + class_state: A _ClassState instance which maintains information about + the current stack of nested class declarations being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + error) + CheckForNonStandardConstructs(filename, clean_lines, line, + class_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + + +def ProcessFileData(filename, file_extension, lines, error): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is termined with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + class_state = _ClassState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, class_state, error) + class_state.CheckFinished(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForUnicodeReplacementCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessFile(filename, vlevel): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if (filename != '-' and file_extension != 'cc' and file_extension != 'h' + and file_extension != 'cpp'): + sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) + else: + ProcessFileData(filename, file_extension, lines, Error) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputing only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + sys.stderr.write('Done processing %s\n' % filename) + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if not val in ('emacs', 'vs7'): + PrintUsage('The only allowed output formats are emacs and vs7.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/ch21/employer b/ch21/employer new file mode 100755 index 0000000000000000000000000000000000000000..059b282b4f155d930d68758730c010a0344d74ea GIT binary patch literal 10361 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 wjUJn4;~YL2 + +using namespace std; + +class Employer { + public: + Employer(char *n) : name(n) {} + // virtual tells the compiler that any call to this function + // has to trace the real type of the object + virtual void print(ostream &os) const { + os << name; + } + private: + char *name; +}; + +ostream &operator << (ostream &os, + const Employer &e) { + e.print(os); + return os; +} + +class RichGuy : public Employer { + public: + RichGuy(char *n, int s) : Employer(n), stocks(s) {} + void print(ostream &os) const { + Employer::print(os); + os << " (this guy has " << stocks << " stocks!!)"; + } + private: + int stocks; +}; + +int main(int argc, char **argv) { + RichGuy bill("Bill Gates", 1576354987); + std::cout << bill << std::endl; + return 0; +} diff --git a/ch21/more_fancy b/ch21/more_fancy new file mode 100755 index 0000000000000000000000000000000000000000..ff69941c83b58cf2f8137b206c2af1d60acf14de GIT binary patch literal 26719 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 M&r|3f<*JtdFWxGwi~s-t literal 0 HcmV?d00001 diff --git a/ch21/more_fancy.cc b/ch21/more_fancy.cc new file mode 100644 index 0000000..9be5725 --- /dev/null +++ b/ch21/more_fancy.cc @@ -0,0 +1,51 @@ +#include +#include + +using namespace std; + +class Function { + public: + // This is pure virtual + virtual double eval(double x) = 0; + // This calls the pure one + double derivative(double x, double e) { + std::cout << "Function::derivative" << std::endl; + return (eval(x+e) - eval(x-e))/(2*e); + } + // Let's define a derivative by default + virtual double derivative(double x) { + return derivative(x, 0.001); + } +}; + +class Oscillating : public Function { + public: + Oscillating(double aa, double bb, double cc) : a(aa), b(bb), c(cc) {} + double eval(double x) { + return a*sin(b*x+c); + } + private: + double a, b, c; +}; + +class Quadratic : public Function { + public: + Quadratic(double aa, double bb, double cc) :a(aa),b(bb),c(cc) {} + double eval(double x) { + return c + (b + a*x)*x; + } + double derivative(double x) { + return b + 2*a*x; + } + private: + double a, b, c; +}; + +int main(int argc, char **argv) { + Oscillating f(2, 3, 4); + std::cout << f.derivative(2) << std::endl; + + Quadratic q(5, 4, 5); + std::cout << q.derivative(2) << std::endl; + return 0; +} diff --git a/ch21/multi_inheritage.cc b/ch21/multi_inheritage.cc new file mode 100644 index 0000000..ec1f069 --- /dev/null +++ b/ch21/multi_inheritage.cc @@ -0,0 +1,57 @@ +class Polynomial { + public: + Polynomial(double *c, int d) : coeff(new double[d+1]), degree(d) { + for (int k = 0; k <= d; k++) { + coeff[k] = c[k]; + } + } + double value(double x) { + double r = 0; + for (int k = degree; k >= 0; k--) { + r = coeff[k] + x*r; + } + return r; + } + protected: + // We suspect we'll need those field in subclasses + double *coeff; + int degree; +}; + +class Function { + public: + // This is pure virtual + virtual double eval(double x) = 0; + // This calls the pure one + double derivative(double x, double e) { + return (eval(x+e) - eval(x-e))/(2*e); + } + // Let's define a derivate by default + virtual double derivative(double x) { + return derivative(x, 0.001); + } +}; + +class FunctionPoly : public Function, public Polynomial { + public: + FunctionPoly(double *c, int d) : Polynomial(c, d) {} + double eval(double x) { + return value(x); + } +}; + +// Let's implement amalytic derivative now +class FunctionPolyAD : public FunctionPoly { + public: + FunctionPolyAD(double *c, int d) : FunctionPoly(c, d) {} + double derivative(double x) { + double r = 0; + for (int k = degree; k >= 1; k--) { + r = k*coeff[k] + x*r; + } + return r; + } +}; + + + diff --git a/ch21/non_trivial.cc b/ch21/non_trivial.cc new file mode 100644 index 0000000..114d5ca --- /dev/null +++ b/ch21/non_trivial.cc @@ -0,0 +1,112 @@ +#include +#include "swindow.h" + +using namespace std; + +class GraphicalObject { + public: + virtual int width() = 0; + virtual int height() = 0; + virtual void draw(SimpleWindow &win, int x0, int y0) = 0; +}; + +class Circle : public GraphicalObject { + public: + Circle(int rr) : r(rr) {} + int width() { + return 2*r; + } + int heigth() { + return 2*r; + } + void draw(SimpleWindow &win, int x0, int y0) { + win.color(0.0, 0.0, 0.0); + win.drawCircle(x0+r, y0+r, r); + } + private: + int r; +}; + +class Rectangle : public GraphicalObject { + public: + Rectangle(int ww, int hh) : w(ww), h(hh) {} + int width() { + return w; + } + int height() { + return h; + } + void draw(SimpleWindow &win, int x0, int y0) { + win.color(0.0, 0.0, 0.0); + win.drawLine(x0, y0, x0+w, y0); + win.drawLine(x0+w, y0, x0+w, y0+h); + win.drawLine(x0+w, y0+h, x0, y0+h); + win.drawLine(x0, y0+h, x0, y0); + } + private: + int w, h; +}; + +class Couple : public GraphicalObject { + public: + Couple(bool v, + GraphicalObject *oo1, + GraphicalObject *oo2) : vertical(v), o1(oo1), o2(oo2) {} + ~Couple() { + delete o1; + delete o2; + } + int max(int a, int b) { + if (a >= b) { + return a; + } else { + return b; + } + } + + int width() { + if (vertical) { + return max(o1->width(), o2->width()); + } else { + return o1->width() + o2->width(); + } + } + + int height() { + if (vertical) { + return o1->height() + o2->height(); + } else { + max(o1->height(), o2->height()); + } + } + + void draw(SimpleWindow &win, int x0, int y0) { + o1->draw(win, x0, y0); + if (vertical) { + return o2->draw(win, x0, y0+o1->height()); + } else { + o2->draw(win, x0 + o1->width(), y0); + } + } + private: + bool vertical; + GraphicalObject *o1, *o2; +}; + +int main(int argc, char **argv) { + GraphicalObject *g1 = new Rectangle(100, 50); + // GraphicalObject *g2 = new Circle(80); + // GraphicalObject *g3 = new Couple(true, g1, g2); + // GraphicalObject *g4 = new Circle(34); + // GraphicalObject *g5 = new Couple(false, g3, g4); + SimpleWindow window("GraphicObject", g1->width(), g1->height()); + window.color(1.0, 1.0, 1.0); + window.fill(); + g1->draw(window, 0, 0); + window.show(); + cin.get(); + delete g1; + + return 0; +} + diff --git a/ch21/pointer_virtual b/ch21/pointer_virtual new file mode 100755 index 0000000000000000000000000000000000000000..e30b16bf7a9b0e94795363d199c61cb1d1e69aed GIT binary patch literal 10486 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 W){z{S?UY_q)#rRFOYEtxyZRq71}NA7 literal 0 HcmV?d00001 diff --git a/ch21/pointer_virtual.cc b/ch21/pointer_virtual.cc new file mode 100644 index 0000000..f348dce --- /dev/null +++ b/ch21/pointer_virtual.cc @@ -0,0 +1,30 @@ +#include + +using namespace std; + +class ReallyVirtual { + public: + virtual double operation(double x, double y) = 0; + double twiceTheOpertation(double x, double y) { + return 2.0*operation(x, y); + } +}; + +class NotVirtualAnymore : public ReallyVirtual { + public: + NotVirtualAnymore(double l) : k(l) {} + double operation(double x, double y) { + return x+k*y; + } + private: + double k; +}; + +int main(int argc, char **argv) { + ReallyVirtual *f = new NotVirtualAnymore(3.5); + double x = f->twiceTheOpertation(4.3, -8.9); + std::cout << x << std::endl; + delete f; + + return 0; +} diff --git a/ch21/pure_virtual_function b/ch21/pure_virtual_function new file mode 100755 index 0000000000000000000000000000000000000000..60a3a6a97a4db3032123536c4348d87f0af0b7b5 GIT binary patch literal 24604 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 N#BWy1>09Hy{|0MqyH5ZB literal 0 HcmV?d00001 diff --git a/ch21/pure_virtual_function.cc b/ch21/pure_virtual_function.cc new file mode 100644 index 0000000..a7cc37b --- /dev/null +++ b/ch21/pure_virtual_function.cc @@ -0,0 +1,39 @@ +#include +#include + +using namespace std; + +class Function { + public: + // This is pure virtual + virtual double eval(double x) = 0; + + // This calls the pure one + double derivative(double x, double e) { + return (eval(x+e) - eval(x-e))/(2*e); + } +}; + +class Oscillating : public Function { + public: + Oscillating(double aa, double bb, double cc) + :a(aa), b(bb), c(cc) {} + double eval(double x) { + return a*sin(b*x+c); + } + private: + double a, b, c; +}; + +int main(int argc, char **argv) { + Oscillating f(1, 1, 0); + cout << f.derivative(0, 0.1) << endl; + cout << f.derivative(0, 0.01) << endl; + cout << f.derivative(0, 0.001) << endl; + Function f1; + // let's mess the complier! + std::cout << f1.eval(3.0) << std::endl; + return 0; +} + + diff --git a/ch21/swindow.h b/ch21/swindow.h new file mode 100644 index 0000000..d2cf675 --- /dev/null +++ b/ch21/swindow.h @@ -0,0 +1,39 @@ +#ifndef SWINDOW_H +#define SWINDOW_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class SimpleWindow { + Display *dpy; + Window window; + Pixmap pixmap; + GC gc; + +protected: + int width, height; + +public: + SimpleWindow(char *name, int w, int h); + ~SimpleWindow(); + + int getWidth(); + int getHeight(); + + void color(int red, int green, int blue); + void drawPoint(int x, int y); + void drawLine(int x1, int y1, int x2, int y2); + void drawCircle(int x, int y, int r); + void show(); + void fill(); +}; + +#endif diff --git a/ch21/weak_inheritance b/ch21/weak_inheritance new file mode 100755 index 0000000000000000000000000000000000000000..dfb7d3977f6aee85f723f1052f2f81800ab6db0f GIT binary patch literal 8568 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 + +using namespace std; + +class FirstClass +{ + + public: + // FirstClass() + // { } + // ~FirstClass() + // { } + // private: + void something() { + std::cout << "FirstClass::something()" << std::endl; + } + +}; + +class SecondClass : public FirstClass +{ + + public: + // SecondClass() + // { } + // ~SecondClass() + // { } + // private: + void something() { + std::cout << "SecondClass::something()" << std::endl; + } +}; + +void niceFunction(FirstClass y) { + y.something(); +} + +int main(int argc, char **argv) { + SecondClass x; + x.something(); + niceFunction(x); + return 0; +}