From 32c95fa0dacce195d088bca0d6276800df299397 Mon Sep 17 00:00:00 2001 From: Tyler Veness Date: Sun, 21 Aug 2016 10:31:43 -0700 Subject: [PATCH] Moved style guide documents and scripts to wpilibsuite/styleguide (#207) --- .styleguide | 38 + CONTRIBUTING.md | 2 +- README.md | 2 +- styleguide/clangformat.py | 15 - styleguide/cppguide.html | 2424 -------- styleguide/cpplint.py | 6115 --------------------- styleguide/eclipse-cpp-google-style.xml | 167 - styleguide/eclipse-java-google-style.xml | 337 -- styleguide/format.py | 127 +- styleguide/include/link.png | Bin 189 -> 0 bytes styleguide/include/styleguide.css | 261 - styleguide/include/styleguide.js | 289 - styleguide/intellij-java-google-style.xml | 475 -- styleguide/javaguide.css | 515 -- styleguide/javaguide.html | 806 --- styleguide/javaguidelink.png | Bin 189 -> 0 bytes styleguide/licenseupdate.py | 117 - styleguide/lint.py | 76 - styleguide/newline.py | 35 - styleguide/styleguide.css | 146 - styleguide/styleguide.xsl | 923 ---- styleguide/task.py | 100 - styleguide/whitespace.py | 23 - 23 files changed, 56 insertions(+), 12937 deletions(-) create mode 100644 .styleguide delete mode 100644 styleguide/clangformat.py delete mode 100644 styleguide/cppguide.html delete mode 100644 styleguide/cpplint.py delete mode 100644 styleguide/eclipse-cpp-google-style.xml delete mode 100644 styleguide/eclipse-java-google-style.xml delete mode 100644 styleguide/include/link.png delete mode 100644 styleguide/include/styleguide.css delete mode 100644 styleguide/include/styleguide.js delete mode 100644 styleguide/intellij-java-google-style.xml delete mode 100644 styleguide/javaguide.css delete mode 100644 styleguide/javaguide.html delete mode 100644 styleguide/javaguidelink.png delete mode 100644 styleguide/licenseupdate.py delete mode 100644 styleguide/lint.py delete mode 100644 styleguide/newline.py delete mode 100644 styleguide/styleguide.css delete mode 100644 styleguide/styleguide.xsl delete mode 100644 styleguide/task.py delete mode 100644 styleguide/whitespace.py diff --git a/.styleguide b/.styleguide new file mode 100644 index 0000000000..f910fc7923 --- /dev/null +++ b/.styleguide @@ -0,0 +1,38 @@ +genFolderExclude { + FRC_FPGA_ChipObject + NetworkCommunication + ctre + frccansae + gtest + i2clib + msgs + ni-libraries + ni/vision + spilib + wpilibj/src/athena/cpp/nivision + visa +} + +genFileExclude { + CanTalonSRX\.h$ + NIIMAQdx\.h$ + can_proto\.h$ + nivision\.h$ +} + +modifiableFolderExclude { + \.git + \.gradle + __pycache__ + build + wpilibj/src/athena/cpp/include + wpilibj/src/athena/cpp/lib +} + +modifiableFileExclude { + \.jar$ + \.patch$ + \.png$ + \.py$ + \.so$ +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6883e0bf5..614cf9ec01 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont ## Coding Guidelines -WPILib uses modified Google style guides for both C++ and Java, which can be found in the style guide directory of the repository. Autoformatters are available for many popular editors at https://github.com/google/styleguide. Another option for C++ is running format.py in the style guide directory. +WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Another option for C++ is running format.py in the style guide directory. While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome. diff --git a/README.md b/README.md index 6d0283813f..0689578c95 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ The integration test directories for C++ and Java contain test code that runs on The hal directory contains more C++ code meant to run on the roboRIO. HAL is an acronym for "Hardware Abstraction Layer", and it interfaces with the NI Libraries. The NI Libraries contain the low-level code for controlling devices on your robot. The NI Libraries are found in the ni-libraries folder. -The styleguide directory contains the styleguide for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines). +The [styleguide repository](https://github.com/wpilibsuite/styleguide) contains our style guides for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines). # Contributing to WPILib diff --git a/styleguide/clangformat.py b/styleguide/clangformat.py deleted file mode 100644 index d61bd0accc..0000000000 --- a/styleguide/clangformat.py +++ /dev/null @@ -1,15 +0,0 @@ -# This task runs clang-format on all C++ source files. - -import subprocess -import sys -from task import Task - -class ClangFormat(Task): - def getIncludeExtensions(self): - return ["cpp", "h", "inc"] - - def run(self, name): - # Run clang-format - if subprocess.call(["clang-format", "-i", "-style=file", name]) == -1: - print("Error: clang-format not found in PATH. Is it installed?", - file = sys.stderr) diff --git a/styleguide/cppguide.html b/styleguide/cppguide.html deleted file mode 100644 index 4d80a13596..0000000000 --- a/styleguide/cppguide.html +++ /dev/null @@ -1,2424 +0,0 @@ - - - - -WPILib C++ Style Guide - - - - -
-

WPILib C++ Style Guide (Based on the Google C++ Style Guide)

-
- -
- - - -

This guide is a work in progress. -We are currently working on getting this guide updated to -a point where it is useful for WPILib developers to use.

- -

C++ is one of the two main languages (Java being the other) -used in WPILib; in order to maintain consistency and keep the -maintenance of the code manageable, we use this style guide.

- -

There are two main overarching purposes to this guide. The first -is to act as a normal C++ style guide (both in terms fo formatting -and programming practices) for C++ developers of WPILib. -The other purpose is to help Java programmers who may -know a moderate amount of C++ but may not be fully -up to date with things like C++11 and so may not even -realize that certain C++ features exist.

- -

This style guide is a heavily modified version of the - -Google C++ Style Guide. The Google Style Guide has -a lot of good points and is a good read, but in order -to cut the style guide down to a more readable size and to -focus mroe on WPILib-specific information, we have -altetered the original style guide in several ways.

- -

One way in which we haven't done much to -alter the original style guide is to keep the vast -majority of the formatting/naming/etc. related -information intact. This is both so that we -do not have to write up our own standards and so -that existing tools such as clang-format and -the Google eclipse format configuration files -can work out of the box. All of these things -should be relatively non-controversial and do not -require much discussion.

- -

Where we deviate more from the original guide is -in the style of the code itself. At the moment (ie, -when we first created this modified version), we -deleted all of the sections of the original guide -which mandate particular programming practices -such as forbidding exceptions, multiple inheritance, -etc. However, as time goes on, we gradually add in more -information along this lines, either by copying -directly from Google's Style Guide or by writing -our own decisions and best practices, some of which -may be very WPILib-specific.

- -

As the original guide makes very clear, consistency -is extremely important to keeping the code base -manageable, and so we encourage that, wherever -reasonable, that you keep everything consistent -with whatever the standard style is.

- -

Along with just C++ style, it is also important -to keep in mind that WPILib consists of both a C++ -and Java half. In order to keep things consistent -and easier for users, we ask that, in general, -Java and C++ be kept as consistent with one another -as reasonable. This includes everything from using -two spaces for indentation in both language to -keeping the inheritance structure essentially the -same in both. Although the two do not have to be -precisely the same, it does mean that if there is -something that you are doing which will be imposssible -to reproduce in some way in Java, then you may -want to reconsider.

- -

One final thing to remember is that High School -students with relatively little experience programming -are the main user for this code, and throwing the full -brunt of C++ at a student just learning how to program -is likely not the best of ideas. As such, any -user-facing APIs should minimize the use of any -more complicated C++ features. As always, -use your judgement and ask others in cases where -there is something which may violate anything -in this guide.

- -

Programming Guidelines

-

C++ is a large, complicated language, and in order -to ensure that we stay consistent and maintain certain -best practices, we have certain rules. For the most part -these are common sense rules and in some cases exist -solely to point out features of C++ that someone more -familiar with Java may not realize even exist.

- -

Pointers

-

In general, we strongly discourage the use of -raw pointers in C++ code; instead, references or -STL pointers should be used where appropriate. -There are two exceptions to this rule:

-
    -
  • When interfacing with lower-level C code or - with any libraries which force you to use raw pointers.
  • -
  • In order to keep user interfaces consistent, - we may keep around deprecated functions which - take raw pointers. Any user-facing functions - which take raw pointers should be deprecated - using the - [[deprecated]] - attribute and replaced with either references - or STL pointers.
  • -
-

As of C++11, the following are options in the -place of raw pointers:

-
    -
  • std::unique_ptr Should be used - when you still need to use a pointer, but you - only need one entity to own the pointer. The - std::unique_ptr will automatically - be deleted when there are no more references to - it.
  • -
  • std::shared_ptr Should be used - when you still need to use a pointer and you - need many references to the object. When - there are zero remaining references to the - object, it will be deleted. Use std::weak_ptr - where necessary to avoid circular dependencies - or other potential issues.
  • -
  • L-value references (the traditional sort - of reference that has been around since before C++11) - should be used when you want to pass around a - reference to an object and want to guarantee - that it won't be null. Use const references - if you want to avoid copying a large object - but don't want to modify it.
  • -
  • R-value references were introduced in C++11 - and allow for the use of std::move. - R-value references should be used where it makes - sense that a parameter to a function is having - its ownership passed from one place to another. - In general, R-value references are not inherently - bad, but they do introduce additional complexity - that may confuse people who are not familiar - with them.
  • -
- -

Deprecation

-

When updating APIs, make liberal use of the -[[deprecated]] attribute (although if -it is reasonable to simply remove any old interfaces -then do so) to indicate that users should no longer -use the function. Currently, this will cause warnings -in user code and errors in the WPILib build.

- -
-[[deprecated("This is a deprecated function; this text will be displayed when"
-             " the compiler throws a warning.")]]
-void foo() {}
-class [[deprecated("This is a deprecated class.")]] Foo {};
-int bar [[deprecated("This is a deprecated variable.")]];
-
- -

See -here for more information on deprecation.

- -

Header Files

- -

In general, every .cc file should have an -associated .h file. There are some common -exceptions, such as unittests and -small .cpp files containing just a -main() function.

- -

Correct use of header files can make a huge difference to -the readability, size and performance of your code.

- -

The following rules will guide you through the various -pitfalls of using header files.

- - -

Self-contained Headers

- -
-

Header files should be self-contained and end in .h. Files that -are meant for textual inclusion, but are not headers, should end in -.inc. Separate -inl.h headers are disallowed.

-
- -
-

All header files should be self-contained. In other -words, users and refactoring tools should not have to adhere to special -conditions in order to include the header. Specifically, a -header should have #pragma once, -should include all other headers it needs, and should not require any -particular symbols to be defined.

- -

There are rare cases where a file is not meant to be self-contained, but -instead is meant to be textually included at a specific point in the code. -Examples are files that need to be included multiple times or -platform-specific extensions that essentially are part of other headers. Such -files should use the file extension .inc.

- -

If a template or inline function is declared in a .h file, -define it in that same file. The definitions of these constructs must -be included into every .cc file that uses them, or the -program may fail to link in some build configurations. Do not move these -definitions to separate -inl.h files.

- -

As an exception, a function template that is explicitly -instantiated for all relevant sets of template arguments, or -that is a private member of a class, may -be defined in the only .cc file that -instantiates the template.

- -
- -

#pragma once

- -
-

All header files should have #pragma once at the top to -prevent multiple inclusion. -

- -

Forward Declarations

- -
-

You may forward declare ordinary classes in order to avoid -unnecessary #includes.

-
- -
- -
-

A "forward declaration" is a declaration of a class, -function, or template without an associated definition. -#include lines can often be replaced with -forward declarations of whatever symbols are actually -used by the client code.

-
- -
-
    -
  • Unnecessary #includes force the - compiler to open more files and process more - input.
  • - -
  • They can also force your code to be recompiled more - often, due to changes in the header.
  • -
-
- -
-
    -
  • It can be difficult to determine the correct form - of a forward declaration in the presence of features - like templates, typedefs, default parameters, and using - declarations.
  • - -
  • It can be difficult to determine whether a forward - declaration or a full #include is needed - for a given piece of code, particularly when implicit - conversion operations are involved. In extreme cases, - replacing an #include with a forward - declaration can silently change the meaning of - code.
  • - -
  • Forward declaring multiple symbols from a header - can be more verbose than simply - #includeing the header.
  • - -
  • Forward declarations of functions and templates can - prevent the header owners from making - otherwise-compatible changes to their APIs; for - example, widening a parameter type, or adding a - template parameter with a default value.
  • -
  • Forward declaring symbols from namespace - std:: usually yields undefined - behavior.
  • - -
  • Structuring code to enable forward declarations - (e.g. using pointer members instead of object members) - can make the code slower and more complex.
  • - -
  • The practical efficiency benefits of forward - declarations are unproven.
  • -
-
- -
-
    -
  • When using a function declared in a header file, - always #include that header.
  • - -
  • When using a class template, prefer to - #include its header file.
  • - -
  • When using an ordinary class, relying on a forward - declaration is OK, but be wary of situations where a - forward declaration may be insufficient or incorrect; - when in doubt, just #include the - appropriate header.
  • - -
  • Do not replace data members with pointers just to - avoid an #include.
  • -
- -

Please see Names and Order -of Includes for rules about when to #include a header.

-
- -
- -

Inline Functions

- -
-

Define functions inline only when they are small, say, 10 -lines or less.

-
- -
- -
-

You can declare functions in a way that allows the compiler to expand -them inline rather than calling them through the usual -function call mechanism.

-
- -
-

Inlining a function can generate more efficient object -code, as long as the inlined function is small. Feel free -to inline accessors and mutators, and other short, -performance-critical functions.

-
- -
-

Overuse of inlining can actually make programs slower. -Depending on a function's size, inlining it can cause the -code size to increase or decrease. Inlining a very small -accessor function will usually decrease code size while -inlining a very large function can dramatically increase -code size. On modern processors smaller code usually runs -faster due to better use of the instruction cache.

-
- -
-

A decent rule of thumb is to not inline a function if -it is more than 10 lines long. Beware of destructors, -which are often longer than they appear because of -implicit member- and base-destructor calls!

- -

Another useful rule of thumb: it's typically not cost -effective to inline functions with loops or switch -statements (unless, in the common case, the loop or -switch statement is never executed).

- -

It is important to know that functions are not always -inlined even if they are declared as such; for example, -virtual and recursive functions are not normally inlined. -Usually recursive functions should not be inline. The -main reason for making a virtual function inline is to -place its definition in the class, either for convenience -or to document its behavior, e.g., for accessors and -mutators.

-
- -
- -

Function Parameter Ordering

- -
-

When defining a function, parameter order is: inputs, then -outputs.

-
- -
-

Parameters to C/C++ functions are either input to the -function, output from the function, or both. Input -parameters are usually values or const -references, while output and input/output parameters will -be non-const pointers. When ordering -function parameters, put all input-only parameters before -any output parameters. In particular, do not add new -parameters to the end of the function just because they -are new; place new input-only parameters before the -output parameters.

- -

This is not a hard-and-fast rule. Parameters that are -both input and output (often classes/structs) muddy the -waters, and, as always, consistency with related -functions may require you to bend the rule.

- -
- -

Names and Order of Includes

- -
-

Use standard order for readability and to avoid hidden -dependencies: Related header, C library, C++ library, other libraries' -.h, your project's .h.

-
- -
-

-All of a project's header files should be -listed as descendants of the project's source -directory without use of UNIX directory shortcuts -. (the current directory) or .. -(the parent directory). For example, - -google-awesome-project/src/base/logging.h -should be included as:

- -
#include "base/logging.h"
-
- -

In dir/foo.cc or -dir/foo_test.cc, whose main -purpose is to implement or test the stuff in -dir2/foo2.h, order your includes -as follows:

- -
    -
  1. dir2/foo2.h.
  2. - -
  3. C system files.
  4. - -
  5. C++ system files.
  6. - -
  7. Other libraries' .h - files.
  8. - -
  9. - Your project's .h - files.
  10. -
- -

C and C++ standard library includes are considered system files in their -respective group. Includes within one group should not have an empty line -separating them, while there should be an empty line between groups.

- -

With the preferred ordering, if -dir2/foo2.h omits any necessary -includes, the build of dir/foo.cc -or dir/foo_test.cc will break. -Thus, this rule ensures that build breaks show up first -for the people working on these files, not for innocent -people in other packages.

- -

dir/foo.cc and -dir2/foo2.h are usually in the same -directory (e.g. base/basictypes_test.cc and -base/basictypes.h), but may sometimes be in different -directories too.

- - - -

Within each section the includes should be ordered -alphabetically. Note that older code might not conform to -this rule and should be fixed when convenient.

- -

You should include all the headers that define the symbols you rely -upon (except in cases of forward -declaration). If you rely on symbols from bar.h, -don't count on the fact that you included foo.h which -(currently) includes bar.h: include bar.h -yourself, unless foo.h explicitly demonstrates its intent -to provide you the symbols of bar.h. However, any -includes present in the related header do not need to be included -again in the related cc (i.e., foo.cc can -rely on foo.h's includes).

- -

For example, the includes in - -google-awesome-project/src/foo/internal/fooserver.cc -might look like this:

- - -
#include "foo/server/fooserver.h"
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <hash_map>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/commandlineflags.h"
-#include "foo/server/bar.h"
-
- -

Sometimes, system-specific code needs -conditional includes. Such code can put conditional -includes after other includes. Of course, keep your -system-specific code small and localized. Example:

- -
#include "foo/public/fooserver.h"
-
-#include "base/port.h"  // For LANG_CXX11.
-
-#ifdef LANG_CXX11
-#include <initializer_list>
-#endif  // LANG_CXX11
-
- -
- -

Naming

- -

The most important consistency rules are those that govern -naming. The style of a name immediately informs us what sort of -thing the named entity is: a type, a variable, a function, a -constant, a macro, etc., without requiring us to search for the -declaration of that entity. The pattern-matching engine in our -brains relies a great deal on these naming rules. -

- -

Naming rules are pretty arbitrary, but - we feel that -consistency is more important than individual preferences in this -area, so regardless of whether you find them sensible or not, -the rules are the rules.

- -

General Naming Rules

- -
-

Function names, variable names, and filenames should be -descriptive; eschew abbreviation.

-
- -
-

Give as descriptive a name as possible, within reason. -Do not worry about saving horizontal space as it is far -more important to make your code immediately -understandable by a new reader. Do not use abbreviations -that are ambiguous or unfamiliar to readers outside your -project, and do not abbreviate by deleting letters within -a word.

- -
int price_count_reader;    // No abbreviation.
-int num_errors;            // "num" is a widespread convention.
-int num_dns_connections;   // Most people know what "DNS" stands for.
-
- -
int n;                     // Meaningless.
-int nerr;                  // Ambiguous abbreviation.
-int n_comp_conns;          // Ambiguous abbreviation.
-int wgc_connections;       // Only your group knows what this stands for.
-int pc_reader;             // Lots of things can be abbreviated "pc".
-int cstmr_id;              // Deletes internal letters.
-
- -
- -

File Names

- -
-

Filenames should be all lowercase and can include -underscores (_) or dashes (-). -Follow the convention that your - -project uses. If there is no consistent -local pattern to follow, prefer "_".

-
- -
- -

Examples of acceptable file names:

- -
    -
  • my_useful_class.cc
  • -
  • my-useful-class.cc
  • -
  • myusefulclass.cc
  • -
  • myusefulclass_test.cc // _unittest and _regtest are deprecated.
  • -
- -

C++ files should end in .cc and header files should end in -.h. Files that rely on being textually included at specific points -should end in .inc (see also the section on -self-contained headers).

- -

Do not use filenames that already exist in -/usr/include, such as db.h.

- -

In general, make your filenames very specific. For -example, use http_server_logs.h rather than -logs.h. A very common case is to have a pair -of files called, e.g., foo_bar.h and -foo_bar.cc, defining a class called -FooBar.

- -

Inline functions must be in a .h file. If -your inline functions are very short, they should go -directly into your .h file.

- -
- -

Type Names

- -
-

Type names start with a capital letter and have a capital -letter for each new word, with no underscores: -MyExcitingClass, MyExcitingEnum.

-
- -
- -

The names of all types — classes, structs, typedefs, -and enums — have the same naming convention. Type names -should start with a capital letter and have a capital letter -for each new word. No underscores. For example:

- -
// classes and structs
-class UrlTable { ...
-class UrlTableTester { ...
-struct UrlTableProperties { ...
-
-// typedefs
-typedef hash_map<UrlTableProperties *, string> PropertiesMap;
-
-// enums
-enum UrlTableErrors { ...
-
- -
- -

Variable Names

- -
-

The names of variables and data members are all lowercase, with -underscores between words. Data members of classes (but not structs) -additionally are prefixed with "m_". For instance: -a_local_variable, a_struct_data_member, -m_a_class_data_member.

-
- -
- -

Common Variable names

- -

For example:

- -
string table_name;  // OK - uses underscore.
-string tablename;   // OK - all lowercase.
-
- -
string tableName;   // Bad - mixed case.
-
- -

Class Data Members

- -

Data members of classes, both static and non-static, are -named like ordinary nonmember variables, but prefixed with a -"m_".

- -
class TableInfo {
-  ...
- private:
-  string m_table_name;  // OK - m_ at beginning.
-  string m_tablename;   // OK.
-  static Pool<TableInfo>* m_pool;  // OK.
-};
-
- -

Struct Data Members

- -

Data members of structs, both static and non-static, -are named like ordinary nonmember variables. They do not have -the preceding "m_" that data members in classes have.

- -
struct UrlTableProperties {
-  string name;
-  int num_entries;
-  static Pool<UrlTableProperties>* pool;
-};
-
- - -

See Structs vs. -Classes for a discussion of when to use a struct -versus a class.

- -

Global Variables

- -

There are no special requirements for global -variables, which should be rare in any case, but if you -use one, consider prefixing it with g_ or -some other marker to easily distinguish it from local -variables.

- -
- -

Constant Names

- -
-

Use a k followed by mixed case, e.g., -kDaysInAWeek, for constants defined globally or within a class.

-
- -
- -

As a convenience to the reader, compile-time constants of global or class scope -follow a different naming convention from other variables. -Use a k followed by words with uppercase first letters:

- -
const int kDaysInAWeek = 7;
-
- -

This convention may optionally be used for compile-time constants of local scope; -otherwise the usual variable naming rules apply. - -

- -

Function Names

- -
-

Regular functions have mixed case; accessors and mutators -match the name of the variable: -MyExcitingFunction(), -MyExcitingMethod(), -my_exciting_member_variable(), -set_my_exciting_member_variable().

-
- -
- -

Regular Functions

- -

Functions should start with a capital letter and have -a capital letter for each new word. No underscores.

- -

If your function crashes upon an error, you should -append OrDie to the function name. This only applies to -functions which could be used by production code and to -errors that are reasonably likely to occur during normal -operation.

- -
AddTableEntry()
-DeleteUrl()
-OpenFileOrDie()
-
- -

Accessors and Mutators

- -

Accessors and mutators (get and set functions) should -match the name of the variable they are getting and -setting. This shows an excerpt of a class whose instance -variable is num_entries_.

- -
class MyClass {
- public:
-  ...
-  int num_entries() const { return num_entries_; }
-  void set_num_entries(int num_entries) { num_entries_ = num_entries; }
-
- private:
-  int num_entries_;
-};
-
- -

You may also use lowercase letters for other very -short inlined functions. For example if a function were -so cheap you would not cache the value if you were -calling it in a loop, then lowercase naming would be -acceptable.

- -
- -

Namespace Names

- -
- - -

Namespace names are all lower-case, -and based on project names and possibly their directory -structure: google_awesome_project.

-
- -
- -

See Namespaces for a -discussion of namespaces and how to name them.

- -
- -

Enumerator Names

- -
-

Enumerators should be named like -constants: kEnumName.

-
- -
- -

The enumeration name, -UrlTableErrors (and -AlternateUrlTableErrors), is a type, and -therefore mixed case.

- -
enum UrlTableErrors {
-  kOK = 0,
-  kErrorOutOfMemory,
-  kErrorMalformedInput,
-};
-
- -
- -

Macro Names

- -
-

You're not really going to -define a macro, are you? If you do, they're like this: -MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

-
- -
- -

Please see the description -of macros; in general macros should not be used. -However, if they are absolutely needed, then they should be -named with all capitals and underscores.

- -
#define ROUND(x) ...
-#define PI_ROUNDED 3.0
-
- -
- -

Exceptions to Naming Rules

- -
-

If you are naming something that is analogous to an -existing C or C++ entity then you can follow the existing -naming convention scheme.

-
- -
- -
-
bigopen()
-
function name, follows form of open()
- -
uint
-
typedef
- -
bigpos
-
struct or class, follows - form of pos
- -
sparse_hash_map
-
STL-like entity; follows STL naming conventions
- -
LONGLONG_MAX
-
a constant, as in INT_MAX
-
- -
- -

Comments

- -

Though a pain to write, comments are absolutely vital to -keeping our code readable. The following rules describe what -you should comment and where. But remember: while comments are -very important, the best code is self-documenting. Giving -sensible names to types and variables is much better than using -obscure names that you must then explain through comments.

- -

When writing your comments, write for your audience: the -next -contributor who will need to -understand your code. Be generous — the next -one may be you!

- -

Comment Style

- -
-

Use either the // or /* */ -syntax, as long as you are consistent.

-
- -
- -

You can use either the // or the /* -*/ syntax; however, // is -much more common. Be consistent with how you -comment and what style you use where.

- -
- -

File Comments

- -
-

Start each file with license -boilerplate, followed by a description of its -contents.

-
- -
- -

Legal Notice and Author -Line

- - - -

Every file should contain license -boilerplate. Choose the appropriate boilerplate for the -license used by the project (for example, Apache 2.0, -BSD, LGPL, GPL).

- -

If you make significant changes to a file with an -author line, consider deleting the author line.

- -

File Contents

- -

Every file should have a comment at the top describing -its contents.

- -

Generally a .h file will describe the -classes that are declared in the file with an overview of -what they are for and how they are used. A -.cc file should contain more information -about implementation details or discussions of tricky -algorithms. If you feel the implementation details or a -discussion of the algorithms would be useful for someone -reading the .h, feel free to put it there -instead, but mention in the .cc that the -documentation is in the .h file.

- -

Do not duplicate comments in both the .h -and the .cc. Duplicated comments -diverge.

- -
- -

Class Comments

- -
-

Every class definition should have an accompanying comment -that describes what it is for and how it should be used.

-
- -
- -
// Iterates over the contents of a GargantuanTable.  Sample usage:
-//    GargantuanTableIterator* iter = table->NewIterator();
-//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
-//      process(iter->key(), iter->value());
-//    }
-//    delete iter;
-class GargantuanTableIterator {
-  ...
-};
-
- -

If you have already described a class in detail in the -comments at the top of your file feel free to simply -state "See comment at top of file for a complete -description", but be sure to have some sort of -comment.

- -

Document the synchronization assumptions the class -makes, if any. If an instance of the class can be -accessed by multiple threads, take extra care to document -the rules and invariants surrounding multithreaded -use.

- -
- -

Function Comments

- -
-

Declaration comments describe use of the function; comments -at the definition of a function describe operation.

-
- -
- -

Function Declarations

- -

Every function declaration should have comments -immediately preceding it that describe what the function -does and how to use it. These comments should be -descriptive ("Opens the file") rather than imperative -("Open the file"); the comment describes the function, it -does not tell the function what to do. In general, these -comments do not describe how the function performs its -task. Instead, that should be left to comments in the -function definition.

- -

Types of things to mention in comments at the function -declaration:

- -
    -
  • What the inputs and outputs are.
  • - -
  • For class member functions: whether the object - remembers reference arguments beyond the duration of - the method call, and whether it will free them or - not.
  • - -
  • If the function allocates memory that the caller - must free.
  • - -
  • Whether any of the arguments can be a null - pointer.
  • - -
  • If there are any performance implications of how a - function is used.
  • - -
  • If the function is re-entrant. What are its - synchronization assumptions?
  • -
- -

Here is an example:

- -
// Returns an iterator for this table.  It is the client's
-// responsibility to delete the iterator when it is done with it,
-// and it must not use the iterator once the GargantuanTable object
-// on which the iterator was created has been deleted.
-//
-// The iterator is initially positioned at the beginning of the table.
-//
-// This method is equivalent to:
-//    Iterator* iter = table->NewIterator();
-//    iter->Seek("");
-//    return iter;
-// If you are going to immediately seek to another place in the
-// returned iterator, it will be faster to use NewIterator()
-// and avoid the extra seek.
-Iterator* GetIterator() const;
-
- -

However, do not be unnecessarily verbose or state the -completely obvious. Notice below that it is not necessary - to say "returns false otherwise" because this is -implied.

- -
// Returns true if the table cannot hold any more entries.
-bool IsTableFull();
-
- -

When commenting constructors and destructors, remember -that the person reading your code knows what constructors -and destructors are for, so comments that just say -something like "destroys this object" are not useful. -Document what constructors do with their arguments (for -example, if they take ownership of pointers), and what -cleanup the destructor does. If this is trivial, just -skip the comment. It is quite common for destructors not -to have a header comment.

- -

Function Definitions

- -

If there is anything tricky about how a function does -its job, the function definition should have an -explanatory comment. For example, in the definition -comment you might describe any coding tricks you use, -give an overview of the steps you go through, or explain -why you chose to implement the function in the way you -did rather than using a viable alternative. For instance, -you might mention why it must acquire a lock for the -first half of the function but why it is not needed for -the second half.

- -

Note you should not just repeat the comments -given with the function declaration, in the -.h file or wherever. It's okay to -recapitulate briefly what the function does, but the -focus of the comments should be on how it does it.

- -
- -

Variable Comments

- -
-

In general the actual name of the variable should be -descriptive enough to give a good idea of what the variable -is used for. In certain cases, more comments are required.

-
- -
- -

Class Data Members

- -

Each class data member (also called an instance -variable or member variable) should have a comment -describing what it is used for. If the variable can take -sentinel values with special meanings, such as a null -pointer or -1, document this. For example:

- - -
private:
- // Keeps track of the total number of entries in the table.
- // Used to ensure we do not go over the limit. -1 means
- // that we don't yet know how many entries the table has.
- int num_total_entries_;
-
- -

Global Variables

- -

As with data members, all global variables should have -a comment describing what they are and what they are used -for. For example:

- -
// The total number of tests cases that we run through in this regression test.
-const int kNumTestCases = 6;
-
- -
- -

Implementation Comments

- -
-

In your implementation you should have comments in tricky, -non-obvious, interesting, or important parts of your code.

-
- -
- -

Explanatory Comments

- -

Tricky or complicated code blocks should have comments -before them. Example:

- -
// Divide result by two, taking into account that x
-// contains the carry from the add.
-for (int i = 0; i < result->size(); i++) {
-  x = (x << 8) + (*result)[i];
-  (*result)[i] = x >> 1;
-  x &= 1;
-}
-
- -

Line Comments

- -

Also, lines that are non-obvious should get a comment -at the end of the line. These end-of-line comments should -be separated from the code by 2 spaces. Example:

- -
// If we have enough memory, mmap the data portion too.
-mmap_budget = max<int64>(0, mmap_budget - index_->length());
-if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
-  return;  // Error already logged.
-
- -

Note that there are both comments that describe what -the code is doing, and comments that mention that an -error has already been logged when the function -returns.

- -

If you have several comments on subsequent lines, it -can often be more readable to line them up:

- -
DoSomething();                  // Comment here so the comments line up.
-DoSomethingElseThatIsLonger();  // Two spaces between the code and the comment.
-{ // One space before comment when opening a new scope is allowed,
-  // thus the comment lines up with the following comments and code.
-  DoSomethingElse();  // Two spaces before line comments normally.
-}
-vector<string> list{// Comments in braced lists describe the next element ..
-                    "First item",
-                    // .. and should be aligned appropriately.
-                    "Second item"};
-DoSomething(); /* For trailing block comments, one space is fine. */
-
- -

nullptr/NULL, true/false, 1, 2, 3...

- -

When you pass in a null pointer, boolean, or literal -integer values to functions, you should consider adding a -comment about what they are, or make your code -self-documenting by using constants. For example, -compare:

- -
bool success = CalculateSomething(interesting_value,
-                                  10,
-                                  false,
-                                  NULL);  // What are these arguments??
-
- -

versus:

- -
bool success = CalculateSomething(interesting_value,
-                                  10,     // Default base value.
-                                  false,  // Not the first time we're calling this.
-                                  NULL);  // No callback.
-
- -

Or alternatively, constants or self-describing variables:

- -
const int kDefaultBaseValue = 10;
-const bool kFirstTimeCalling = false;
-Callback *null_callback = NULL;
-bool success = CalculateSomething(interesting_value,
-                                  kDefaultBaseValue,
-                                  kFirstTimeCalling,
-                                  null_callback);
-
- -

Don'ts

- -

Note that you should never describe the code -itself. Assume that the person reading the code knows C++ -better than you do, even though he or she does not know -what you are trying to do:

- -
// Now go through the b array and make sure that if i occurs,
-// the next element is i+1.
-...        // Geez.  What a useless comment.
-
- -
- -

Punctuation, Spelling and Grammar

- -
-

Pay attention to punctuation, spelling, and grammar; it is -easier to read well-written comments than badly written -ones.

-
- -
- -

Comments should be as readable as narrative text, with -proper capitalization and punctuation. In many cases, -complete sentences are more readable than sentence -fragments. Shorter comments, such as comments at the end -of a line of code, can sometimes be less formal, but you -should be consistent with your style.

- -

Although it can be frustrating to have a code reviewer -point out that you are using a comma when you should be -using a semicolon, it is very important that source code -maintain a high level of clarity and readability. Proper -punctuation, spelling, and grammar help with that -goal.

- -
- -

TODO Comments

- -
-

Use TODO comments for code that is temporary, -a short-term solution, or good-enough but not perfect.

-
- -
- -

TODOs should include the string -TODO in all caps, followed by the - -name, e-mail address, or other -identifier of the person - with the best context -about the problem referenced by the TODO. The -main purpose is to have a consistent TODO that -can be searched to find out how to get more details upon -request. A TODO is not a commitment that the -person referenced will fix the problem. Thus when you create -a TODO, it is almost always your - -name -that is given.

- - - -
-
// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
-// TODO(Zeke) change this to use relations.
-
-
- -

If your TODO is of the form "At a future -date do something" make sure that you either include a -very specific date ("Fix by November 2005") or a very -specific event ("Remove this code when all clients can -handle XML responses.").

- -
- -

Deprecation Comments

- -
-

Mark deprecated interface points with DEPRECATED -comments.

-
- -
- -

You can mark an interface as deprecated by writing a -comment containing the word DEPRECATED in -all caps. The comment goes either before the declaration -of the interface or on the same line as the -declaration.

- - - -

After the word -DEPRECATED, write your name, e-mail address, -or other identifier in parentheses.

- -

A deprecation comment must include simple, clear -directions for people to fix their callsites. In C++, you -can implement a deprecated function as an inline function -that calls the new interface point.

- -

Marking an interface point DEPRECATED -will not magically cause any callsites to change. If you -want people to actually stop using the deprecated -facility, you will have to fix the callsites yourself or -recruit a crew to help you.

- -

New code should not contain calls to deprecated -interface points. Use the new interface point instead. If -you cannot understand the directions, find the person who -created the deprecation and ask them for help using the -new interface point.

- - - -
- -

Formatting

- -

Coding style and formatting are pretty arbitrary, but a - -project is much easier to follow -if everyone uses the same style. Individuals may not agree with every -aspect of the formatting rules, and some of the rules may take -some getting used to, but it is important that all - -project contributors follow the -style rules so that -they can all read and understand -everyone's code easily.

- - - -

To help your code conform to our format, we've -created a python script that runs clang-format on it -and performs various other checks.

- -

Line Length

- -
-

Each line of text in your code should be at most 80 -characters long.

-
- -
- - - -

We recognize that this rule is -controversial, but so much existing code already adheres -to it, and we feel that consistency is important.

- -
-

Those who favor this rule -argue that it is rude to force them to resize -their windows and there is no need for anything longer. -Some folks are used to having several code windows -side-by-side, and thus don't have room to widen their -windows in any case. People set up their work environment -assuming a particular maximum window width, and 80 -columns has been the traditional standard. Why change -it?

-
- -
-

Proponents of change argue that a wider line can make -code more readable. The 80-column limit is an hidebound -throwback to 1960s mainframes; modern equipment has wide screens that -can easily show longer lines.

-
- -
-

80 characters is the maximum.

- -

If a comment line contains an example -command or a literal URL longer than 80 characters, that -line may be longer than 80 characters for ease of cut and -paste.

- -

A raw-string literal may have content -that exceeds 80 characters. Except for test code, such literals -should appear near top of a file.

- -

An #include statement with a -long path may exceed 80 columns.

-
- -
- -

Non-ASCII Characters

- -
-

Non-ASCII characters should be rare, and must use UTF-8 -formatting.

-
- -
- -

You shouldn't hard-code user-facing text in source, -even English, so use of non-ASCII characters should be -rare. However, in certain cases it is appropriate to -include such words in your code. For example, if your -code parses data files from foreign sources, it may be -appropriate to hard-code the non-ASCII string(s) used in -those data files as delimiters. More commonly, unittest -code (which does not need to be localized) might -contain non-ASCII strings. In such cases, you should use -UTF-8, since that is an encoding -understood by most tools able to handle more than just -ASCII.

- -

Hex encoding is also OK, and encouraged where it -enhances readability — for example, -"\xEF\xBB\xBF", or, even more simply, -u8"\uFEFF", is the Unicode zero-width -no-break space character, which would be invisible if -included in the source as straight UTF-8.

- -

Use the u8 prefix -to guarantee that a string literal containing -\uXXXX escape sequences is encoded as UTF-8. -Do not use it for strings containing non-ASCII characters -encoded as UTF-8, because that will produce incorrect -output if the compiler does not interpret the source file -as UTF-8.

- -

You shouldn't use the C++11 char16_t and -char32_t character types, since they're for -non-UTF-8 text. For similar reasons you also shouldn't -use wchar_t (unless you're writing code that -interacts with the Windows API, which uses -wchar_t extensively).

- -
- -

Spaces vs. Tabs

- -
-

Use only spaces, and indent 2 spaces at a time.

-
- -
- -

We use spaces for indentation. Do not use tabs in your -code. You should set your editor to emit spaces when you -hit the tab key.

- -
- -

Function Declarations and Definitions

- -
-

Return type on the same line as function name, parameters -on the same line if they fit. Wrap parameter lists which do -not fit on a single line as you would wrap arguments in a -function call.

-
- -
- -

Functions look like this:

- - -
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
-  DoSomething();
-  ...
-}
-
- -

If you have too much text to fit on one line:

- -
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
-                                             Type par_name3) {
-  DoSomething();
-  ...
-}
-
- -

or if you cannot fit even the first parameter:

- -
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
-    Type par_name1,  // 4 space indent
-    Type par_name2,
-    Type par_name3) {
-  DoSomething();  // 2 space indent
-  ...
-}
-
- -

Some points to note:

- -
    -
  • If you cannot fit the return type and the function - name on a single line, break between them.
  • - -
  • If you break after the return type of a function - declaration or definition, do not indent.
  • - -
  • The open parenthesis is always on the same line as - the function name.
  • - -
  • There is never a space between the function name - and the open parenthesis.
  • - -
  • There is never a space between the parentheses and - the parameters.
  • - -
  • The open curly brace is always at the end of the - same line as the last parameter.
  • - -
  • The close curly brace is either on the last line by - itself or (if other style rules permit) on the same - line as the open curly brace.
  • - -
  • There should be a space between the close - parenthesis and the open curly brace.
  • - -
  • All parameters should be named, with identical - names in the declaration and implementation.
  • - -
  • All parameters should be aligned if possible.
  • - -
  • Default indentation is 2 spaces.
  • - -
  • Wrapped parameters have a 4 space indent.
  • -
- -

If some parameters are unused, comment out the -variable name in the function definition:

- -
// Always have named parameters in interfaces.
-class Shape {
- public:
-  virtual void Rotate(double radians) = 0;
-};
-
-// Always have named parameters in the declaration.
-class Circle : public Shape {
- public:
-  virtual void Rotate(double radians);
-};
-
-// Comment out unused named parameters in definitions.
-void Circle::Rotate(double /*radians*/) {}
-
- -
// Bad - if someone wants to implement later, it's not clear what the
-// variable means.
-void Circle::Rotate(double) {}
-
- -
- -

Lambda Expressions

- -
-

Format parameters and bodies as for any other function, and capture -lists like other comma-separated lists.

-
- -
-

For by-reference captures, do not leave a space between the -ampersand (&) and the variable name.

-
int x = 0;
-auto add_to_x = [&x](int n) { x += n; };
-
-

Short lambdas may be written inline as function arguments.

-
std::set<int> blacklist = {7, 8, 9};
-std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
-digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
-               return blacklist.find(i) != blacklist.end();
-             }),
-             digits.end());
-
- -
- -

Function Calls

- -
-

Either write the call all on a single line, wrap the -arguments at the parenthesis, or start the arguments on a new -line indented by four spaces and continue at that 4 space -indent. In the absence of other considerations, use the -minimum number of lines, including placing multiple arguments -on each line where appropriate.

-
- -
- -

Function calls have the following format:

-
bool retval = DoSomething(argument1, argument2, argument3);
-
- -

If the arguments do not all fit on one line, they -should be broken up onto multiple lines, with each -subsequent line aligned with the first argument. Do not -add spaces after the open paren or before the close -paren:

-
bool retval = DoSomething(averyveryveryverylongargument1,
-                          argument2, argument3);
-
- -

Arguments may optionally all be placed on subsequent -lines with a four space indent:

-
if (...) {
-  ...
-  ...
-  if (...) {
-    DoSomething(
-        argument1, argument2,  // 4 space indent
-        argument3, argument4);
-  }
-
- -

Put multiple arguments on a single line to reduce the -number of lines necessary for calling a function unless -there is a specific readability problem. Some find that -formatting with strictly one argument on each line is -more readable and simplifies editing of the arguments. -However, we prioritize for the reader over the ease of -editing arguments, and most readability problems are -better addressed with the following techniques.

- -

If having multiple arguments in a single line decreases -readability due to the complexity or confusing nature of the -expressions that make up some arguments, try creating -variables that capture those arguments in a descriptive name:

-
int my_heuristic = scores[x] * y + bases[x];
-bool retval = DoSomething(my_heuristic, x, y, z);
-
- -

Or put the confusing argument on its own line with -an explanatory comment:

-
bool retval = DoSomething(scores[x] * y + bases[x],  // Score heuristic.
-                          x, y, z);
-
- -

If there is still a case where one argument is -significantly more readable on its own line, then put it on -its own line. The decision should be specific to the argument -which is made more readable rather than a general policy.

- -

Sometimes arguments form a structure that is important -for readability. In those cases, feel free to format the -arguments according to that structure:

-
// Transform the widget by a 3x3 matrix.
-my_widget.Transform(x1, x2, x3,
-                    y1, y2, y3,
-                    z1, z2, z3);
-
- -
- -

Braced Initializer List Format

- -
-

Format a braced initializer list -exactly like you would format a function call in its place.

-
- -
- -

If the braced list follows a name (e.g. a type or -variable name), format as if the {} were the -parentheses of a function call with that name. If there -is no name, assume a zero-length name.

- -
// Examples of braced init list on a single line.
-return {foo, bar};
-functioncall({foo, bar});
-pair<int, int> p{foo, bar};
-
-// When you have to wrap.
-SomeFunction(
-    {"assume a zero-length name before {"},
-    some_other_function_parameter);
-SomeType variable{
-    some, other, values,
-    {"assume a zero-length name before {"},
-    SomeOtherType{
-        "Very long string requiring the surrounding breaks.",
-        some, other values},
-    SomeOtherType{"Slightly shorter string",
-                  some, other, values}};
-SomeType variable{
-    "This is too long to fit all in one line"};
-MyType m = {  // Here, you could also break before {.
-    superlongvariablename1,
-    superlongvariablename2,
-    {short, interior, list},
-    {interiorwrappinglist,
-     interiorwrappinglist2}};
-
- -
- -

Conditionals

- -
-

Prefer no spaces inside parentheses. The if -and else keywords belong on separate lines.

-
- -
- -

There are two acceptable formats for a basic -conditional statement. One includes spaces between the -parentheses and the condition, and one does not.

- -

The most common form is without spaces. Either is -fine, but be consistent. If you are modifying a -file, use the format that is already present. If you are -writing new code, use the format that the other files in -that directory or project use. If in doubt and you have -no personal preference, do not add the spaces.

- -
if (condition) {  // no spaces inside parentheses
-  ...  // 2 space indent.
-} else if (...) {  // The else goes on the same line as the closing brace.
-  ...
-} else {
-  ...
-}
-
- -

If you prefer you may add spaces inside the -parentheses:

- -
if ( condition ) {  // spaces inside parentheses - rare
-  ...  // 2 space indent.
-} else {  // The else goes on the same line as the closing brace.
-  ...
-}
-
- -

Note that in all cases you must have a space between -the if and the open parenthesis. You must -also have a space between the close parenthesis and the -curly brace, if you're using one.

- -
if(condition) {   // Bad - space missing after IF.
-if (condition){   // Bad - space missing before {.
-if(condition){    // Doubly bad.
-
- -
if (condition) {  // Good - proper space after IF and before {.
-
- -

Short conditional statements may be written on one -line if this enhances readability. You may use this only -when the line is brief and the statement does not use the -else clause.

- -
if (x == kFoo) return new Foo();
-if (x == kBar) return new Bar();
-
- -

This is not allowed when the if statement has an -else:

- -
// Not allowed - IF statement on one line when there is an ELSE clause
-if (x) DoThis();
-else DoThat();
-
- -

In general, curly braces are not required for -single-line statements, but they are allowed if you like -them; conditional or loop statements with complex -conditions or statements may be more readable with curly -braces. Some -projects require that an -if must always always have an accompanying -brace.

- -
if (condition)
-  DoSomething();  // 2 space indent.
-
-if (condition) {
-  DoSomething();  // 2 space indent.
-}
-
- -

However, if one part of an -if-else statement uses curly -braces, the other part must too:

- -
// Not allowed - curly on IF but not ELSE
-if (condition) {
-  foo;
-} else
-  bar;
-
-// Not allowed - curly on ELSE but not IF
-if (condition)
-  foo;
-else {
-  bar;
-}
-
- -
// Curly braces around both IF and ELSE required because
-// one of the clauses used braces.
-if (condition) {
-  foo;
-} else {
-  bar;
-}
-
- -
- -

Loops and Switch Statements

- -
-

Switch statements may use braces for blocks. Annotate -non-trivial fall-through between cases. -Braces are optional for single-statement loops. -Empty loop bodies should use {} or continue.

-
- -
- -

case blocks in switch -statements can have curly braces or not, depending on -your preference. If you do include curly braces they -should be placed as shown below.

- -

If not conditional on an enumerated value, switch -statements should always have a default case -(in the case of an enumerated value, the compiler will -warn you if any values are not handled). If the default -case should never execute, simply -assert:

- - - -
-
switch (var) {
-  case 0: {  // 2 space indent
-    ...      // 4 space indent
-    break;
-  }
-  case 1: {
-    ...
-    break;
-  }
-  default: {
-    assert(false);
-  }
-}
-
-
- - - - - -

Braces are optional for single-statement loops.

- -
for (int i = 0; i < kSomeNumber; ++i)
-  printf("I love you\n");
-
-for (int i = 0; i < kSomeNumber; ++i) {
-  printf("I take it back\n");
-}
-
- - -

Empty loop bodies should use {} or -continue, but not a single semicolon.

- -
while (condition) {
-  // Repeat test until it returns false.
-}
-for (int i = 0; i < kSomeNumber; ++i) {}  // Good - empty body.
-while (condition) continue;  // Good - continue indicates no logic.
-
- -
while (condition);  // Bad - looks like part of do/while loop.
-
- -
- -

Pointer and Reference Expressions

- -
-

No spaces around period or arrow. Pointer operators do not -have trailing spaces.

-
- -
- -

The following are examples of correctly-formatted -pointer and reference expressions:

- -
x = *p;
-p = &x;
-x = r.y;
-x = r->y;
-
- -

Note that:

- -
    -
  • There are no spaces around the period or arrow when - accessing a member.
  • - -
  • Pointer operators have no space after the - * or &.
  • -
- -

When declaring a pointer variable or argument, you may -place the asterisk adjacent to either the type or to the -variable name:

- -
// These are fine, space preceding.
-char *c;
-const string &str;
-
-// These are fine, space following.
-char* c;    // but remember to do "char* c, *d, *e, ...;"!
-const string& str;
-
- -
char * c;  // Bad - spaces on both sides of *
-const string & str;  // Bad - spaces on both sides of &
-
- -

You should do this consistently within a single -file, -so, when modifying an existing file, use the style in -that file.

- -
- -

Boolean Expressions

- -
-

When you have a boolean expression that is longer than the -standard line length, be -consistent in how you break up the lines.

-
- -
- -

In this example, the logical AND operator is always at -the end of the lines:

- -
if (this_one_thing > this_other_thing &&
-    a_third_thing == a_fourth_thing &&
-    yet_another && last_one) {
-  ...
-}
-
- -

Note that when the code wraps in this example, both of -the && logical AND operators are at -the end of the line. This is more common in Google code, -though wrapping all operators at the beginning of the -line is also allowed. Feel free to insert extra -parentheses judiciously because they can be very helpful -in increasing readability when used -appropriately. Also note that you should always use -the punctuation operators, such as -&& and ~, rather than -the word operators, such as and and -compl.

- -
- -

Return Values

- -
-

Do not needlessly surround the return -expression with parentheses.

-
- -
- -

Use parentheses in return expr; only -where you would use them in x = expr;.

- -
return result;                  // No parentheses in the simple case.
-// Parentheses OK to make a complex expression more readable.
-return (some_long_condition &&
-        another_condition);
-
- -
return (value);                // You wouldn't write var = (value);
-return(result);                // return is not a function!
-
- -
- - - -

Variable and Array Initialization

- -
-

Your choice of =, (), or -{}.

-
- -
- -

You may choose between =, -(), and {}; the following are -all correct:

- -
int x = 3;
-int x(3);
-int x{3};
-string name = "Some Name";
-string name("Some Name");
-string name{"Some Name"};
-
- -

Be careful when using a braced initialization list {...} -on a type with an std::initializer_list constructor. -A nonempty braced-init-list prefers the -std::initializer_list constructor whenever -possible. Note that empty braces {} are special, and -will call a default constructor if available. To force the -non-std::initializer_list constructor, use parentheses -instead of braces.

- -
vector<int> v(100, 1);  // A vector of 100 1s.
-vector<int> v{100, 1};  // A vector of 100, 1.
-
- -

Also, the brace form prevents narrowing of integral -types. This can prevent some types of programming -errors.

- -
int pi(3.14);  // OK -- pi == 3.
-int pi{3.14};  // Compile error: narrowing conversion.
-
- -
- -

Preprocessor Directives

- -
-

The hash mark that starts a preprocessor directive should -always be at the beginning of the line.

-
- -
- -

Even when preprocessor directives are within the body -of indented code, the directives should start at the -beginning of the line.

- -
// Good - directives at beginning of line
-  if (lopsided_score) {
-#if DISASTER_PENDING      // Correct -- Starts at beginning of line
-    DropEverything();
-# if NOTIFY               // OK but not required -- Spaces after #
-    NotifyClient();
-# endif
-#endif
-    BackToNormal();
-  }
-
- -
// Bad - indented directives
-  if (lopsided_score) {
-    #if DISASTER_PENDING  // Wrong!  The "#if" should be at beginning of line
-    DropEverything();
-    #endif                // Wrong!  Do not indent "#endif"
-    BackToNormal();
-  }
-
- -
- -

Class Format

- -
-

Sections in public, protected and -private order, each indented one space.

-
- -
- -

The basic format for a class declaration (lacking the -comments, see Class -Comments for a discussion of what comments are -needed) is:

- -
class MyClass : public OtherClass {
- public:      // Note the 1 space indent!
-  MyClass();  // Regular 2 space indent.
-  explicit MyClass(int var);
-  ~MyClass() {}
-
-  void SomeFunction();
-  void SomeFunctionThatDoesNothing() {
-  }
-
-  void set_some_var(int var) { some_var_ = var; }
-  int some_var() const { return some_var_; }
-
- private:
-  bool SomeInternalFunction();
-
-  int some_var_;
-  int some_other_var_;
-};
-
- -

Things to note:

- -
    -
  • Any base class name should be on the same line as - the subclass name, subject to the 80-column limit.
  • - -
  • The public:, protected:, - and private: keywords should be indented - one space.
  • - -
  • Except for the first instance, these keywords - should be preceded by a blank line. This rule is - optional in small classes.
  • - -
  • Do not leave a blank line after these - keywords.
  • - -
  • The public section should be first, - followed by the protected and finally the - private section.
  • - -
  • See Declaration - Order for rules on ordering declarations within - each of these sections.
  • -
- -
- -

Constructor Initializer Lists

- -
-

Constructor initializer lists can be all on one line or -with subsequent lines indented four spaces.

-
- -
- -

There are two acceptable formats for initializer -lists:

- -
// When it all fits on one line:
-MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {}
-
- -

or

- -
// When it requires multiple lines, indent 4 spaces, putting the colon on
-// the first initializer line:
-MyClass::MyClass(int var)
-    : some_var_(var),             // 4 space indent
-      some_other_var_(var + 1) {  // lined up
-  ...
-  DoSomething();
-  ...
-}
-
- -
- -

Namespace Formatting

- -
-

The contents of namespaces are not indented.

-
- -
- -

Namespaces do not add an -extra level of indentation. For example, use:

- -
namespace {
-
-void foo() {  // Correct.  No extra indentation within namespace.
-  ...
-}
-
-}  // namespace
-
- -

Do not indent within a namespace:

- -
namespace {
-
-  // Wrong.  Indented when it should not be.
-  void foo() {
-    ...
-  }
-
-}  // namespace
-
- -

When declaring nested namespaces, put each namespace -on its own line.

- -
namespace foo {
-namespace bar {
-
- -
- -

Horizontal Whitespace

- -
-

Use of horizontal whitespace depends on location. Never put -trailing whitespace at the end of a line.

-
- -
- -

General

- -
void f(bool b) {  // Open braces should always have a space before them.
-  ...
-int i = 0;  // Semicolons usually have no space before them.
-// Spaces inside braces for braced-init-list are optional.  If you use them,
-// put them on both sides!
-int x[] = { 0 };
-int x[] = {0};
-
-// Spaces around the colon in inheritance and initializer lists.
-class Foo : public Bar {
- public:
-  // For inline function implementations, put spaces between the braces
-  // and the implementation itself.
-  Foo(int b) : Bar(), baz_(b) {}  // No spaces inside empty braces.
-  void Reset() { baz_ = 0; }  // Spaces separating braces from implementation.
-  ...
-
- -

Adding trailing whitespace can cause extra work for -others editing the same file, when they merge, as can -removing existing trailing whitespace. So: Don't -introduce trailing whitespace. Remove it if you're -already changing that line, or do it in a separate -clean-up -operation (preferably when no-one -else is working on the file).

- -

Loops and Conditionals

- -
if (b) {          // Space after the keyword in conditions and loops.
-} else {          // Spaces around else.
-}
-while (test) {}   // There is usually no space inside parentheses.
-switch (i) {
-for (int i = 0; i < 5; ++i) {
-// Loops and conditions may have spaces inside parentheses, but this
-// is rare.  Be consistent.
-switch ( i ) {
-if ( test ) {
-for ( int i = 0; i < 5; ++i ) {
-// For loops always have a space after the semicolon.  They may have a space
-// before the semicolon, but this is rare.
-for ( ; i < 5 ; ++i) {
-  ...
-
-// Range-based for loops always have a space before and after the colon.
-for (auto x : counts) {
-  ...
-}
-switch (i) {
-  case 1:         // No space before colon in a switch case.
-    ...
-  case 2: break;  // Use a space after a colon if there's code after it.
-
- -

Operators

- -
// Assignment operators always have spaces around them.
-x = 0;
-
-// Other binary operators usually have spaces around them, but it's
-// OK to remove spaces around factors.  Parentheses should have no
-// internal padding.
-v = w * x + y / z;
-v = w*x + y/z;
-v = w * (x + z);
-
-// No spaces separating unary operators and their arguments.
-x = -5;
-++x;
-if (x && !y)
-  ...
-
- -

Templates and Casts

- -
// No spaces inside the angle brackets (< and >), before
-// <, or between >( in a cast
-vector<string> x;
-y = static_cast<char*>(x);
-
-// Spaces between type and pointer are OK, but be consistent.
-vector<char *> x;
-set<list<string>> x;        // Permitted in C++11 code.
-set<list<string> > x;       // C++03 required a space in > >.
-
-// You may optionally use symmetric spacing in < <.
-set< list<string> > x;
-
- -
- -

Vertical Whitespace

- -
-

Minimize use of vertical whitespace.

-
- -
- -

This is more a principle than a rule: don't use blank -lines when you don't have to. In particular, don't put -more than one or two blank lines between functions, -resist starting functions with a blank line, don't end -functions with a blank line, and be discriminating with -your use of blank lines inside functions.

- -

The basic principle is: The more code that fits on one -screen, the easier it is to follow and understand the -control flow of the program. Of course, readability can -suffer from code being too dense as well as too spread -out, so use your judgement. But in general, minimize use -of vertical whitespace.

- -

Some rules of thumb to help when blank lines may be -useful:

- -
    -
  • Blank lines at the beginning or end of a function - very rarely help readability.
  • - -
  • Blank lines inside a chain of if-else blocks may - well help readability.
  • -
- -
- -

Exceptions to the Rules

- -

The coding conventions described above are mandatory. -However, like all good rules, these sometimes have exceptions, -which we discuss here.

- - - -
-

Existing Non-conformant Code

- -
-

You may diverge from the rules when dealing with code that -does not conform to this style guide.

-
- -
- -

If you find yourself modifying code that was written -to specifications other than those presented by this -guide, you may have to diverge from these rules in order -to stay consistent with the local conventions in that -code. If you are in doubt about how to do this, ask the -original author or the person currently responsible for -the code. Remember that consistency includes -local consistency, too.

- -
-
- - - - -

Use common sense and BE CONSISTENT.

- -

If you are editing code, take a few minutes to look at the -code around you and determine its style. If they use spaces -around their if clauses, you should, too. If their -comments have little boxes of stars around them, make your -comments have little boxes of stars around them too.

- -

The point of having style guidelines is to have a common -vocabulary of coding so people can concentrate on what you are -saying, rather than on how you are saying it. We present global -style rules here so people know the vocabulary. But local style -is also important. If code you add to a file looks drastically -different from the existing code around it, the discontinuity -throws readers out of their rhythm when they go to read it. Try -to avoid this.

- - - -

OK, enough writing about writing code; the code itself is much -more interesting. Have fun!

- -
- -

Revision 4.45

- -
- diff --git a/styleguide/cpplint.py b/styleguide/cpplint.py deleted file mode 100644 index a434c48d83..0000000000 --- a/styleguide/cpplint.py +++ /dev/null @@ -1,6115 +0,0 @@ -#!/usr/bin/env python -# -# 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. - -"""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 copy -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] [--root=subdir] - [--linelength=digits] - [file] ... - - The style guidelines this tries to follow are those in - https://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. - Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the - extensions with the --extensions flag. - - 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'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuming that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ - - linelength=digits - This is the allowed line length for the project. The default value is - 80 characters. - - Examples: - --linelength=120 - - extensions=extension,extension,... - The allowed file extensions that cpplint will check - - Examples: - --extensions=hpp,cpp - - cpplint.py supports per-directory configurations specified in CPPLINT.cfg - files. CPPLINT.cfg file can contain a number of key=value pairs. - Currently the following options are supported: - - set noparent - filter=+filter1,-filter2,... - exclude_files=regex - linelength=80 - root=subdir - - "set noparent" option prevents cpplint from traversing directory tree - upwards looking for more .cfg files in parent directories. This option - is usually placed in the top-level project directory. - - The "filter" option is similar in function to --filter flag. It specifies - message filters in addition to the |_DEFAULT_FILTERS| and those specified - through --filter command-line flag. - - "exclude_files" allows to specify a regular expression to be matched against - a file name. If the expression matches, the file is skipped and not run - through liner. - - "linelength" allows to specify the allowed line length for the project. - - The "root" option is similar in function to the --root flag (see example - above). - - CPPLINT.cfg has an effect on files in the same directory and all - sub-directories, unless overridden by a nested configuration file. - - Example file: - filter=-build/include_order,+build/include_alpha - exclude_files=.*\.cc - - The above example disables build/include_order warning and enables - build/include_alpha as well as excludes all .cc from being - processed by linter, in the current directory (where the .cfg - file is located) and all sub-directories. -""" - -# 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. -_ERROR_CATEGORIES = [ - 'build/class', - 'build/c++11', - 'build/c++14', - 'build/c++tr1', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - '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/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/inheritance', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/nul', - 'readability/strings', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/indentation_namespace', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/string', - 'runtime/threadsafe_fn', - 'runtime/vlog', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_conditional_body', - 'whitespace/empty_if_body', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo', - ] - -# These error categories are no longer enforced by cpplint, but for backwards- -# compatibility they may still appear in NOLINT comments. -_LEGACY_ERROR_CATEGORIES = [ - 'readability/streams', - 'readability/function', - ] - -# The default state of the category filter. This is overridden 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'] - -# The default list of categories suppressed for C (not C++) files. -_DEFAULT_C_SUPPRESSED_CATEGORIES = [ - 'readability/casting', - ] - -# The default list of categories suppressed for Linux Kernel files. -_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ - 'whitespace/tab', - ] - -# 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 separate i18n file. - -# C++ headers -_CPP_HEADERS = frozenset([ - # Legacy - 'algobase.h', - 'algo.h', - 'alloc.h', - 'builtinbuf.h', - 'bvector.h', - 'complex.h', - 'defalloc.h', - 'deque.h', - 'editbuf.h', - 'fstream.h', - 'function.h', - 'hash_map', - 'hash_map.h', - 'hash_set', - 'hash_set.h', - 'hashtable.h', - 'heap.h', - 'indstream.h', - 'iomanip.h', - 'iostream.h', - 'istream.h', - 'iterator.h', - 'list.h', - 'map.h', - 'multimap.h', - 'multiset.h', - 'ostream.h', - 'pair.h', - 'parsestream.h', - 'pfstream.h', - 'procbuf.h', - 'pthread_alloc', - 'pthread_alloc.h', - 'rope', - 'rope.h', - 'ropeimpl.h', - 'set.h', - 'slist', - 'slist.h', - 'stack.h', - 'stdiostream.h', - 'stl_alloc.h', - 'stl_relops.h', - 'streambuf.h', - 'stream.h', - 'strfile.h', - 'strstream.h', - 'tempbuf.h', - 'tree.h', - 'type_traits.h', - 'vector.h', - # 17.6.1.2 C++ library headers - 'algorithm', - 'array', - 'atomic', - 'bitset', - 'chrono', - 'codecvt', - 'complex', - 'condition_variable', - 'deque', - 'exception', - 'forward_list', - 'fstream', - 'functional', - 'future', - 'initializer_list', - 'iomanip', - 'ios', - 'iosfwd', - 'iostream', - 'istream', - 'iterator', - 'limits', - 'list', - 'locale', - 'map', - 'memory', - 'mutex', - 'new', - 'numeric', - 'ostream', - 'queue', - 'random', - 'ratio', - 'regex', - 'scoped_allocator', - 'set', - 'sstream', - 'stack', - 'stdexcept', - 'streambuf', - 'string', - 'strstream', - 'system_error', - 'thread', - 'tuple', - 'typeindex', - 'typeinfo', - 'type_traits', - 'unordered_map', - 'unordered_set', - 'utility', - 'valarray', - 'vector', - # 17.6.1.2 C++ headers for C library facilities - 'cassert', - 'ccomplex', - 'cctype', - 'cerrno', - 'cfenv', - 'cfloat', - 'cinttypes', - 'ciso646', - 'climits', - 'clocale', - 'cmath', - 'csetjmp', - 'csignal', - 'cstdalign', - 'cstdarg', - 'cstdbool', - 'cstddef', - 'cstdint', - 'cstdio', - 'cstdlib', - 'cstring', - 'ctgmath', - 'ctime', - 'cuchar', - 'cwchar', - 'cwctype', - ]) - -# Type names -_TYPES = re.compile( - r'^(?:' - # [dcl.type.simple] - r'(char(16_t|32_t)?)|wchar_t|' - r'bool|short|int|long|signed|unsigned|float|double|' - # [support.types] - r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' - # [cstdint.syn] - r'(u?int(_fast|_least)?(8|16|32|64)_t)|' - r'(u?int(max|ptr)_t)|' - r')$') - - -# These headers are excluded from [build/include] and [build/include_order] -# checks: -# - Anything not following google file name conventions (containing an -# uppercase character, such as Python.h or nsStringAPI.h, for example). -# - Lua headers. -_THIRD_PARTY_HEADERS_PATTERN = re.compile( - r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') - -# Pattern for matching FileInfo.BaseName() against test file name -_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' - -# Pattern that matches only complete whitespace, possibly across multiple lines. -_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) - -# Assertion macros. These are defined in base/logging.h and -# testing/base/public/gunit.h. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE', 'ASSERT_TRUE', - 'EXPECT_FALSE', '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 - -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 - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments and multi-line strings -# but those have always been troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(list(_ALT_TOKEN_REPLACEMENT.keys()))) + r')(?=[ (]|$)') - - -# 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 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - -# Match strings that indicate we're working on a C (not C++) file. -_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' - r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') - -# Match string that indicates we're working on a Linux Kernel file. -_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') - -_regexp_compile_cache = {} - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -# The allowed line length of files. -# This is set by --linelength flag. -_line_length = 80 - -# The allowed extensions for file names -# This is set by --extensions flag. -_valid_extensions = {'cc', 'h', 'cpp', 'cu', 'cuh'} - -# {str, bool}: a map from error categories to booleans which indicate if the -# category should be suppressed for every line. -_global_error_suppressions = {} - - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of line 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. - """ - matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) - if matched: - if matched.group(1): - suppressed_line = linenum + 1 - else: - suppressed_line = linenum - category = matched.group(2) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(suppressed_line) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(suppressed_line) - elif category not in _LEGACY_ERROR_CATEGORIES: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ProcessGlobalSuppresions(lines): - """Updates the list of global error suppressions. - - Parses any lint directives in the file that have global effect. - - Args: - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - """ - for line in lines: - if _SEARCH_C_FILE.search(line): - for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - if _SEARCH_KERNEL_FILE.search(line): - for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - - -def ResetNolintSuppressions(): - """Resets the set of NOLINT suppressions to empty.""" - _error_suppressions.clear() - _global_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/ProcessGlobalSuppresions/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 or - global suppression. - """ - return (_global_error_suppressions.get(category, False) or - 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 pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def ReplaceAll(pattern, rep, s): - """Replaces instances of pattern in a string with a replacement. - - The compiled regex is kept in a cache shared by Match and Search. - - Args: - pattern: regex pattern - rep: replacement text - s: search string - - Returns: - string with replacements made (or original string if no replacements) - """ - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].sub(rep, s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -def _IsSourceExtension(s): - """File extension (excluding dot) matches a source file extension.""" - return s in ('c', 'cc', 'cpp', 'cxx') - - -class _IncludeState(object): - """Tracks line numbers for includes, and the order in which includes appear. - - include_list contains list of lists of (header, line number) pairs. - It's a lists of lists rather than just one flat list to make it - easier to update across preprocessor boundaries. - - 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): - self.include_list = [[]] - self.ResetSection('') - - def FindHeader(self, header): - """Check if a header has already been included. - - Args: - header: header to check. - Returns: - Line number of previous occurrence, or -1 if the header has not - been seen before. - """ - for section_list in self.include_list: - for f in section_list: - if f[0] == header: - return f[1] - return -1 - - def ResetSection(self, directive): - """Reset section checking for preprocessor directive. - - Args: - directive: preprocessor directive (e.g. "if", "else"). - """ - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - # Update list of includes. Note that we never pop from the - # include list. - if directive in ('if', 'ifdef', 'ifndef'): - self.include_list.append([]) - elif directive in ('else', 'elif'): - self.include_list[-1] = [] - - def SetLastHeader(self, header_path): - self._last_header = header_path - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - 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, clean_lines, linenum, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - header_path: Canonicalized header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - # If previous section is different from current section, _last_header will - # be reset to empty string, so it's always less than current header. - # - # If previous line was a blank line, assume that the headers are - # intentionally sorted the way they are. - if (self._last_header > header_path and - Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): - return False - 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[:] - # backup of filter list. Used to restore the state after each file. - self._filters_backup = self.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[:] - self.AddFilters(filters) - - def AddFilters(self, filters): - """ Adds more filters to the existing list of error-message 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 BackupFilters(self): - """ Saves the current filter list to backup storage.""" - self._filters_backup = self.filters[:] - - def RestoreFilters(self): - """ Restores filters previously backed up.""" - self.filters = self._filters_backup[:] - - 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.items(): - 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) - -def _AddFilters(filters): - """Adds more filter overrides. - - Unlike _SetFilters, this function does not reset the current list of filters - available. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.AddFilters(filters) - -def _BackupFilters(): - """ Saves the current filter list to backup storage.""" - _cpplint_state.BackupFilters() - -def _RestoreFilters(): - """ Restores filters previously backed up.""" - _cpplint_state.RestoreFilters() - -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 not self.in_a_function: - return - - 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 analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo(object): - """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 <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = current_dir = os.path.dirname(fullname) - while current_dir != os.path.dirname(current_dir): - if (os.path.exists(os.path.join(current_dir, ".git")) or - os.path.exists(os.path.join(current_dir, ".hg")) or - os.path.exists(os.path.join(current_dir, ".svn"))): - root_dir = current_dir - current_dir = os.path.dirname(current_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - 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 _IsSourceExtension(self.Extension()[1:]) - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not 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)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %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 sequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Match a single C style comment on the same line. -_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' -# Matches multi-line C style 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*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + - _RE_PATTERN_C_COMMENTS + r'\s+|' + - r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + - _RE_PATTERN_C_COMMENTS + r')') - - -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 CleanseRawStrings(raw_lines): - """Removes C++11 raw strings from lines. - - Before: - static const char kData[] = R"( - multi-line string - )"; - - After: - static const char kData[] = "" - (replaced by blank line) - ""; - - Args: - raw_lines: list of raw lines. - - Returns: - list of lines with C++11 raw strings replaced by empty strings. - """ - - delimiter = None - lines_without_raw_strings = [] - for line in raw_lines: - if delimiter: - # Inside a raw string, look for the end - end = line.find(delimiter) - if end >= 0: - # Found the end of the string, match leading space for this - # line and resume copying the original lines, and also insert - # a "" on the last line. - leading_space = Match(r'^(\s*)\S', line) - line = leading_space.group(1) + '""' + line[end + len(delimiter):] - delimiter = None - else: - # Haven't found the end yet, append a blank line. - line = '""' - - # Look for beginning of a raw string, and replace them with - # empty strings. This is done in a loop to handle multiple raw - # strings on the same line. - while delimiter is None: - # Look for beginning of a raw string. - # See 2.14.15 [lex.string] for syntax. - # - # Once we have matched a raw string, we check the prefix of the - # line to make sure that the line is not part of a single line - # comment. It's done this way because we remove raw strings - # before removing comments as opposed to removing comments - # before removing raw strings. This is because there are some - # cpplint checks that requires the comments to be preserved, but - # we don't want to check comments that are inside raw strings. - matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) - if (matched and - not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', - matched.group(1))): - delimiter = ')' + matched.group(2) + '"' - - end = matched.group(3).find(delimiter) - if end >= 0: - # Raw string ended on same line - line = (matched.group(1) + '""' + - matched.group(3)[end + len(delimiter):]) - delimiter = None - else: - # Start of a multi-line raw string - line = matched.group(1) + '""' - else: - break - - lines_without_raw_strings.append(line) - - # TODO(unknown): if delimiter is not None here, we might want to - # emit a warning for unterminated string. - return lines_without_raw_strings - - -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] = '/**/' - - -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].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 4 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. - 3) raw_lines member contains all the lines without processing. - 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw - strings removed. - All these 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) - self.lines_without_raw_strings = CleanseRawStrings(lines) - for linenum in range(len(self.lines_without_raw_strings)): - self.lines.append(CleanseComments( - self.lines_without_raw_strings[linenum])) - elided = self._CollapseStrings(self.lines_without_raw_strings[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 _RE_PATTERN_INCLUDE.match(elided): - return 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) - - # Replace quoted strings and digit separators. Both single quotes - # and double quotes are processed in the same loop, otherwise - # nested quotes wouldn't work. - collapsed = '' - while True: - # Find the first quote character - match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) - if not match: - collapsed += elided - break - head, quote, tail = match.groups() - - if quote == '"': - # Collapse double quoted strings - second_quote = tail.find('"') - if second_quote >= 0: - collapsed += head + '""' - elided = tail[second_quote + 1:] - else: - # Unmatched double quote, don't bother processing the rest - # of the line since this is probably a multiline string. - collapsed += elided - break - else: - # Found single quote, check nearby text to eliminate digit separators. - # - # There is no special handling for floating point here, because - # the integer/fractional/exponent parts would all be parsed - # correctly as long as there are digits on both sides of the - # separator. So we are fine as long as we don't see something - # like "0.'3" (gcc 4.9.0 will not allow this literal). - if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): - match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) - collapsed += head + match_literal.group(1).replace("'", '') - elided = match_literal.group(2) - else: - second_quote = tail.find('\'') - if second_quote >= 0: - collapsed += head + "''" - elided = tail[second_quote + 1:] - else: - # Unmatched single quote - collapsed += elided - break - - return collapsed - - -def FindEndOfExpressionInLine(line, startpos, stack): - """Find the position just after the end of current parenthesized expression. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - stack: nesting stack at startpos. - - Returns: - On finding matching end: (index just after matching end, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at end of this line) - """ - for i in range(startpos, len(line)): - char = line[i] - if char in '([{': - # Found start of parenthesized expression, push to expression stack - stack.append(char) - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - if stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - elif i > 0 and Search(r'\boperator\s*$', line[0:i]): - # operator<, don't add to stack - continue - else: - # Tentative start of template argument list - stack.append('<') - elif char in ')]}': - # Found end of parenthesized expression. - # - # If we are currently expecting a matching '>', the pending '<' - # must have been an operator. Remove them from expression stack. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - if ((stack[-1] == '(' and char == ')') or - (stack[-1] == '[' and char == ']') or - (stack[-1] == '{' and char == '}')): - stack.pop() - if not stack: - return (i + 1, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == '>': - # Found potential end of template argument list. - - # Ignore "->" and operator functions - if (i > 0 and - (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): - continue - - # Pop the stack if there is a matching '<'. Otherwise, ignore - # this '>' since it must be an operator. - if stack: - if stack[-1] == '<': - stack.pop() - if not stack: - return (i + 1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '>', the matching '<' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - - # Did not find end of expression or unbalanced parentheses on this line - return (-1, stack) - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [ or <, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the - linenum/pos that correspond to the closing of the expression. - - TODO(unknown): cpplint spends a fair bit of time matching parentheses. - Ideally we would want to index all opening and closing parentheses once - and have CloseExpression be just a simple lookup, but due to preprocessor - tricks, this is not so easy. - - 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] - if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): - return (line, clean_lines.NumLines(), -1) - - # Check first line - (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) - if end_pos > -1: - return (line, linenum, end_pos) - - # Continue scanning forward - while stack and linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) - if end_pos > -1: - return (line, linenum, end_pos) - - # Did not find end of expression before end of file, give up - return (line, clean_lines.NumLines(), -1) - - -def FindStartOfExpressionInLine(line, endpos, stack): - """Find position at the matching start of current expression. - - This is almost the reverse of FindEndOfExpressionInLine, but note - that the input position and returned position differs by 1. - - Args: - line: a CleansedLines line. - endpos: start searching at this position. - stack: nesting stack at endpos. - - Returns: - On finding matching start: (index at matching start, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at beginning of this line) - """ - i = endpos - while i >= 0: - char = line[i] - if char in ')]}': - # Found end of expression, push to expression stack - stack.append(char) - elif char == '>': - # Found potential end of template argument list. - # - # Ignore it if it's a "->" or ">=" or "operator>" - if (i > 0 and - (line[i - 1] == '-' or - Match(r'\s>=\s', line[i - 1:]) or - Search(r'\boperator\s*$', line[0:i]))): - i -= 1 - else: - stack.append('>') - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - i -= 1 - else: - # If there is a matching '>', we can pop the expression stack. - # Otherwise, ignore this '<' since it must be an operator. - if stack and stack[-1] == '>': - stack.pop() - if not stack: - return (i, None) - elif char in '([{': - # Found start of expression. - # - # If there are any unmatched '>' on the stack, they must be - # operators. Remove those. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - if ((char == '(' and stack[-1] == ')') or - (char == '[' and stack[-1] == ']') or - (char == '{' and stack[-1] == '}')): - stack.pop() - if not stack: - return (i, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '<', the matching '>' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - - i -= 1 - - return (-1, stack) - - -def ReverseCloseExpression(clean_lines, linenum, pos): - """If input points to ) or } or ] or >, finds the position that opens it. - - If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the - linenum/pos that correspond to the opening 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 *at* the opening brace, or - (line, 0, -1) if we never find the matching opening brace. Note - we ignore strings and comments when matching; and the line we - return is the 'cleansed' line at linenum. - """ - line = clean_lines.elided[linenum] - if line[pos] not in ')}]>': - return (line, 0, -1) - - # Check last line - (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) - if start_pos > -1: - return (line, linenum, start_pos) - - # Continue scanning backward - while stack and linenum > 0: - linenum -= 1 - line = clean_lines.elided[linenum] - (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) - if start_pos > -1: - return (line, linenum, start_pos) - - # Did not find start of expression before beginning of file, give up - return (line, 0, -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 range(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 GetIndentLevel(line): - """Return the number of leading spaces in line. - - Args: - line: A string to check. - - Returns: - An integer count of leading spaces, possibly zero. - """ - indent = Match(r'^( *)\S', line) - if indent: - return len(indent.group(1)) - else: - return 0 - - -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) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - # Replace 'c++' with 'cpp'. - filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - suffix = os.sep - # On Windows using directory separator will leave us with - # "bogus escape error" unless we properly escape regex. - if suffix == '\\': - suffix += '\\' - file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root) - return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, clean_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. - clean_lines: A CleansedLines instance containing the file. - error: The function to call with any errors found. - """ - - # Don't check for header guards if there are error suppression - # comments somewhere in this file. - # - # Because this is silencing a warning for a nonexistent line, we - # only support the very specific NOLINT(build/header_guard) syntax, - # and not the general NOLINT or NOLINT(*) syntax. - raw_lines = clean_lines.lines_without_raw_strings - for i in raw_lines: - if Search(r'//\s*NOLINT\(build/header_guard\)', i): - return - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = '' - ifndef_linenum = 0 - define = '' - endif = '' - endif_linenum = 0 - for linenum, line in enumerate(raw_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, raw_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) - - # Check for "//" comments on endif line. - ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, - error) - match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) - if match: - if match.group(1) == '_': - # Issue low severity warning for deprecated double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif // %s"' % cppvar) - return - - # Didn't find the corresponding "//" comment. If this file does not - # contain any "//" comments at all, it could be that the compiler - # only wants "/**/" comments, look for those instead. - no_single_line_comments = True - for i in range(1, len(raw_lines) - 1): - line = raw_lines[i] - if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): - no_single_line_comments = False - break - - if no_single_line_comments: - match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) - if match: - if match.group(1) == '_': - # Low severity warning for double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif /* %s */"' % cppvar) - return - - # Didn't find anything - error(filename, endif_linenum, 'build/header_guard', 5, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckHeaderFileIncluded(filename, include_state, error): - """Logs an error if a .cc file does not include its header.""" - - # Do not check test files - fileinfo = FileInfo(filename) - if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): - return - - headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' - if not os.path.exists(headerfile): - return - headername = FileInfo(headerfile).RepositoryName() - first_include = 0 - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] - - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) - - -def CheckForBadCharacters(filename, lines, error): - """Logs an error for each line containing bad characters. - - Two kinds of bad characters: - - 1. 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. - - 2. NUL bytes. These are problematic for some tools. - - 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 '\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - if '\0' in line: - error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') - - -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. ' - 'Use C++11 raw strings or concatenation instead.') - - -# (non-threadsafe name, thread-safe alternative, validation pattern) -# -# The validation pattern is used to eliminate false positives such as: -# _rand(); // false positive due to substring match. -# ->rand(); // some member function rand(). -# ACMRandom rand(seed); // some variable named rand. -# ISAACRandom rand(); // another variable named rand. -# -# Basically we require the return value of these functions to be used -# in some expression context on the same line by matching on some -# operator before the function name. This eliminates constructors and -# member function calls. -_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' -_THREADING_LIST = ( - ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), - ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), - ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), - ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), - ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), - ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), - ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), - ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), - ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), - ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), - ('strtok(', 'strtok_r(', - _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), - ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), - ) - - -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_func, multithread_safe_func, pattern in _THREADING_LIST: - # Additional pattern matching check to confirm that this is the - # function we are looking for - if Search(pattern, line): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_func + - '...) instead of ' + single_thread_func + - '...) for improved thread safety.') - - -def CheckVlogArguments(filename, clean_lines, linenum, error): - """Checks that VLOG() is only used for defining a logging level. - - For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and - VLOG(FATAL) are not. - - 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 Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): - error(filename, linenum, 'runtime/vlog', 5, - 'VLOG() should be used with numeric verbosity level. ' - 'Use LOG() if you want symbolic severity levels.') - -# 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*).') - - -def IsMacroDefinition(clean_lines, linenum): - if Search(r'^#define', clean_lines[linenum]): - return True - - if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): - return True - - return False - - -def IsForwardClassDeclaration(clean_lines, linenum): - return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, linenum, seen_open_brace): - self.starting_linenum = linenum - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - self.check_namespace_indentation = False - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - 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. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - 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. - """ - pass - - def IsBlockInfo(self): - """Returns true if this block is a _BlockInfo. - - This is convenient for verifying that an object is an instance of - a _BlockInfo, but not an instance of any of the derived classes. - - Returns: - True for this class, False for derived classes. - """ - return self.__class__ == _BlockInfo - - -class _ExternCInfo(_BlockInfo): - """Stores information about an 'extern "C"' block.""" - - def __init__(self, linenum): - _BlockInfo.__init__(self, linenum, True) - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name - self.is_derived = False - self.check_namespace_indentation = True - if class_or_struct == 'struct': - self.access = 'public' - self.is_struct = True - else: - self.access = 'private' - self.is_struct = False - - # Remember initial indentation level for this class. Using raw_lines here - # instead of elided to account for leading comments. - self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - # If there is a DISALLOW macro, it should appear near the end of - # the class. - seen_last_thing_in_class = False - for i in range(linenum - 1, self.starting_linenum, -1): - match = Search( - r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + - self.name + r'\)', - clean_lines.elided[i]) - if match: - if seen_last_thing_in_class: - error(filename, i, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - break - - if not Match(r'^\s*$', clean_lines.elided[i]): - seen_last_thing_in_class = True - - # Check that closing brace is aligned with beginning of the class. - # Only do this if the closing brace is indented by only whitespaces. - # This means we will not check single-line class definitions. - indent = Match(r'^( *)\}', clean_lines.elided[linenum]) - if indent and len(indent.group(1)) != self.class_indent: - if self.is_struct: - parent = 'struct ' + self.name - else: - parent = 'class ' + self.name - error(filename, linenum, 'whitespace/indent', 3, - 'Closing brace should be aligned with beginning of %s' % parent) - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name or '' - self.check_namespace_indentation = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. - # - # We also accept stuff like "// end of namespace ." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. - if self.name: - # Named namespace - if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + - re.escape(self.name) + r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - # If "// namespace anonymous" or "// anonymous namespace (more text)", - # mention "// anonymous namespace" as an acceptable form - if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"' - ' or "// anonymous namespace"') - else: - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Top of the previous stack before each Update(). - # - # Because the nesting_stack is updated at the end of each line, we - # had to do some convoluted checks to find out what is the current - # scope at the beginning of the line. This check is simplified by - # saving the previous top of nesting stack. - # - # We could save the full stack, but we only need the top. Copying - # the full nesting stack would slow down cpplint by ~10%. - self.previous_stack_top = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def InExternC(self): - """Check if we are currently one level inside an 'extern "C"' block. - - Returns: - True if top of the stack is an extern block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ExternCInfo) - - def InClassDeclaration(self): - """Check if we are currently one level inside a class or struct declaration. - - Returns: - True if top of the stack is a class/struct, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ClassInfo) - - def InAsmBlock(self): - """Check if we are currently one level inside an inline ASM block. - - Returns: - True if the top of the stack is a block containing inline ASM. - """ - return self.stack and self.stack[-1].inline_asm != _NO_ASM - - def InTemplateArgumentList(self, clean_lines, linenum, pos): - """Check if current position is inside template argument list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: position just after the suspected template argument. - Returns: - True if (linenum, pos) is inside template arguments. - """ - while linenum < clean_lines.NumLines(): - # Find the earliest character that might indicate a template argument - line = clean_lines.elided[linenum] - match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) - if not match: - linenum += 1 - pos = 0 - continue - token = match.group(1) - pos += len(match.group(0)) - - # These things do not look like template argument list: - # class Suspect { - # class Suspect x; } - if token in ('{', '}', ';'): return False - - # These things look like template argument list: - # template - # template - # template - # template - if token in ('>', '=', '[', ']', '.'): return True - - # Check if token is an unmatched '<'. - # If not, move on to the next character. - if token != '<': - pos += 1 - if pos >= len(line): - linenum += 1 - pos = 0 - continue - - # We can't be sure if we just find a single '<', and need to - # find the matching '>'. - (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) - if end_pos < 0: - # Not sure if template argument list or syntax error in file - return False - linenum = end_line - pos = end_pos - return False - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - # TODO(unknown): Update() is too long, but we will refactor later. - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current 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] - - # Remember top of the previous nesting stack. - # - # The stack is always pushed/popped and not modified in place, so - # we can just do a shallow copy instead of copy.deepcopy. Using - # deepcopy would slow down cpplint by ~28%. - if self.stack: - self.previous_stack_top = self.stack[-1] - else: - self.previous_stack_top = None - - # Update pp_stack - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - class_decl_match = Match( - r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' - r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' - r'(.*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - # We do not want to accept classes that are actually template arguments: - # template , - # template class Ignore3> - # void Function() {}; - # - # To avoid template argument cases, we scan forward and look for - # an unmatched '>'. If we see one, assume we are inside a - # template argument list. - end_declaration = len(class_decl_match.group(1)) - if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): - self.stack.append(_ClassInfo( - class_decl_match.group(3), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(4) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - classinfo = self.stack[-1] - access_match = Match( - r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' - r':(?:[^:]|$)', - line) - if access_match: - classinfo.access = access_match.group(2) - - # Check that access keywords are indented +1 space. Skip this - # check if the keywords are not preceded by whitespaces. - indent = access_match.group(1) - if (len(indent) != classinfo.class_indent + 1 and - Match(r'^\s*$', indent)): - if classinfo.is_struct: - parent = 'struct ' + classinfo.name - else: - parent = 'class ' + classinfo.name - slots = '' - if access_match.group(3): - slots = access_match.group(3) - error(filename, linenum, 'whitespace/indent', 3, - '%s%s: should be indented +1 space inside %s' % ( - access_match.group(2), slots, parent)) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - elif Match(r'^extern\s*"[^"]*"\s*\{', line): - self.stack.append(_ExternCInfo(linenum)) - else: - self.stack.append(_BlockInfo(linenum, True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckCompletedBlocks(self, filename, error): - """Checks that all classes and namespaces 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. - """ - # 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. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - elif isinstance(obj, _NamespaceInfo): - error(filename, obj.starting_linenum, 'build/namespaces', 5, - 'Failed to find complete declaration of namespace %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - r"""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.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # 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. - explicit_constructor_match = Match( - r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' - r'\(((?:[^()]|\([^()]*\))*)\)' - % re.escape(base_classname), - line) - - if explicit_constructor_match: - is_marked_explicit = explicit_constructor_match.group(1) - - if not explicit_constructor_match.group(2): - constructor_args = [] - else: - constructor_args = explicit_constructor_match.group(2).split(',') - - # collapse arguments so that commas in template parameter lists and function - # argument parameter lists don't split arguments in two - i = 0 - while i < len(constructor_args): - constructor_arg = constructor_args[i] - while (constructor_arg.count('<') > constructor_arg.count('>') or - constructor_arg.count('(') > constructor_arg.count(')')): - constructor_arg += ',' + constructor_args[i + 1] - del constructor_args[i + 1] - constructor_args[i] = constructor_arg - i += 1 - - defaulted_args = [arg for arg in constructor_args if '=' in arg] - noarg_constructor = (not constructor_args or # empty arg list - # 'void' arg specifier - (len(constructor_args) == 1 and - constructor_args[0].strip() == 'void')) - onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg - not noarg_constructor) or - # all but at most one arg defaulted - (len(constructor_args) >= 1 and - not noarg_constructor and - len(defaulted_args) >= len(constructor_args) - 1)) - initializer_list_constructor = bool( - onearg_constructor and - Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) - copy_constructor = bool( - onearg_constructor and - Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' - % re.escape(base_classname), constructor_args[0].strip())) - - if (not is_marked_explicit and - onearg_constructor and - not initializer_list_constructor and - not copy_constructor): - if defaulted_args: - error(filename, linenum, 'runtime/explicit', 5, - 'Constructors callable with one argument ' - 'should be marked explicit.') - else: - error(filename, linenum, 'runtime/explicit', 5, - 'Single-parameter constructors should be marked explicit.') - elif is_marked_explicit and not onearg_constructor: - if noarg_constructor: - error(filename, linenum, 'runtime/explicit', 5, - 'Zero-parameter constructors should not be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): - """Checks for the correctness of various spacing around function calls. - - 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] - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', - fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and - not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and - not Search(r'\bcase\s+\(', fncall)): - # TODO(unknown): Space after an operator function seem to be a common - # error, silence those for now by restricting them to highest verbosity. - if Search(r'\boperator_*\b', line): - error(filename, linenum, 'whitespace/parens', 0, - 'Extra space before ( in function call') - else: - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error): - is_namespace_indent_item = ( - len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and - nesting_state.previous_stack_top == nesting_state.stack[-2]) - - if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - clean_lines.elided, line): - CheckItemIndentationInNamespace(filename, clean_lines.elided, - line, error) - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in range(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(line, filename, linenum, next_line_start, error): - """Checks for common mistakes in comments. - - Args: - line: The line in question. - filename: The name of the current file. - linenum: The number of the line to check. - next_line_start: The first non-whitespace column of the next line. - error: The function to call with any errors found. - """ - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: - # Allow one space for new scopes, two spaces otherwise: - if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) 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') - - # Checks for common mistakes in TODO comments. - comment = line[commentpos:] - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 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=g-explicit-bool-comparison - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - - # If the comment contains an alphanumeric character, there - # should be a space somewhere between it and the // unless - # it's a /// or //! Doxygen comment. - if (Match(r'//[^ ]*\w', comment) and - not Match(r'(///|//\!)(\s+|$)', comment)): - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* 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. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, 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 add a blank line - after public/protected/private, 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. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw = clean_lines.lines_without_raw_strings - 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 '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - # - # Also skip blank line checks for 'extern "C"' blocks, which are formatted - # like namespaces. - if (IsBlankLine(line) and - not nesting_state.InNamespaceBody() and - not nesting_state.InExternC()): - 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: - # 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 parameters 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, - 'Redundant blank line at the start of a code block ' - 'should be deleted.') - # 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('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Redundant blank line at the end of a code block ' - 'should be deleted.') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, check comments - next_line_start = 0 - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - next_line_start = len(next_line) - len(next_line.lstrip()) - CheckComment(line, filename, linenum, next_line_start, error) - - # get rid of comments and strings - line = clean_lines.elided[linenum] - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'return []() {};' - if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search(r'for *\(.*[^:]:[^: ]', line) or - Search(r'for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckOperatorSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around operators. - - 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] - - # Don't try to do spacing checks for operator methods. Do this by - # replacing the troublesome characters with something else, - # preserving column position for all other characters. - # - # The replacement is done repeatedly to avoid false positives from - # operators that call operators. - while True: - match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) - if match: - line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) - else: - break - - # 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.]=', line) or - Search(r'=[\w.]', line)) - and not Search(r'\b(if|while|for) ', line) - # Operators taken from [lex.operators] in C++11 standard. - and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) - and not Search(r'operator=', 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. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - # - # If the operator is followed by a comma, assume it's be used in a - # macro context and don't do any checks. This avoids false - # positives. - # - # Note that && is not included here. This is because there are too - # many false positives due to RValue references. - match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - elif not Match(r'#.*include', line): - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Match(r'^(.*[^\s<])<[^\s=<,]', line) - if match: - (_, _, end_pos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if end_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) - if match: - (_, _, start_pos) = ReverseCloseExpression( - clean_lines, linenum, len(match.group(1))) - if start_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - # - # We also allow operators following an opening parenthesis, since - # those tend to be macros that deal with operators. - match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) - if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and - not (match.group(1) == 'operator' and match.group(2) == ';')): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # 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)) - - -def CheckParenthesisSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around parentheses. - - 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] - - # 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 len(match.group(2)) not in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - -def CheckCommaSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing near commas and semicolons. - - 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.lines_without_raw_strings - line = clean_lines.elided[linenum] - - # You should always have a space after a comma (either as fn arg or operator) - # - # This does not apply when the non-space character following the - # comma is another comma, since the only time when that happens is - # for empty macro arguments. - # - # We run this check in two passes: first pass on elided lines to - # verify that lines contain missing whitespaces, second pass on raw - # lines to confirm that those missing whitespaces are not due to - # elided comments. - if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and - Search(r',[^,\s]', raw[linenum])): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - -def _IsType(clean_lines, nesting_state, expr): - """Check if expression looks like a type name, returns true if so. - - Args: - clean_lines: A CleansedLines instance containing the file. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - expr: The expression to check. - Returns: - True, if token looks like a type. - """ - # Keep only the last token in the expression - last_word = Match(r'^.*(\b\S+)$', expr) - if last_word: - token = last_word.group(1) - else: - token = expr - - # Match native types and stdint types - if _TYPES.match(token): - return True - - # Try a bit harder to match templated types. Walk up the nesting - # stack until we find something that resembles a typename - # declaration for what we are looking for. - typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + - r'\b') - block_index = len(nesting_state.stack) - 1 - while block_index >= 0: - if isinstance(nesting_state.stack[block_index], _NamespaceInfo): - return False - - # Found where the opening brace is. We want to scan from this - # line up to the beginning of the function, minus a few lines. - # template - # class C - # : public ... { // start scanning here - last_line = nesting_state.stack[block_index].starting_linenum - - next_block_start = 0 - if block_index > 0: - next_block_start = nesting_state.stack[block_index - 1].starting_linenum - first_line = last_line - while first_line >= next_block_start: - if clean_lines.elided[first_line].find('template') >= 0: - break - first_line -= 1 - if first_line < next_block_start: - # Didn't find any "template" keyword before reaching the next block, - # there are probably no template things to check for this block - block_index -= 1 - continue - - # Look for typename in the specified range - for i in range(first_line, last_line + 1, 1): - if Search(typename_pattern, clean_lines.elided[i]): - return True - block_index -= 1 - - return False - - -def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for horizontal spacing near commas. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces when they are delimiting blocks, classes, namespaces etc. - # And since you should never have braces at the beginning of a line, - # this is an easy test. Except that braces used for initialization don't - # follow the same rule; we often don't want spaces before those. - match = Match(r'^(.*[^ ({>]){', line) - - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # ternary = expr ? new type{} : nullptr; - # OuterTemplate{}> - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<>]:". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - leading_text = match.group(1) - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in range(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] - # We also suppress warnings for `uint64_t{expression}` etc., as the style - # guide recommends brace initialization for integral types to avoid - # overflow/truncation. - if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) - and not _IsType(clean_lines, nesting_state, leading_text)): - 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 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 IsDecltype(clean_lines, linenum, column): - """Check if the token ending on (linenum, column) is decltype(). - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is decltype() expression, False otherwise. - """ - (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) - if start_col < 0: - return False - if Search(r'\bdecltype\s*$', text[0:start_col]): - return True - return False - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -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. Braces are also - # used for brace initializers inside function calls. 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 '}', or if the - # previous line starts a preprocessor block. We also allow a brace on the - # following line if it is part of an array initialization and would not fit - # within the 80 character limit of the preceding line. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline) and - not (GetLineWidth(prevline) > _line_length - 2 and '[]' in 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\b\s*(?:if\b|\{|$)', 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'else if\s*\(', line): # could be multi-line if - brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - brace_on_right = endline[endpos:].find('{') != -1 - if brace_on_left != brace_on_right: # 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') - elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - 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') - - # Check single-line if/else bodies. The style guide says 'curly braces are not - # required for single-line statements'. We additionally allow multi-line, - # single statements, but we reject anything with more than one semicolon in - # it. This means that the first semicolon after the if should be at the end of - # its line, and the line after that should have an indent level equal to or - # lower than the if. We also check for ambiguous if/else nesting without - # braces. - if_else_match = Search(r'\b(if\s*\(|else\b)', line) - if if_else_match and not Match(r'\s*#', line): - if_indent = GetIndentLevel(line) - endline, endlinenum, endpos = line, linenum, if_else_match.end() - if_match = Search(r'\bif\s*\(', line) - if if_match: - # This could be a multiline if condition, so find the end first. - pos = if_match.end() - 1 - (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) - # Check for an opening brace, either directly after the if or on the next - # line. If found, this isn't a single-statement conditional. - if (not Match(r'\s*{', endline[endpos:]) - and not (Match(r'\s*$', endline[endpos:]) - and endlinenum < (len(clean_lines.elided) - 1) - and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): - while (endlinenum < len(clean_lines.elided) - and ';' not in clean_lines.elided[endlinenum][endpos:]): - endlinenum += 1 - endpos = 0 - if endlinenum < len(clean_lines.elided): - endline = clean_lines.elided[endlinenum] - # We allow a mix of whitespace and closing braces (e.g. for one-liner - # methods) and a single \ after the semicolon (for macros) - endpos = endline.find(';') - if not Match(r';[\s}]*(\\?)$', endline[endpos:]): - # Semicolon isn't the last character, there's something trailing. - # Output a warning if the semicolon is not contained inside - # a lambda expression. - if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', - endline): - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - elif endlinenum < len(clean_lines.elided) - 1: - # Make sure the next line is dedented - next_line = clean_lines.elided[endlinenum + 1] - next_indent = GetIndentLevel(next_line) - # With ambiguous nested if statements, this will error out on the - # if that *doesn't* match the else, regardless of whether it's the - # inner one or outer one. - if (if_match and Match(r'\s*else\b', next_line) - and next_indent != if_indent): - error(filename, linenum, 'readability/braces', 4, - 'Else clause should be indented at the same level as if. ' - 'Ambiguous nested if/else chains require braces.') - elif next_indent > if_indent: - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - - -def CheckTrailingSemicolon(filename, clean_lines, linenum, error): - """Looks for redundant trailing semicolon. - - 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] - - # Block bodies should not be followed by a semicolon. Due to C++11 - # brace initialization, there are more places where semicolons are - # required than not, so we use a whitelist approach to check these - # rather than a blacklist. These are the places where "};" should - # be replaced by just "}": - # 1. Some flavor of block following closing parenthesis: - # for (;;) {}; - # while (...) {}; - # switch (...) {}; - # Function(...) {}; - # if (...) {}; - # if (...) else if (...) {}; - # - # 2. else block: - # if (...) else {}; - # - # 3. const member function: - # Function(...) const {}; - # - # 4. Block following some statement: - # x = 42; - # {}; - # - # 5. Block at the beginning of a function: - # Function(...) { - # {}; - # } - # - # Note that naively checking for the preceding "{" will also match - # braces inside multi-dimensional arrays, but this is fine since - # that expression will not contain semicolons. - # - # 6. Block following another block: - # while (true) {} - # {}; - # - # 7. End of namespaces: - # namespace {}; - # - # These semicolons seems far more common than other kinds of - # redundant semicolons, possibly due to people converting classes - # to namespaces. For now we do not warn for this case. - # - # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) - if match: - # Matched closing parenthesis (case 1). Check the token before the - # matching opening parenthesis, and don't warn if it looks like a - # macro. This avoids these false positives: - # - macro that defines a base class - # - multi-line macro that defines a base class - # - macro that defines the whole class-head - # - # But we still issue warnings for macros that we know are safe to - # warn, specifically: - # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P - # - TYPED_TEST - # - INTERFACE_DEF - # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: - # - # We implement a whitelist of safe macros instead of a blacklist of - # unsafe macros, even though the latter appears less frequently in - # google code and would have been easier to implement. This is because - # the downside for getting the whitelist wrong means some extra - # semicolons, while the downside for getting the blacklist wrong - # would result in compile errors. - # - # In addition to macros, we also don't want to warn on - # - Compound literals - # - Lambdas - # - alignas specifier with anonymous structs - # - decltype - closing_brace_pos = match.group(1).rfind(')') - opening_parenthesis = ReverseCloseExpression( - clean_lines, linenum, closing_brace_pos) - if opening_parenthesis[2] > -1: - line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) - func = Match(r'^(.*\])\s*$', line_prefix) - if ((macro and - macro.group(1) not in ( - 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', - 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', - 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or - Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or - Search(r'\bdecltype$', line_prefix) or - Search(r'\s+=\s*$', line_prefix)): - match = None - if (match and - opening_parenthesis[1] > 1 and - Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): - # Multi-line lambda-expression - match = None - - else: - # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) - if not match: - # Try matching cases 4-6. These are always matched on separate lines. - # - # Note that we can't simply concatenate the previous line to the - # current line and do a single match, otherwise we may output - # duplicate warnings for the blank line case: - # if (cond) { - # // blank line - # } - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) - - # Check matching closing brace - if match: - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): - # Current {} pair is eligible for semicolon check, and we have found - # the redundant semicolon, output warning here. - # - # Note: because we are scanning forward for opening braces, and - # outputting warnings for the matching closing brace, if there are - # nested blocks with trailing semicolons, we will get the error - # messages in reversed order. - - # We need to check the line forward for NOLINT - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1, - error) - ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum, - error) - - error(filename, endlinenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyBlockBody(filename, clean_lines, linenum, error): - """Look for empty loop/conditional body with only a single semicolon. - - 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. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - # - # We also check "if" blocks here, since an empty conditional block - # is likely an error. - line = clean_lines.elided[linenum] - matched = Match(r'\s*(for|while|if)\s*\(', line) - if matched: - # Find the end of the conditional expression. - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - if matched.group(1) == 'if': - error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, - 'Empty conditional bodies should use {}') - else: - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - # Check for if statements that have completely empty bodies (no comments) - # and no else clauses. - if end_pos >= 0 and matched.group(1) == 'if': - # Find the position of the opening { for the if statement. - # Return without logging an error if it has no brackets. - opening_linenum = end_linenum - opening_line_fragment = end_line[end_pos:] - # Loop until EOF or find anything that's not whitespace or opening {. - while not Search(r'^\s*\{', opening_line_fragment): - if Search(r'^(?!\s*$)', opening_line_fragment): - # Conditional has no brackets. - return - opening_linenum += 1 - if opening_linenum == len(clean_lines.elided): - # Couldn't find conditional's opening { or any code before EOF. - return - opening_line_fragment = clean_lines.elided[opening_linenum] - # Set opening_line (opening_line_fragment may not be entire opening line). - opening_line = clean_lines.elided[opening_linenum] - - # Find the position of the closing }. - opening_pos = opening_line_fragment.find('{') - if opening_linenum == end_linenum: - # We need to make opening_pos relative to the start of the entire line. - opening_pos += end_pos - (closing_line, closing_linenum, closing_pos) = CloseExpression( - clean_lines, opening_linenum, opening_pos) - if closing_pos < 0: - return - - # Now construct the body of the conditional. This consists of the portion - # of the opening line after the {, all lines until the closing line, - # and the portion of the closing line before the }. - if (clean_lines.raw_lines[opening_linenum] != - CleanseComments(clean_lines.raw_lines[opening_linenum])): - # Opening line ends with a comment, so conditional isn't empty. - return - if closing_linenum > opening_linenum: - # Opening line after the {. Ignore comments here since we checked above. - body = list(opening_line[opening_pos+1:]) - # All lines until closing line, excluding closing line, with comments. - body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) - # Closing line before the }. Won't (and can't) have comments. - body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) - body = '\n'.join(body) - else: - # If statement has brackets and fits on a single line. - body = opening_line[opening_pos+1:closing_pos-1] - - # Check if the body is empty - if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): - return - # The body is empty. Now make sure there's not an else clause. - current_linenum = closing_linenum - current_line_fragment = closing_line[closing_pos:] - # Loop until EOF or find anything that's not whitespace or else clause. - while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): - if Search(r'^(?=\s*else)', current_line_fragment): - # Found an else clause, so don't log an error. - return - current_linenum += 1 - if current_linenum == len(clean_lines.elided): - break - current_line_fragment = clean_lines.elided[current_linenum] - - # The body is empty and there's no else clause until EOF or other code. - error(filename, end_linenum, 'whitespace/empty_if_body', 4, - ('If statement had no body and no else clause')) - - -def FindCheckMacro(line): - """Find a replaceable CHECK-like macro. - - Args: - line: line to search on. - Returns: - (macro name, start position), or (None, -1) if no replaceable - macro is found. - """ - for macro in _CHECK_MACROS: - i = line.find(macro) - if i >= 0: - # Find opening parenthesis. Do a regular expression match here - # to make sure that we are matching the expected CHECK macro, as - # opposed to some other macro that happens to contain the CHECK - # substring. - matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) - if not matched: - continue - return (macro, len(matched.group(1))) - return (None, -1) - - -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 - lines = clean_lines.elided - (check_macro, start_pos) = FindCheckMacro(lines[linenum]) - if not check_macro: - return - - # Find end of the boolean expression by matching parentheses - (last_line, end_line, end_pos) = CloseExpression( - clean_lines, linenum, start_pos) - if end_pos < 0: - return - - # If the check macro is followed by something other than a - # semicolon, assume users will log their own custom error messages - # and don't suggest any replacements. - if not Match(r'\s*;', last_line[end_pos:]): - return - - if linenum == end_line: - expression = lines[linenum][start_pos + 1:end_pos - 1] - else: - expression = lines[linenum][start_pos + 1:] - for i in range(linenum + 1, end_line): - expression += lines[i] - expression += last_line[0:end_pos - 1] - - # Parse expression so that we can take parentheses into account. - # This avoids false positives for inputs like "CHECK((a < 4) == b)", - # which is not replaceable by CHECK_LE. - lhs = '' - rhs = '' - operator = None - while expression: - matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' - r'==|!=|>=|>|<=|<|\()(.*)$', expression) - if matched: - token = matched.group(1) - if token == '(': - # Parenthesized operand - expression = matched.group(2) - (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) - if end < 0: - return # Unmatched parenthesis - lhs += '(' + expression[0:end] - expression = expression[end:] - elif token in ('&&', '||'): - # Logical and/or operators. This means the expression - # contains more than one term, for example: - # CHECK(42 < a && a < b); - # - # These are not replaceable with CHECK_LE, so bail out early. - return - elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): - # Non-relational operator - lhs += token - expression = matched.group(2) - else: - # Relational operator - operator = token - rhs = matched.group(2) - break - else: - # Unparenthesized operand. Instead of appending to lhs one character - # at a time, we do another regular expression match to consume several - # characters at once if possible. Trivial benchmark shows that this - # is more efficient when the operands are longer than a single - # character, which is generally the case. - matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) - if not matched: - matched = Match(r'^(\s*\S)(.*)$', expression) - if not matched: - break - lhs += matched.group(1) - expression = matched.group(2) - - # Only apply checks if we got all parts of the boolean expression - if not (lhs and operator and rhs): - return - - # Check that rhs do not contain logical operators. We already know - # that lhs is fine since the loop above parses out && and ||. - if rhs.find('&&') > -1 or rhs.find('||') > -1: - return - - # At least one of the operands must be a constant literal. This is - # to avoid suggesting replacements for unprintable things like - # CHECK(variable != iterator) - # - # The following pattern matches decimal, hex integers, strings, and - # characters (in that order). - lhs = lhs.strip() - rhs = rhs.strip() - match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' - if Match(match_constant, lhs) or Match(match_constant, rhs): - # Note: since we know both lhs and rhs, we can provide a more - # descriptive error message like: - # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) - # Instead of: - # Consider using CHECK_EQ instead of CHECK(a == b) - # - # We are still keeping the less descriptive message because if lhs - # or rhs gets long, the error message might become unreadable. - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[check_macro][operator], - check_macro, operator)) - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - 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] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -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, str): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - 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. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw_lines = clean_lines.lines_without_raw_strings - line = raw_lines[linenum] - prev = raw_lines[linenum - 1] if linenum > 0 else '' - - 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; - scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' - classinfo = nesting_state.InnermostClass() - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - # There are certain situations we allow one space, notably for - # section labels, and also lines containing multi-line raw strings. - # We also don't check for lines that look like continuation lines - # (of lines ending in double quotes, commas, equals, or angle brackets) - # because the rules for how to indent those are non-trivial. - if (not Search(r'[",=><] *$', prev) and - (initial_spaces == 1 or initial_spaces == 3) and - not Match(scope_or_label_pattern, cleansed_line) and - not (clean_lines.raw_lines[linenum] != line and - Match(r'^\s*""', line))): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - - # 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. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^\s*//\s*[^\s]*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > _line_length: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= %i characters long' % _line_length) - - 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', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckTrailingSemicolon(filename, clean_lines, linenum, error) - CheckEmptyBlockBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckOperatorSpacing(filename, clean_lines, linenum, error) - CheckParenthesisSpacing(filename, clean_lines, linenum, error) - CheckCommaSpacing(filename, clean_lines, linenum, error) - CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_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 _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_cpp_h = 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" - # Only do this check if the included header follows google naming - # conventions. If not, assume that it's a 3rd party API that - # requires special include conventions. - # - # We also make an exception for Lua headers, which follow google - # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - 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) == '<') - duplicate_line = include_state.FindHeader(include) - if duplicate_line >= 0: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, duplicate_line)) - elif (include.endswith('.cc') and - os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): - error(filename, linenum, 'build/include', 4, - 'Do not include .cc files from other packages') - elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): - include_state.include_list[-1].append((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())) - canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) - if not include_state.IsInAlphabeticalOrder( - clean_lines, linenum, canonical_include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - include_state.SetLastHeader(canonical_include) - - - -def _GetTextInside(text, start_pattern): - r"""Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(unknown): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.values()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -# Patterns for matching call-by-reference parameters. -# -# Supports nested templates up to 2 levels deep using this messy pattern: -# < (?: < (?: < [^<>]* -# > -# | [^<>] )* -# > -# | [^<>] )* -# > -_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* -_RE_PATTERN_TYPE = ( - r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' - r'(?:\w|' - r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' - r'::)+') -# A call-by-reference parameter ends with '& identifier'. -_RE_PATTERN_REF_PARAM = re.compile( - r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' - r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') -# A call-by-const-reference parameter either ends with 'const& identifier' -# or looks like 'const type& identifier' when 'type' is atomic. -_RE_PATTERN_CONST_REF_PARAM = ( - r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + - r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') -# Stream types. -_RE_PATTERN_REF_STREAM_PARAM = ( - r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, - include_state, nesting_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. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - 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 - - # Reset include state across preprocessor directives. This is meant - # to silence warnings for conditional includes. - match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) - if match: - include_state.ResetSection(match.group(1)) - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # Perform other checks now that we are sure that this is not an include line - CheckCasts(filename, clean_lines, linenum, error) - CheckGlobalStatic(filename, clean_lines, linenum, error) - CheckPrintf(filename, clean_lines, linenum, error) - - 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 declare or disable copy/assign - # (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)) - - # 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()) - # TODO(unknown): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # 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 because 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.") - - # 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 ' - 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckGlobalStatic(filename, clean_lines, linenum, error): - """Check for unsafe global or static objects. - - 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] - - # Match two lines at a time to support multiline declarations - if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): - line += clean_lines.elided[linenum + 1].strip() - - # 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, and - # also because globals can be destroyed when some threads are still running. - # TODO(unknown): Generalize this to also find static unique_ptr instances. - # TODO(unknown): File bugs for clang-tidy to find these. - match = Match( - r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' - r'([a-zA-Z0-9_:]+)\b(.*)', - line) - - # Remove false positives: - # - String pointers (as opposed to values). - # string *pointer - # const string *pointer - # string const *pointer - # string *const pointer - # - # - Functions and template specializations. - # string Function(... - # string Class::Method(... - # - # - Operators. These are matched separately because operator names - # cross non-word boundaries, and trying to match both operators - # and functions at the same time would decrease accuracy of - # matching identifiers. - # string Class::operator*() - if (match and - not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and - not Search(r'\boperator\W', line) and - not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): - if Search(r'\bconst\b', line): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string ' - 'instead: "%schar%s %s[]".' % - (match.group(1), match.group(2) or '', match.group(3))) - else: - error(filename, linenum, 'runtime/string', 4, - 'Static/global string variables are not permitted.') - - if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or - Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - -def CheckPrintf(filename, clean_lines, linenum, error): - """Check for printf related issues. - - 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] - - # 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\s*\(', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\s*\(', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - -def IsDerivedFunction(clean_lines, linenum): - """Check if current line contains an inherited function. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains a function with "override" - virt-specifier. - """ - # Scan back a few lines for start of current function - for i in range(linenum, max(-1, linenum - 10), -1): - match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) - if match: - # Look for "override" after the matching closing parenthesis - line, _, closing_paren = CloseExpression( - clean_lines, i, len(match.group(1))) - return (closing_paren >= 0 and - Search(r'\boverride\b', line[closing_paren:])) - return False - - -def IsOutOfLineMethodDefinition(clean_lines, linenum): - """Check if current line contains an out-of-line method definition. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains an out-of-line method definition. - """ - # Scan back a few lines for start of current function - for i in range(linenum, max(-1, linenum - 10), -1): - if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): - return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None - return False - - -def IsInitializerList(clean_lines, linenum): - """Check if current line is inside constructor initializer list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line appears to be inside constructor initializer - list, False otherwise. - """ - for i in range(linenum, 1, -1): - line = clean_lines.elided[i] - if i == linenum: - remove_function_body = Match(r'^(.*)\{\s*$', line) - if remove_function_body: - line = remove_function_body.group(1) - - if Search(r'\s:\s*\w+[({]', line): - # A lone colon tend to indicate the start of a constructor - # initializer list. It could also be a ternary operator, which - # also tend to appear in constructor initializer lists as - # opposed to parameter lists. - return True - if Search(r'\}\s*,\s*$', line): - # A closing brace followed by a comma is probably the end of a - # brace-initialized member in constructor initializer list. - return True - if Search(r'[{};]\s*$', line): - # Found one of the following: - # - A closing brace or semicolon, probably the end of the previous - # function. - # - An opening brace, probably the start of current class or namespace. - # - # Current line is probably not inside an initializer list since - # we saw one of those things without seeing the starting colon. - return False - - # Got to the beginning of the file without seeing the start of - # constructor initializer list. - return False - - -def CheckForNonConstReference(filename, clean_lines, linenum, - nesting_state, error): - """Check for non-const references. - - Separate from CheckLanguage since it scans backwards from current - line, instead of scanning forward. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # Do nothing if there is no '&' on current line. - line = clean_lines.elided[linenum] - if '&' not in line: - return - - # If a function is inherited, current function doesn't have much of - # a choice, so any non-const references should not be blamed on - # derived function. - if IsDerivedFunction(clean_lines, linenum): - return - - # Don't warn on out-of-line method definitions, as we would warn on the - # in-line declaration, if it isn't marked with 'override'. - if IsOutOfLineMethodDefinition(clean_lines, linenum): - return - - # Long type names may be broken across multiple lines, usually in one - # of these forms: - # LongType - # ::LongTypeContinued &identifier - # LongType:: - # LongTypeContinued &identifier - # LongType< - # ...>::LongTypeContinued &identifier - # - # If we detected a type split across two lines, join the previous - # line to current line so that we can match const references - # accordingly. - # - # Note that this only scans back one line, since scanning back - # arbitrary number of lines would be expensive. If you have a type - # that spans more than 2 lines, please use a typedef. - if linenum > 1: - previous = None - if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): - # previous_line\n + ::current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', - clean_lines.elided[linenum - 1]) - elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): - # previous_line::\n + current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', - clean_lines.elided[linenum - 1]) - if previous: - line = previous.group(1) + line.lstrip() - else: - # Check for templated parameter that is split across multiple lines - endpos = line.rfind('>') - if endpos > -1: - (_, startline, startpos) = ReverseCloseExpression( - clean_lines, linenum, endpos) - if startpos > -1 and startline < linenum: - # Found the matching < on an earlier line, collect all - # pieces up to current line. - line = '' - for i in range(startline, linenum + 1): - line += clean_lines.elided[i].strip() - - # Check for non-const references in function parameters. A single '&' may - # found in the following places: - # inside expression: binary & for bitwise AND - # inside expression: unary & for taking the address of something - # inside declarators: reference parameter - # We will exclude the first two cases by checking that we are not inside a - # function body, including one that was just introduced by a trailing '{'. - # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. - if (nesting_state.previous_stack_top and - not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or - isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): - # Not at toplevel, not within a class, and not within a namespace - return - - # Avoid initializer lists. We only need to scan back from the - # current line for something that starts with ':'. - # - # We don't need to check the current line, since the '&' would - # appear inside the second set of parentheses on the current line as - # opposed to the first set. - if linenum > 0: - for i in range(linenum - 1, max(0, linenum - 10), -1): - previous_line = clean_lines.elided[i] - if not Search(r'[),]\s*$', previous_line): - break - if Match(r'^\s*:\s+\S', previous_line): - return - - # Avoid preprocessors - if Search(r'\\\s*$', line): - return - - # Avoid constructor initializer lists - if IsInitializerList(clean_lines, linenum): - return - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". Do not check - # those function parameters. - # - # We also accept & in static_assert, which looks like a function but - # it's actually a declaration expression. - whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' - r'operator\s*[<>][<>]|' - r'static_assert|COMPILE_ASSERT' - r')\s*\(') - if Search(whitelisted_functions, line): - return - elif not Search(r'\S+\([^)]*$', line): - # Don't see a whitelisted function on this line. Actually we - # didn't see any function name on this line, so this is likely a - # multi-line parameter list. Try a bit harder to catch this case. - for i in range(2): - if (linenum > i and - Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): - return - - decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body - for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): - if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and - not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer: ' + - ReplaceAll(' *<', '<', parameter)) - - -def CheckCasts(filename, clean_lines, linenum, error): - """Various cast related checks. - - 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] - - # 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+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' - r'(int|float|double|bool|char|int32|uint32|int64|uint64)' - r'(\([^)].*)', line) - expecting_function = ExpectingFunctionArgs(clean_lines, linenum) - if match and not expecting_function: - matched_type = match.group(2) - - # matched_new_or_template is used to silence two false positives: - # - New operators - # - Template arguments with function types - # - # For template arguments, we match on types immediately following - # an opening bracket without any spaces. This is a fast way to - # silence the common case where the function type is the first - # template argument. False negative with less-than comparison is - # avoided because those operators are usually followed by a space. - # - # function // bracket + no space = false positive - # value < double(42) // bracket + space = true positive - matched_new_or_template = match.group(1) - - # Avoid arrays by looking for brackets that come after the closing - # parenthesis. - if Match(r'\([^()]+\)\s*\[', match.group(3)): - return - - # Other things to ignore: - # - Function pointers - # - Casts to pointer types - # - Placement new - # - Alias declarations - matched_funcptr = match.group(3) - if (matched_new_or_template is None and - not (matched_funcptr and - (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', - matched_funcptr) or - matched_funcptr.startswith('(*)'))) and - not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and - not Search(r'new\(\S+\)\s*' + matched_type, line)): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - matched_type) - - if not expecting_function: - CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', - r'\(((unsigned )?(char|(short |long )?int|long)|float|' - 'double|bool|u?int(8_t|16_t|32_t|64_t))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', - r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', - r'\(((const )?\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. - # - # Some non-identifier character is required before the '&' for the - # expression to be recognized as a cast. These are casts: - # expression = &static_cast(temporary()); - # function(&(int*)(temporary())); - # - # This is not a cast: - # reference_type&(int* function_param); - match = Search( - r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' - r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) - if match: - # Try a better error message when the & is bound to something - # dereferenced by the casted pointer, as opposed to the casted - # pointer itself. - parenthesis_error = False - match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) - if match: - _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) - if x1 >= 0 and clean_lines.elided[y1][x1] == '(': - _, y2, x2 = CloseExpression(clean_lines, y1, x1) - if x2 >= 0: - extended_line = clean_lines.elided[y2][x2:] - if y2 < clean_lines.NumLines() - 1: - extended_line += clean_lines.elided[y2 + 1] - if Match(r'\s*(?:->|\[)', extended_line): - parenthesis_error = True - - if parenthesis_error: - error(filename, linenum, 'readability/casting', 4, - ('Are you taking an address of something dereferenced ' - 'from a cast? Wrapping the dereferenced expression in ' - 'parentheses will make the binding more obvious')) - else: - 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')) - - -def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): - """Checks for a C-style cast by looking for the pattern. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - line = clean_lines.elided[linenum] - match = Search(pattern, line) - if not match: - return False - - # Exclude lines with keywords that tend to look like casts - context = line[0:match.start(1) - 1] - if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): - return False - - # Try expanding current context to see if we one level of - # parentheses inside a macro. - if linenum > 0: - for i in range(linenum - 1, max(0, linenum - 5), -1): - context = clean_lines.elided[i] + context - if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): - return False - - # operator++(int) and operator--(int) - if context.endswith(' operator++') or context.endswith(' operator--'): - return False - - # A single unnamed argument for a function tends to look like old style cast. - # If we see those, don't issue warnings for deprecated casts. - remainder = line[match.end(0):] - if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', - remainder): - return False - - # 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))) - - return True - - -def ExpectingFunctionArgs(clean_lines, linenum): - """Checks whether where function type arguments are expected. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - - Returns: - True if the line at 'linenum' is inside something that expects arguments - of function types. - """ - line = clean_lines.elided[linenum] - return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - (linenum >= 2 and - (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', - clean_lines.elided[linenum - 1]) or - Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', - clean_lines.elided[linenum - 2]) or - Search(r'\bstd::m?function\s*\<\s*$', - clean_lines.elided[linenum - 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', 'make_shared', 'make_unique', 'shared_ptr', - 'unique_ptr', 'weak_ptr')), - ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), - ('', ('stack',)), - ('', ('char_traits', 'basic_string',)), - ('', ('tuple',)), - ('', ('unordered_map', 'unordered_multimap')), - ('', ('unordered_set', 'unordered_multiset')), - ('', ('pair',)), - ('', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('', ('hash_map', 'hash_multimap',)), - ('', ('hash_set', 'hash_multiset',)), - ('', ('slist',)), - ) - -_HEADERS_MAYBE_TEMPLATES = ( - ('', ('copy', 'max', 'min', 'min_element', 'sort', - 'transform', - )), - ('', ('forward', 'make_pair', 'move', 'swap')), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_headers_maybe_templates = [] -for _header, _templates in _HEADERS_MAYBE_TEMPLATES: - for _template in _templates: - # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_headers_maybe_templates.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - _header)) - -# Other scripts may reach in and modify this pattern. -_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. - """ - - fileinfo = FileInfo(filename_cc) - if not fileinfo.IsSource(): - return (False, '') - filename_cc = filename_cc[:-len(fileinfo.Extension())] - matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) - if matched_test_suffix: - filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] - 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_dict, io=codecs): - """Fill up the include_dict with new includes found from the file. - - Args: - filename: the name of the header to read. - include_dict: a dictionary 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 successfully 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) - include_dict.setdefault(include, 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 range(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') - - for pattern, template, header in _re_pattern_headers_maybe_templates: - 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: - matched = pattern.search(line) - if matched: - # Don't warn about IWYU in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[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 flatten the include_state include_list and copy it into a dictionary. - include_dict = dict([item for sublist in include_state.include_list - for item in sublist]) - - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_dict is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = list(include_dict.keys()) - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_dict, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_dict: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - 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] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def CheckRedundantVirtual(filename, clean_lines, linenum, error): - """Check if line contains a redundant "virtual" function-specifier. - - 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. - """ - # Look for "virtual" on current line. - line = clean_lines.elided[linenum] - virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) - if not virtual: return - - # Ignore "virtual" keywords that are near access-specifiers. These - # are only used in class base-specifier and do not apply to member - # functions. - if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or - Match(r'^\s+(public|protected|private)\b', virtual.group(3))): - return - - # Ignore the "virtual" keyword from virtual base classes. Usually - # there is a column on the same line in these cases (virtual base - # classes are rare in google3 because multiple inheritance is rare). - if Match(r'^.*[^:]:[^:].*$', line): return - - # Look for the next opening parenthesis. This is the start of the - # parameter list (possibly on the next line shortly after virtual). - # TODO(unknown): doesn't work if there are virtual functions with - # decltype() or other things that use parentheses, but csearch suggests - # that this is rare. - end_col = -1 - end_line = -1 - start_col = len(virtual.group(2)) - for start_line in range(linenum, min(linenum + 3, clean_lines.NumLines())): - line = clean_lines.elided[start_line][start_col:] - parameter_list = Match(r'^([^(]*)\(', line) - if parameter_list: - # Match parentheses to find the end of the parameter list - (_, end_line, end_col) = CloseExpression( - clean_lines, start_line, start_col + len(parameter_list.group(1))) - break - start_col = 0 - - if end_col < 0: - return # Couldn't find end of parameter list, give up - - # Look for "override" or "final" after the parameter list - # (possibly on the next few lines). - for i in range(end_line, min(end_line + 3, clean_lines.NumLines())): - line = clean_lines.elided[i][end_col:] - match = Search(r'\b(override|final)\b', line) - if match: - error(filename, linenum, 'readability/inheritance', 4, - ('"virtual" is redundant since function is ' - 'already declared as "%s"' % match.group(1))) - - # Set end_col to check whole lines after we are done with the - # first line. - end_col = 0 - if Search(r'[^\w]\s*$', line): - break - - -def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): - """Check if line contains a redundant "override" or "final" virt-specifier. - - 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. - """ - # Look for closing parenthesis nearby. We need one to confirm where - # the declarator ends and where the virt-specifier starts to avoid - # false positives. - line = clean_lines.elided[linenum] - declarator_end = line.rfind(')') - if declarator_end >= 0: - fragment = line[declarator_end:] - else: - if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: - fragment = line - else: - return - - # Check that at most one of "override" or "final" is present, not both - if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): - error(filename, linenum, 'readability/inheritance', 4, - ('"override" is redundant since function is ' - 'already declared as "final"')) - - - - -# Returns true if we are at a new block, and it is directly -# inside of a namespace. -def IsBlockInNameSpace(nesting_state, is_forward_declaration): - """Checks that the new block is directly in a namespace. - - Args: - nesting_state: The _NestingState object that contains info about our state. - is_forward_declaration: If the class is a forward declared class. - Returns: - Whether or not the new block is directly in a namespace. - """ - if is_forward_declaration: - if len(nesting_state.stack) >= 1 and ( - isinstance(nesting_state.stack[-1], _NamespaceInfo)): - return True - else: - return False - - return (len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.stack[-2], _NamespaceInfo)) - - -def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - raw_lines_no_comments, linenum): - """This method determines if we should apply our namespace indentation check. - - Args: - nesting_state: The current nesting state. - is_namespace_indent_item: If we just put a new class on the stack, True. - If the top of the stack is not a class, or we did not recently - add the class, False. - raw_lines_no_comments: The lines without the comments. - linenum: The current line number we are processing. - - Returns: - True if we should apply our namespace indentation check. Currently, it - only works for classes and namespaces inside of a namespace. - """ - - is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, - linenum) - - if not (is_namespace_indent_item or is_forward_declaration): - return False - - # If we are in a macro, we do not want to check the namespace indentation. - if IsMacroDefinition(raw_lines_no_comments, linenum): - return False - - return IsBlockInNameSpace(nesting_state, is_forward_declaration) - - -# Call this method if the line is directly inside of a namespace. -# If the line above is blank (excluding comments) or the start of -# an inner namespace, it cannot be indented. -def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, - error): - line = raw_lines_no_comments[linenum] - if Match(r'^\s+', line): - error(filename, linenum, 'runtime/indentation_namespace', 4, - 'Do not indent within a namespace') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """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. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error) - if nesting_state.InAsmBlock(): return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - nesting_state, error) - CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckVlogArguments(filename, clean_lines, line, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - CheckRedundantVirtual(filename, clean_lines, line, error) - CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def FlagCxx11Features(filename, clean_lines, linenum, error): - """Flag those c++11 features that we only allow in certain places. - - 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] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++ TR1 headers. - if include and include.group(1).startswith('tr1/'): - error(filename, linenum, 'build/c++tr1', 5, - ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) - - # Flag unapproved C++11 headers. - if include and include.group(1) in ('cfenv', - 'condition_variable', - 'fenv.h', - 'future', - 'mutex', - 'thread', - 'chrono', - 'ratio', - 'regex', - 'system_error', - ): - error(filename, linenum, 'build/c++11', 5, - ('<%s> is an unapproved C++11 header.') % include.group(1)) - - # The only place where we need to worry about C++11 keywords and library - # features in preprocessor directives is in macro definitions. - if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return - - # These are classes and free functions. The classes are always - # mentioned as std::*, but we only catch the free functions if - # they're not found by ADL. They're alphabetical by header. - for top_name in ( - # type_traits - 'alignment_of', - 'aligned_union', - ): - if Search(r'\bstd::%s\b' % top_name, line): - error(filename, linenum, 'build/c++11', 5, - ('std::%s is an unapproved C++11 class or function. Send c-style ' - 'an example of where it would make your code more readable, and ' - 'they may let you use it.') % top_name) - - -def FlagCxx14Features(filename, clean_lines, linenum, error): - """Flag those C++14 features that we restrict. - - 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] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++14 headers. - if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): - error(filename, linenum, 'build/c++14', 5, - ('<%s> is an unapproved C++14 header.') % include.group(1)) - - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """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 terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - 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() - nesting_state = NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - ProcessGlobalSuppresions(lines) - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - - if file_extension == 'h': - CheckForHeaderGuard(filename, clean_lines, error) - - for line in range(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - FlagCxx11Features(filename, clean_lines, line, error) - nesting_state.CheckCompletedBlocks(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # Check that the .cc file has included its header if it exists. - if _IsSourceExtension(file_extension): - CheckHeaderFileIncluded(filename, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForBadCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessConfigOverrides(filename): - """ Loads the configuration files and processes the config overrides. - - Args: - filename: The name of the file being processed by the linter. - - Returns: - False if the current |filename| should not be processed further. - """ - - abs_filename = os.path.abspath(filename) - cfg_filters = [] - keep_looking = True - while keep_looking: - abs_path, base_name = os.path.split(abs_filename) - if not base_name: - break # Reached the root directory. - - cfg_file = os.path.join(abs_path, "CPPLINT.cfg") - abs_filename = abs_path - if not os.path.isfile(cfg_file): - continue - - try: - with open(cfg_file) as file_handle: - for line in file_handle: - line, _, _ = line.partition('#') # Remove comments. - if not line.strip(): - continue - - name, _, val = line.partition('=') - name = name.strip() - val = val.strip() - if name == 'set noparent': - keep_looking = False - elif name == 'filter': - cfg_filters.append(val) - elif name == 'exclude_files': - # When matching exclude_files pattern, use the base_name of - # the current file name or the directory name we are processing. - # For example, if we are checking for lint errors in /foo/bar/baz.cc - # and we found the .cfg file at /foo/CPPLINT.cfg, then the config - # file's "exclude_files" filter is meant to be checked against "bar" - # and not "baz" nor "bar/baz.cc". - if base_name: - pattern = re.compile(val) - if pattern.match(base_name): - sys.stderr.write('Ignoring "%s": file excluded by "%s". ' - 'File path component "%s" matches ' - 'pattern "%s"\n' % - (filename, cfg_file, base_name, val)) - return False - elif name == 'linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - sys.stderr.write('Line length must be numeric.') - elif name == 'root': - global _root - _root = val - else: - sys.stderr.write( - 'Invalid configuration option (%s) in file %s\n' % - (name, cfg_file)) - - except IOError: - sys.stderr.write( - "Skipping config file '%s': Can't open for reading\n" % cfg_file) - keep_looking = False - - # Apply all the accumulated filters in reverse order (top-level directory - # config options having the least priority). - for filter in reversed(cfg_filters): - _AddFilters(filter) - - return True - - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """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. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) - _BackupFilters() - - if not ProcessConfigOverrides(filename): - _RestoreFilters() - return - - lf_lines = [] - crlf_lines = [] - 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 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') - - # Remove trailing '\r'. - # The -1 accounts for the extra trailing blank line we get from split() - for linenum in range(len(lines) - 1): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - crlf_lines.append(linenum + 1) - else: - lf_lines.append(linenum + 1) - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - _RestoreFilters() - 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 not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - - # If end-of-line sequences are a mix of LF and CR-LF, issue - # warnings on the lines with CR. - # - # Don't issue any warnings if all lines are uniformly LF or CR-LF, - # since critique can handle these just fine, and the style guide - # doesn't dictate a particular end of line sequence. - # - # We can't depend on os.linesep to determine what the desired - # end-of-line sequence should be, since that will return the - # server-side end-of-line sequence. - if lf_lines and crlf_lines: - # Warn on every line with CR. An alternative approach might be to - # check whether the file is mostly CRLF or just LF, and warn on the - # minority, we bias toward LF here since most tools prefer LF. - for linenum in crlf_lines: - Error(filename, linenum, 'whitespace/newline', 1, - 'Unexpected \\r (^M) found; better to use only \\n') - - sys.stderr.write('Done processing %s\n' % filename) - _RestoreFilters() - - -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=', - 'root=', - 'linelength=', - 'extensions=']) - 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 val not in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - 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 - elif opt == '--root': - global _root - _root = val - elif opt == '--linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - PrintUsage('Line length must be digits.') - elif opt == '--extensions': - global _valid_extensions - try: - _valid_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma seperated list.') - - 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:]) - - _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/styleguide/eclipse-cpp-google-style.xml b/styleguide/eclipse-cpp-google-style.xml deleted file mode 100644 index aa05a819ab..0000000000 --- a/styleguide/eclipse-cpp-google-style.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/styleguide/eclipse-java-google-style.xml b/styleguide/eclipse-java-google-style.xml deleted file mode 100644 index b3177d30f3..0000000000 --- a/styleguide/eclipse-java-google-style.xml +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/styleguide/format.py b/styleguide/format.py index 50f814ec02..5736ea81de 100755 --- a/styleguide/format.py +++ b/styleguide/format.py @@ -1,123 +1,28 @@ #!/usr/bin/env python3 -import argparse -import multiprocessing +"""This script invokes format.py in the wpilibsuite/styleguide repository. + +Set the WPI_FORMAT environment variable to its location on disk before use. For +example: + +WPI_FORMAT="$HOME/styleguide" ./format.py +""" + import os -import queue import subprocess import sys -from threading import Lock -from threading import Thread - -from clangformat import ClangFormat -from licenseupdate import LicenseUpdate -from newline import Newline -from whitespace import Whitespace -from lint import Lint -from task import Task - -# Check that the current directory is part of a Git repository -def inGitRepo(directory): - ret = subprocess.run(["git", "rev-parse"], stderr = subprocess.DEVNULL) - return ret.returncode == 0 - -def threadFunc(workQueue, isVerbose, printLock): - # Lint is run last since previous tasks can affect its output - tasks = [ClangFormat(), LicenseUpdate(), Newline(), Whitespace(), Lint()] - - try: - while True: - name = workQueue.get_nowait() - - if isVerbose: - printLock.acquire() - print("Processing", name,) - for task in tasks: - if task.fileMatchesExtension(name): - print(" with " + type(task).__name__) - printLock.release() - - for task in tasks: - if task.fileMatchesExtension(name): - task.run(name) - except queue.Empty: - pass def main(): - if not inGitRepo("."): - print("Error: not invoked within a Git repository", file = sys.stderr) + path = os.environ.get("WPI_FORMAT") + if path == None: + print("Error: WPI_FORMAT environment variable not set") sys.exit(1) - # Handle running in either the root or styleguide directories - configPath = "" - if os.getcwd().rpartition(os.sep)[2] == "styleguide": - configPath = ".." - else: - configPath = "." - - # Delete temporary files from previous incomplete run - files = [os.path.join(dp, f) for dp, dn, fn in - os.walk(os.path.expanduser(configPath)) for f in fn] - for f in files: - if f.endswith(".tmp"): - os.remove(f) - - # Recursively create list of files in given directory - files = [os.path.join(dp, f) for dp, dn, fn in - os.walk(os.path.expanduser(configPath)) for f in fn] - - if not files: - print("Error: no files found to format", file = sys.stderr) - sys.exit(1) - - # Don't check for changes in or run tasks on modifiable files - files = [name for name in files if not Task.isModifiableFile(name)] - - # Create list of all changed files - changedFileList = [] - proc = subprocess.Popen(["git", "diff", "--name-only", "master"], - bufsize = 1, stdout = subprocess.PIPE) - for line in proc.stdout: - changedFileList.append(configPath + os.sep + - line.strip().decode("ascii")) - - # Emit warning if a generated file was editted - for name in files: - if Task.isGeneratedFile(name) and name in changedFileList: - print("Warning: generated file '" + name + "' modified") - - # Don't format generated files - files = [name for name in files if not Task.isGeneratedFile(name)] - - # Parse command-line arguments - parser = argparse.ArgumentParser(description = "Runs all formatting tasks on the code base. This should be invoked from either the styleguide directory or the root directory of the project.") - parser.add_argument("-v", dest = "verbose", action = "store_true", - help = "enable output verbosity") - parser.add_argument("-j", dest = "jobs", type = int, - default = multiprocessing.cpu_count(), - help = "number of jobs to run (default is number of cores)") - args = parser.parse_args() - jobs = args.jobs - isVerbose = args.verbose - - # Add files to work queue - workQueue = queue.Queue() - for file in files: - workQueue.put(file) - - threads = [] - printLock = Lock() - - # Start worker threads - for i in range(0, jobs): - thread = Thread(target = threadFunc, - args = (workQueue, isVerbose, printLock)) - thread.start() - threads.append(thread) - - # Wait for worker threads to finish - for i in range(0, jobs): - threads[i].join() + # Run main format.py script + args = ["python", path + "/format.py"] + args.extend(sys.argv[1:]) + proc = subprocess.Popen(args) + proc.wait() if __name__ == "__main__": main() diff --git a/styleguide/include/link.png b/styleguide/include/link.png deleted file mode 100644 index 75d5c7ba8dcc22b100b18b8c800da9ed99afbf7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{1|(OCFP#RYT0C7GLo80eoovW;K!L}#nDP3f zLaS;${>#Z0UFCva2M?aC%WS)H?QB_5g+S7$o~{tDg%>YO@zZ2dVrDwAKuzez0i`Ul zbE@l3-bt1V61%3rbUmT<%Dy%3?@ZTjdAE&Ol|^ce?3NY5cci0UGX8nXab09-b9Pp3 o=^vw)Kdk>g`TD$n)}_KZpTAr6giAPD0^P&l>FVdQ&MBb@0Ar|2 lowestLevel) continue; - - // If level is a masterLevel, make it a TOC parent category - if ((level == masterLevel) && (!hasClass(heading, 'ignoreLink'))) { - toc_current_row = AddTOCMaster(tbody_element, heading); - ignoreChildren = false; - } - - if ((level == masterLevel) && (hasClass(heading, 'ignoreLink'))) { - ignoreChildren = true; - } - - if ((level != masterLevel) && (!ignoreChildren)) { - AddTOCElements(toc_current_row, heading); - } - - // Advance the header counter - h++; - } -} - -// Adds a master Table of Content heading -function AddTOCMaster(tocTable, heading) { - - // Add the table row scaffolding - var toc_tr = document.createElement('tr'); - tocTable.appendChild(toc_tr); - toc_tr.setAttribute('valign', 'top'); - var toc_tr_td = document.createElement('td'); - toc_tr.appendChild(toc_tr_td); - var toc_category = document.createElement('div'); - toc_tr_td.appendChild(toc_category); - toc_category.className = 'toc_category'; - - // Create the link to this header - var link = document.createElement('a'); - link.href = '#' + heading.id; // Create the anchor link - link.textContent = heading.textContent; // Link text is same as heading - toc_category.appendChild(link); - - // Add the container table cell for its children - var toc_td = document.createElement('td'); - toc_tr.appendChild(toc_td); - var toc_td_div = document.createElement('div'); - toc_td_div.className = 'toc_stylepoint'; - toc_td.appendChild(toc_td_div); - - return (toc_td_div); -} - -// Adds Table of Contents element to a master heading as children -function AddTOCElements(toc_div, heading) { - - if (heading.offsetParent === null) { - // The element is currently hidden, so don't create a TOC entry - } else { - // Create the list item element - var toc_list_element = document.createElement('li'); - toc_list_element.className = 'toc_entry'; - toc_div.appendChild(toc_list_element); - - // Create the link to this header - var link = document.createElement('a'); - link.href = '#' + heading.id; // Create the anchor link - link.textContent = heading.textContent; // Link text is same as heading - toc_list_element.appendChild(link); - } -} - -function CreateVerticalTOC(headings, masterLevel, lowestLevel, tbody_element) { - - // Create the Column scaffolding - var toc_tr = document.createElement('tr'); - tbody_element.appendChild(toc_tr); - var toc_tr_td = document.createElement('td'); - toc_tr_td.className = 'two_columns'; - toc_tr.appendChild(toc_tr_td); - - - // Initialize the header counter and the current row - var h = 0; - var toc_current_col = null; - var ignoreChildren = false; - - while (h < headings.length) { - // Get current heading - var heading = headings[h]; - - // Get the current heading level - var level = parseInt(heading.tagName.charAt(1)); - - if (isNaN(level) || level < 1 || level > lowestLevel) continue; - - // If level is a masterLevel, make it a TOC parent category - if ((level == masterLevel) && (!hasClass(heading, 'ignoreLink'))) { - if (heading.offsetParent === null) { - // The element is currently hidden, so don't create a TOC entry - } else { - var td_dl = document.createElement('dl'); - toc_tr_td.appendChild(td_dl); - var td_dt = document.createElement('dt'); - td_dl.appendChild(td_dt); - toc_current_col = td_dl; - - // Create the link to this header - var link = document.createElement('a'); - link.href = '#' + heading.id; // Create the anchor link - link.textContent = heading.textContent; // Link text is same as heading - td_dt.appendChild(link); - ignoreChildren = false; - } - } - - // If level is a masterLevel but it's specified to ignore links, skip it - // and its children. - if ((level == masterLevel) && (hasClass(heading, 'ignoreLink'))) { - ignoreChildren = true; - } - - if ((level != masterLevel) && (!ignoreChildren)) { - if (heading.offsetParent === null) { - // The element is currently hidden, so don't create a TOC entry - } else { - var td_dd = document.createElement('dd'); - toc_current_col.appendChild(td_dd); - // Create the link to this header - var link = document.createElement('a'); - link.href = '#' + heading.id; // Create the anchor link - link.textContent = heading.textContent; // Link text is same as heading - td_dd.appendChild(link); - } - } - - // Advance the header counter - h++; - } -} - -/* - * Utility function for finding elements with a given - * class. - */ -function hasClass(element, cls) { - return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; -} - -/* - * Linkify all h2 through h4 headers, except for those marked - * "ignoreLink" - */ - -// Add the link image to the element. -function LinkifyHeader(header, fileName, sizePixels) { - var link = document.createElement('a'); - link.href = '#' + header.id; - link.alt = 'link to ' + header.id; - link.innerHTML = - ''; - header.appendChild(link); -} - -// Find all elements of the given tag and linkify if -// they don't have 'ignoreLink' in their class. -function LinkifyHeadersForTag(tagName) { - var headers = document.getElementsByTagName(tagName); - var header; - for (var j = 0; j != headers.length; j++) { - header = headers[j]; - if (!hasClass(header, 'ignoreLink') && ('id' in header)) { - if (header.id != '') { - LinkifyHeader(header, 'link.png', 21); - header.style.left = '-46px'; - header.style.position = 'relative'; - } - } - } -} - -// Linkify all h2, h3, and h4s. h1s are titles. -function LinkifyHeaders() { - LinkifyHeadersForTag('h2'); - LinkifyHeadersForTag('h3'); - LinkifyHeadersForTag('h4'); -} - -/* - * Initialize the style guide by showing all internal - * elements and then linkifying the headers. - */ - -function initStyleGuide() { - LinkifyHeaders(); - CreateTOC('tocDiv'); -} diff --git a/styleguide/intellij-java-google-style.xml b/styleguide/intellij-java-google-style.xml deleted file mode 100644 index fb1adddde0..0000000000 --- a/styleguide/intellij-java-google-style.xml +++ /dev/null @@ -1,475 +0,0 @@ - - - - - diff --git a/styleguide/javaguide.css b/styleguide/javaguide.css deleted file mode 100644 index c42ba835a0..0000000000 --- a/styleguide/javaguide.css +++ /dev/null @@ -1,515 +0,0 @@ -table { - border-collapse: collapse; -} - -td, th { - border: 1px solid #ccc; - padding: 2px 12px; - font-size: 10pt; -} - -code, samp, var { - color: #060; -} - -pre { - font-size: 10pt; - display: block; - color: #060; - background-color: #e8fff6; - border-color: #f0fff0; - border-style: solid; - border-top-width: 1px; - border-bottom-width: 1px; - border-right-width: 1px; - border-left-width: 5px; - padding-left: 12px; - padding-right: 12px; - padding-top: 4px; - padding-bottom: 4px; -} - -pre.badcode { - color: #c00; - background-color: #ffe6d8; - border-color: #fff0f0; -} - -hr { - margin-top: 3.5em; - border-width: 1px; - color: #fff; -} - -html { - margin-top:2em; - margin-left:10%; - margin-right:10%; - padding:0; -} - -.bp-reset-element, -body, -h1, -h2, -h3, -h4, -h5, -h6, -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -menu, -nav, -section, -summary, -blockquote, -q, -th, -td, -caption, -table, -div, -span, -object, -iframe, -p, -pre, -a, -abbr, -acronym, -address, -code, -del, -dfn, -em, -img, -dl, -dt, -dd, -ol, -ul, -li, -fieldset, -form, -label, -legend, -caption, -tbody, -tfoot, -thead, -tr { - margin:0; - padding:0; - border:0; - font-weight:inherit; - font-style:inherit; - font-size:100%; - font-family:inherit; - vertical-align:baseline; -} - -body { - font-family:'Arial', sans-serif; - font-size:81.25%; - color:#222; - background-color:#fff; - line-height:1.67; - overflow: auto; -} - -.change { - text-align: right; - margin-bottom:1em; -} - -em { - font-style: italic -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight:bold; -} - -h1 { - margin-bottom:.50em; - text-align: center -} - -h2, -h3, -h4, -h5, -h6 { - margin-top:1.5em; - margin-bottom:.75em; -} - -h1 {font-size:200%;} -h2 {font-size:167%;} -h3 {font-size:133%;} -h4 {font-size:120%;} -h5 {font-size:110%;} - -p { - margin:0 0 1.5em; -} - -a[href=''] { - cursor:default; -} - -h1 img, -h2 img, -h3 img, -h4 img, -h5 img, -h6 img { - margin:0; -} - -a img { - border:none; -} - -pre { - margin:1.5em 0; - white-space:pre; -} - -pre, -code, -kbd, -tt { - font:1em 'Droid Sans Mono', monospace; - line-height:1.5; -} - -dl { - margin:0 0 1.5em 0; -} - -dl dt { - font-weight:bold; -} - -dd { - margin-left:1.5em; -} - -dd.toc3 { - margin-left:3em; -} - -hr { - height:0; - border:0; - border-top:1px solid #ccc; - background-color:#ccc; -} - -table { - border:1px solid #bbb; - border-spacing:0; - border-collapse:collapse; - margin:0 0 1.5em; - vertical-align:middle; - width:100%; -} - -table.unlined, -table.unlined th, -table.unlined tr, -table.unlined td { - border:0; -} - -th, -td, -caption { - float:none !important; - text-align:left; - font-weight:normal; - vertical-align:middle; - padding:4px; -} - -caption { - padding:0; -} - -td { - border:1px solid #bbb; - vertical-align:top; -} - -th { - border:0; - border-bottom:1px solid black; - font-weight:bold; - background:rgb(229, 236, 249); -} - -table th code { - background-color:inherit; - color:inherit; -} - -table tfoot th { - border:1px solid #bbb; -} - -tfoot { - font-style:italic; -} - -caption { - background:#eee; -} - -table[border='0'] { - border:none; -} - -table[border='0']>tbody>tr>td, -table[border='0']>tr>td { - border:none; -} - -tr.alt td, -td.alt { - background-color:#efefef; -} - -table.striped tr:nth-child(even) td, -table tr.even td { - background:#efefef; -} - -table.columns { - border:none; -} - -table.columns>tbody>tr>td, -table.columns>tr>td { - border:none; - padding:0 3em 0 0; -} - -table.columns>tbody>tr>td:last-child, -table.columns>tr>td:last-child { - border:none; - padding:0; -} - -ul, -ol { - margin:0 1.5em 1.5em 0; - padding-left:2em; -} - -li ul, -li ol { - margin:0; -} - -ul { - list-style-type:disc; -} - -ol { - list-style-type:decimal; -} - -ul { - list-style-type:disc; -} - -ul ul { - list-style-type:circle; -} - -ul ul ul { - list-style-type:square; -} - -ul.disc { - list-style-type:disc; -} - -ul.circle { - list-style-type:circle; -} - -ul.square { - list-style-type:square; -} - -ol { - list-style-type:decimal; -} - -ol ol { - list-style-type:lower-alpha; -} - -ol ol ol { - list-style-type:lower-roman; -} - -ol ul { - list-style-type:circle; -} - -ol.decimal { - list-style-type:decimal; -} - -ol.upper-alpha { - list-style-type:upper-alpha; -} - -ol.lower-alpha { - list-style-type:lower-alpha; -} - -ol.upper-roman { - list-style-type:upper-roman; -} - -ol.lower-roman { - list-style-type:lower-roman; -} - -ol.nolist, -ul.nolist { - padding-left:0; - list-style-image:none; - list-style-type:none; - margin-left:0; -} - -.center { - text-align:center; -} - -code, -kbd, -pre { - color:#009900; -} - -kbd { - font-weight: bold; -} - -table.striped code { - background-color:inherit; -} - -pre { - padding:6px 10px; - background-color:#FAFAFA; - border:1px solid #bbb; - overflow:auto; -} - -pre.prettyprint { - padding:6px 10px !important; - border:1px solid #bbb !important; -} - -code.bad, code.badcode { - color: magenta; -} -pre.bad, pre.badcode { - background-color:#ffe6d8; - border-top:1px inset #a03; - border-left:1px inset #a03; -} - -.tip { - background-color:#fffbd9; - padding:6px 8px 6px 10px; - border-left:6px solid #ffef70; -} - -.note { - background-color:#e5ecf9; - padding:6px 8px 6px 10px; - border-left:6px solid #36c; -} - -@media print { - - .str { - color:#060; - } - - .kwd { - color:#006; - font-weight:bold; - } - - .com { - color:#600; - font-style:italic; - } - - .typ { - color:#404; - font-weight:bold; - } - - .lit { - color:#044; - } - - .pun, - .opn, - .clo { - color:#440; - } - - .pln { - color:#000; - } - - .tag { - color:#006; - font-weight:bold; - } - - .atn { - color:#404; - } - - .atv { - color:#060; - } - - h1 { - font-style:italic; - } -} - -ol.linenums { - margin-top:0; - margin-bottom:0; -} - -code { - background-color:#FAFAFA; - padding: 0.25em 0.5em; - white-space: nowrap -} diff --git a/styleguide/javaguide.html b/styleguide/javaguide.html deleted file mode 100644 index da34b8516b..0000000000 --- a/styleguide/javaguide.html +++ /dev/null @@ -1,806 +0,0 @@ - - - - - - - WPILib Java Style - - -

WPILib Java Style (Based on the - Google Java Style Guide)

-
Last changed: June 19, 2015
- - - - -
-
-
-
-1 Introduction -
-
-1.1 Terminology notes -
-
-1.2 Guide notes -
-
-
-2 Source file basics -
-
-2.1 File name -
-
-2.2 File encoding: UTF-8 -
-
-2.3 Special characters -
-
-2.3.1 Whitespace characters -
-
-2.3.2 Special escape sequences -
-
-2.3.3 Non-ASCII characters -
-
-
-3 Source file structure -
-
-3.1 License or copyright information, if present -
-
-3.2 Package statement -
-
-3.3 Import statements -
-
-3.3.1 No wildcard imports -
-
-3.3.2 No line-wrapping -
-
-3.3.3 Ordering and spacing -
-
-3.4 Class declaration -
-
-3.4.1 Exactly one top-level class declaration -
-
-3.4.2 Class member ordering -
-
-
-
-
-
-4 Formatting -
-
-4.1 Braces -
-
-4.1.1 Braces are used where optional -
-
-4.1.2 Nonempty blocks: K & R style -
-
-4.1.3 Empty blocks: may be concise -
-
-4.2 Block indentation: +2 spaces -
-
-4.3 One statement per line -
-
-4.4 Column limit: 80 or 100 -
-
-4.5 Line-wrapping -
-
-4.5.1 Where to break -
-
-4.5.2 Indent continuation lines at least +4 spaces -
-
-4.6 Whitespace -
-
-4.6.1 Vertical Whitespace -
-
-4.6.2 Horizontal whitespace -
-
-4.6.3 Horizontal alignment: never required -
-
-4.7 Grouping parentheses: recommended -
-
-4.8 Specific constructs -
-
-4.8.1 Enum classes -
-
-4.8.2 Variable declarations -
-
-4.8.3 Arrays -
-
-4.8.4 Switch statements -
-
-4.8.5 Annotations -
-
-4.8.6 Comments -
-
-4.8.7 Modifiers -
-
-4.8.8 Numeric Literals -
-
-
-
-
-
-5 Naming -
-
-5.1 Rules common to all identifiers -
-
-5.2 Rules by identifier type -
-
-5.2.1 Package names -
-
-5.2.2 Class names -
-
-5.2.3 Method names -
-
-5.2.4 Constant names -
-
-5.2.5 Non-constant field names -
-
-5.2.6 Parameter names -
-
-5.2.7 Local variable names -
-
-5.2.8 Type variable names -
-
-5.3 Camel case: defined -
-
-
-6 Programming Practices -
-
-6.1 @Override: always used -
-
-6.2 Caught exceptions: not ignored -
-
-6.3 Static members: qualified using class -
-
-6.4 Finalizers: not used -
-
-
-7 Javadoc -
-
-7.1 Formatting -
-
-7.1.1 General form -
-
-7.1.2 Paragraphs -
-
-7.1.3 At-clauses -
-
-7.2 The summary fragment -
-
-7.3 Where Javadoc is used -
-
-7.3.1 Exception: self-explanatory methods -
-
-7.3.2 Exception: overrides -
-
-
-
-

1 Introduction 

- -

This guide is a work in progress. -We are currently working on getting this guide updated to -a point where it is useful for WPILib developers to use.

- -

This document serves as the style guide for WPILib. It is heavily - based on the Google Java Style Guide and copies pretty much word-for-word - the formatting/style components of the guide while cutting a couple - of the programming practices. As this guide evolves, we will likely - introduce more suggested/mandated programming practices specific - to WPILib.

-

It is encouraged that anyone working on the Java WPILib also - read the corresponding C++ guide, as we generally try to develop - the C++ and Java components of the library in parallel and many - programming practices true in one language will be true in the other - (although this is not universally true).

-

-

1.1 Terminology notes 

-

In this document, unless otherwise clarified:

  1. The term class is used inclusively to mean an "ordinary" class, enum class, - interface or annotation type (@interface).
  2. The term comment always refers to implementation comments. We do not - use the phrase "documentation comments", instead using the common term "Javadoc."

Other "terminology notes" will appear occasionally throughout the document.

-

1.2 Guide notes 

-

Example code in this document is non-normative. That is, while the examples -are in Google Style, they may not illustrate the only stylish way to represent the -code. Optional formatting choices made in examples should not be enforced as rules.

-

2 Source file basics 

- -

2.1 File name 

-

The source file name consists of the case-sensitive name of the top-level class it contains, -plus the .java extension.

-

2.2 File encoding: UTF-8 

-

Source files are encoded in UTF-8.

-

2.3 Special characters 

- -

2.3.1 Whitespace characters 

-

Aside from the line terminator sequence, the ASCII horizontal space -character (0x20) is the only whitespace character that appears -anywhere in a source file. This implies that:

  1. All other whitespace characters in string and character literals are escaped.
  2. Tab characters are not used for indentation.
-

2.3.2 Special escape sequences 

-

For any character that has a special escape sequence -(\b, -\t, -\n, -\f, -\r, -\", -\' and -\\), that sequence -is used rather than the corresponding octal -(e.g. \012) or Unicode -(e.g. \u000a) escape.

-

2.3.3 Non-ASCII characters 

-

For the remaining non-ASCII characters, either the actual Unicode character -(e.g. ) or the equivalent Unicode escape -(e.g. \u221e) is used, depending only on which -makes the code easier to read and understand.

Tip: In the Unicode escape case, and occasionally even when actual -Unicode characters are used, an explanatory comment can be very helpful.

Examples:

ExampleDiscussion
String unitAbbrev = "μs";Best: perfectly clear even without a comment.
String unitAbbrev = "\u03bcs"; // "μs"Allowed, but there's no reason to do this.
String unitAbbrev = "\u03bcs"; - // Greek letter mu, "s"Allowed, but awkward and prone to mistakes.
String unitAbbrev = "\u03bcs";Poor: the reader has no idea what this is.
return '\ufeff' + content; - // byte order markGood: use escapes for non-printable characters, and comment if necessary.

Tip: Never make your code less readable simply out of fear that -some programs might not handle non-ASCII characters properly. If that should happen, those -programs are broken and they must be fixed.

-

3 Source file structure 

-

A source file consists of, in order:

  1. License or copyright information, if present
  2. Package statement
  3. Import statements
  4. Exactly one top-level class

Exactly one blank line separates each section that is present.

-

3.1 License or copyright information, if present 

-

If license or copyright information belongs in a file, it belongs here.

-

3.2 Package statement 

-

The package statement is not line-wrapped. The column limit (Section 4.4, -Column limit: 80) does not apply to package statements.

-

3.3 Import statements 

- -

3.3.1 No wildcard imports 

-

Wildcard imports, static or otherwise, are not used.

-

3.3.2 No line-wrapping 

-

Import statements are not line-wrapped. The column limit (Section 4.4, -Column limit: 80) does not apply to import -statements.

-

3.3.3 Ordering and spacing 

-

Import statements are divided into the following groups, in this order, with each group -separated by a single blank line:

  1. All static imports in a single group
  2. com.google imports - (only if this source file is in the com.google package - space)
  3. Third-party imports, one group per top-level package, in ASCII sort order -
    • for example: android, com, junit, org, - sun
  4. java imports
  5. javax imports

Within a group there are no blank lines, and the imported names appear in ASCII sort -order. (Note: this is not the same as the import statements being in -ASCII sort order; the presence of semicolons warps the result.)

-

3.4 Class declaration 

- -

3.4.1 Exactly one top-level class declaration 

-

Each top-level class resides in a source file of its own.

-

3.4.2 Class member ordering 

-

The ordering of the members of a class can have a great effect on learnability, but there is -no single correct recipe for how to do it. Different classes may order their members -differently.

What is important is that each class order its members in some logical -order, which its maintainer could explain if asked. For example, new methods are not -just habitually added to the end of the class, as that would yield "chronological by date -added" ordering, which is not a logical ordering.

-
3.4.2.1 Overloads: never split 
-

When a class has multiple constructors, or multiple methods with the same name, these appear -sequentially, with no intervening members.

-

4 Formatting 

-

Terminology Note: block-like construct refers to -the body of a class, method or constructor. Note that, by Section 4.8.3.1 on -array initializers, any array initializer -may optionally be treated as if it were a block-like construct.

-

4.1 Braces 

- -

4.1.1 Braces are used where optional 

-

Braces are used with -if, -else, -for, -do and -while statements, even when the -body is empty or contains only a single statement.

-

4.1.2 Nonempty blocks: K & R style 

-

Braces follow the Kernighan and Ritchie style -("Egyptian brackets") -for nonempty blocks and block-like constructs:

  • No line break before the opening brace.
  • Line break after the opening brace.
  • Line break before the closing brace.
  • Line break after the closing brace if that brace terminates a statement or the body - of a method, constructor or named class. For example, there is no line break - after the brace if it is followed by else or a - comma.

Example:

-return new MyClass() {
-  @Override public void method() {
-    if (condition()) {
-      try {
-        something();
-      } catch (ProblemException e) {
-        recover();
-      }
-    }
-  }
-};
-

A few exceptions for enum classes are given in Section 4.8.1, -Enum classes.

-

4.1.3 Empty blocks: may be concise 

-

An empty block or block-like construct may be closed immediately after it is -opened, with no characters or line break in between -({}), unless it is part of a -multi-block statement (one that directly contains multiple blocks: -if/else-if/else or -try/catch/finally).

Example:

-  void doNothing() {}
-
-

4.2 Block indentation: +2 spaces 

-

Each time a new block or block-like construct is opened, the indent increases by two -spaces. When the block ends, the indent returns to the previous indent level. The indent level -applies to both code and comments throughout the block. (See the example in Section 4.1.2, -Nonempty blocks: K & R Style.)

-

4.3 One statement per line 

-

Each statement is followed by a line-break.

-

4.4 Column limit: 80 

-

- Projects should have a column limit of 80 characters. - -Except as noted below, any line that would exceed this limit must be line-wrapped, as explained in -Section 4.5, Line-wrapping. -

Exceptions:

  1. Lines where obeying the column limit is not possible (for example, a long URL in Javadoc, - or a long JSNI method reference).
  2. package and - import statements (see Sections - 3.2 Package statement and - 3.3 Import statements).
  3. Command lines in a comment that may be cut-and-pasted into a shell.
-

4.5 Line-wrapping 

-

Terminology Note: When code that might otherwise legally -occupy a single line is divided into multiple lines, typically to avoid overflowing the column -limit, this activity is called -line-wrapping.

There is no comprehensive, deterministic formula showing exactly how to line-wrap in -every situation. Very often there are several valid ways to line-wrap the same piece of code.

Tip: Extracting a method or local variable may solve the problem -without the need to line-wrap.

-

4.5.1 Where to break 

-

The prime directive of line-wrapping is: prefer to break at a -higher syntactic level. Also:

  1. When a line is broken at a non-assignment operator the break comes before - the symbol. (Note that this is not the same practice used in Google style for other languages, - such as C++ and JavaScript.) -
    • This also applies to the following "operator-like" symbols: the dot separator - (.), the ampersand in type bounds - (<T extends Foo & Bar>), and the pipe in - catch blocks - (catch (FooException | BarException e)).
  2. When a line is broken at an assignment operator the break typically comes - after the symbol, but either way is acceptable. -
    • This also applies to the "assignment-operator-like" colon in an enhanced - for ("foreach") statement.
  3. A method or constructor name stays attached to the open parenthesis - (() that follows it.
  4. A comma (,) stays attached to the token that - precedes it.
-

4.5.2 Indent continuation lines at least +4 spaces 

-

When line-wrapping, each line after the first (each continuation line) is indented -at least +4 from the original line.

When there are multiple continuation lines, indentation may be varied beyond +4 as -desired. In general, two continuation lines use the same indentation level if and only if they -begin with syntactically parallel elements.

Section 4.6.3 on Horizontal alignment addresses -the discouraged practice of using a variable number of spaces to align certain tokens with -previous lines.

-

4.6 Whitespace 

- -

4.6.1 Vertical Whitespace 

-

A single blank line appears:

  1. Between consecutive members (or initializers) of a class: fields, constructors, - methods, nested classes, static initializers, instance initializers. -
    • Exception: A blank line between two consecutive - fields (having no other code between them) is optional. Such blank lines are used as needed to - create logical groupings of fields.
  2. Within method bodies, as needed to create logical groupings of statements.
  3. Optionally before the first member or after the last member of the class (neither - encouraged nor discouraged).
  4. As required by other sections of this document (such as Section 3.3, - Import statements).

Multiple consecutive blank lines are permitted, but never required (or encouraged).

-

4.6.2 Horizontal whitespace 

-

Beyond where required by the language or other style rules, and apart from literals, comments and -Javadoc, a single ASCII space also appears in the following places only.

  1. Separating any reserved word, such as - if, - for or - catch, from an open parenthesis - (() - that follows it on that line
  2. Separating any reserved word, such as - else or - catch, from a closing curly brace - (}) that precedes it on that line
  3. Before any open curly brace - ({), with two exceptions: -
    • @SomeAnnotation({a, b}) (no space is used)
    • String[][] x = {{"foo"}}; (no space is required - between {{, by item 8 below)
  4. On both sides of any binary or ternary operator. This also applies to the following - "operator-like" symbols: -
    • the ampersand in a conjunctive type bound: - <T extends Foo & Bar>
    • the pipe for a catch block that handles multiple exceptions: - catch (FooException | BarException e)
    • the colon (:) in an enhanced - for ("foreach") statement
  5. After ,:; or the closing parenthesis - ()) of a cast
  6. On both sides of the double slash (//) that - begins an end-of-line comment. Here, multiple spaces are allowed, but not required.
  7. Between the type and variable of a declaration: - List<String> list
  8. Optional just inside both braces of an array initializer -
    • new int[] {5, 6} and - new int[] { 5, 6 } are both valid

Note: This rule never requires or forbids additional space at the -start or end of a line, only interior space.

-

4.6.3 Horizontal alignment: never required 

-

Terminology Note: Horizontal alignment is the -practice of adding a variable number of additional spaces in your code with the goal of making -certain tokens appear directly below certain other tokens on previous lines.

This practice is permitted, but is never required by Google Style. It is not -even required to maintain horizontal alignment in places where it was already used.

Here is an example without alignment, then using alignment:

-private int x; // this is fine
-private Color color; // this too
-
-private int   x;      // permitted, but future edits
-private Color color;  // may leave it unaligned
-

Tip: Alignment can aid readability, but it creates problems for -future maintenance. Consider a future change that needs to touch just one line. This change may -leave the formerly-pleasing formatting mangled, and that is allowed. More often -it prompts the coder (perhaps you) to adjust whitespace on nearby lines as well, possibly -triggering a cascading series of reformattings. That one-line change now has a "blast radius." -This can at worst result in pointless busywork, but at best it still corrupts version history -information, slows down reviewers and exacerbates merge conflicts.

-

4.7 Grouping parentheses: recommended 

-

Optional grouping parentheses are omitted only when author and reviewer agree that there is no -reasonable chance the code will be misinterpreted without them, nor would they have made the code -easier to read. It is not reasonable to assume that every reader has the entire Java -operator precedence table memorized.

-

4.8 Specific constructs 

- -

4.8.1 Enum classes 

-

After each comma that follows an enum constant, a line-break is optional.

An enum class with no methods and no documentation on its constants may optionally be formatted -as if it were an array initializer (see Section 4.8.3.1 on -array initializers).

-private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
-

Since enum classes are classes, all other rules for formatting classes apply.

-

4.8.2 Variable declarations 

- -
4.8.2.1 One variable per declaration 
-

Every variable declaration (field or local) declares only one variable: declarations such as -int a, b; are not used.

-
4.8.2.2 Declared when needed, initialized as soon as -possible 
-

Local variables are not habitually declared at the start of their containing -block or block-like construct. Instead, local variables are declared close to the point they are -first used (within reason), to minimize their scope. Local variable declarations typically have -initializers, or are initialized immediately after declaration.

-

4.8.3 Arrays 

- -
4.8.3.1 Array initializers: can be "block-like" 
-

Any array initializer may optionally be formatted as if it were a "block-like -construct." For example, the following are all valid (not an exhaustive -list):

-new int[] {           new int[] {
-  0, 1, 2, 3            0,
-}                       1,
-                        2,
-new int[] {             3,
-  0, 1,               }
-  2, 3
-}                     new int[]
-                          {0, 1, 2, 3}
-
-
4.8.3.2 No C-style array declarations 
-

The square brackets form a part of the type, not the variable: -String[] args, not -String args[].

-

4.8.4 Switch statements 

-

Terminology Note: Inside the braces of a -switch block are one or more statement groups. Each statement group consists of -one or more switch labels (either case FOO: or -default:), followed by one or more statements.

-
4.8.4.1 Indentation 
-

As with any other block, the contents of a switch block are indented +2.

After a switch label, a newline appears, and the indentation level is increased +2, exactly as -if a block were being opened. The following switch label returns to the previous indentation -level, as if a block had been closed.

-
4.8.4.2 Fall-through: commented 
-

Within a switch block, each statement group either terminates abruptly (with a -break, -continue, -return or thrown exception), or is marked with a comment -to indicate that execution will or might continue into the next statement group. Any -comment that communicates the idea of fall-through is sufficient (typically -// fall through). This special comment is not required in -the last statement group of the switch block. Example:

-switch (input) {
-  case 1:
-  case 2:
-    prepareOneOrTwo();
-    // fall through
-  case 3:
-    handleOneTwoOrThree();
-    break;
-  default:
-    handleLargeNumber(input);
-}
-
-
4.8.4.3 The default case is present 
-

Each switch statement includes a default statement -group, even if it contains no code.

-

4.8.5 Annotations 

-

Annotations applying to a class, method or constructor appear immediately after the -documentation block, and each annotation is listed on a line of its own (that is, one annotation -per line). These line breaks do not constitute line-wrapping (Section -4.5, Line-wrapping), so the indentation level is not -increased. Example:

-@Override
-@Nullable
-public String getNameIfPresent() { ... }
-

Exception: A single parameterless annotation -may instead appear together with the first line of the signature, for example:

-@Override public int hashCode() { ... }
-

Annotations applying to a field also appear immediately after the documentation block, but in -this case, multiple annotations (possibly parameterized) may be listed on the same line; -for example:

-@Partial @Mock DataLoader loader;
-

There are no specific rules for formatting parameter and local variable annotations.

-

4.8.6 Comments 

- -
4.8.6.1 Block comment style 
-

Block comments are indented at the same level as the surrounding code. They may be in -/* ... */ style or -// ... style. For multi-line -/* ... */ comments, subsequent lines must start with -* aligned with the * on the previous line.

-/*
- * This is          // And so           /* Or you can
- * okay.            // is this.          * even do this. */
- */
-

Comments are not enclosed in boxes drawn with asterisks or other characters.

Tip: When writing multi-line comments, use the -/* ... */ style if you want automatic code formatters to -re-wrap the lines when necessary (paragraph-style). Most formatters don't re-wrap lines in -// ... style comment blocks.

-

4.8.7 Modifiers 

-

Class and member modifiers, when present, appear in the order -recommended by the Java Language Specification: -

-public protected private abstract static final transient volatile synchronized native strictfp
-
-

4.8.8 Numeric Literals 

-

long-valued integer literals use an uppercase L suffix, never -lowercase (to avoid confusion with the digit 1). For example, 3000000000L -rather than 3000000000l.

-

5 Naming 

- -

5.1 Rules common to all identifiers 

-

Identifiers use only ASCII letters and digits, and in two cases noted below, underscores. Thus -each valid identifier name is matched by the regular expression \w+ .

In Google Style special prefixes or -suffixes, like those seen in the examples name_, -mName, s_name and -kName, are not used. -For WPILib, we do make one exception to this rule for non-constant field name.

-

5.2 Rules by identifier type 

- -

5.2.1 Package names 

-

Package names are all lowercase, with consecutive words simply concatenated together (no -underscores). For example, com.example.deepspace, not -com.example.deepSpace or -com.example.deep_space.

-

5.2.2 Class names 

-

Class names are written in UpperCamelCase.

Class names are typically nouns or noun phrases. For example, -Character or -ImmutableList. Interface names may also be nouns or -noun phrases (for example, List), but may sometimes be -adjectives or adjective phrases instead (for example, -Readable).

There are no specific rules or even well-established conventions for naming annotation types.

Test classes are named starting with the name of the class they are testing, and ending -with Test. For example, -HashTest or -HashIntegrationTest.

-

5.2.3 Method names 

-

Method names are written in lowerCamelCase.

Method names are typically verbs or verb phrases. For example, -sendMessage or -stop.

Underscores may appear in JUnit test method names to separate logical components of the -name. One typical pattern is test<MethodUnderTest>_<state>, -for example testPop_emptyStack. There is no One Correct -Way to name test methods.

-

5.2.4 Constant names 

-

Constant names use CONSTANT_CASE: all uppercase -letters, with words separated by underscores. But what is a constant, exactly?

Every constant is a static final field, but not all static final fields are constants. Before -choosing constant case, consider whether the field really feels like a constant. For -example, if any of that instance's observable state can change, it is almost certainly not a -constant. Merely intending to never mutate the object is generally not -enough. Examples:

-// Constants
-static final int NUMBER = 5;
-static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
-static final Joiner COMMA_JOINER = Joiner.on(',');  // because Joiner is immutable
-static final SomeMutableType[] EMPTY_ARRAY = {};
-enum SomeEnum { ENUM_CONSTANT }
-
-// Not constants
-static String nonFinal = "non-final";
-final String nonStatic = "non-static";
-static final Set<String> mutableCollection = new HashSet<String>();
-static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
-static final Logger logger = Logger.getLogger(MyClass.getName());
-static final String[] nonEmptyArray = {"these", "can", "change"};
-

These names are typically nouns or noun phrases.

-

5.2.5 Non-constant field names 

-

Non-constant field names (static or otherwise) are written -in lowerCamelCase with a preceding m_.

-

These names are typically nouns or noun phrases. For example, -m_computedValues or -m_index.

-

5.2.6 Parameter names 

-

Parameter names are written in lowerCamelCase.

One-character parameter names should be avoided.

-

5.2.7 Local variable names 

-

Local variable names are written in lowerCamelCase, and can be -abbreviated more liberally than other types of names.

However, one-character names should be avoided, except for temporary and looping variables.

Even when final and immutable, local variables are not considered to be constants, and should not -be styled as constants.

-

5.2.8 Type variable names 

-

Each type variable is named in one of two styles:

  • A single capital letter, optionally followed by a single numeral (such as - E, T, - X, T2) -
  • A name in the form used for classes (see Section 5.2.2, - Class names), followed by the capital letter - T (examples: - RequestT, - FooBarT).
-

5.3 Camel case: defined 

-

Sometimes there is more than one reasonable way to convert an English phrase into camel case, -such as when acronyms or unusual constructs like "IPv6" or "iOS" are present. To improve -predictability, Google Style specifies the following (nearly) deterministic scheme.

Beginning with the prose form of the name:

  1. Convert the phrase to plain ASCII and remove any apostrophes. For example, "Müller's - algorithm" might become "Muellers algorithm".
  2. Divide this result into words, splitting on spaces and any remaining punctuation (typically - hyphens). - -
    • Recommended: if any word already has a conventional camel-case appearance in common - usage, split this into its constituent parts (e.g., "AdWords" becomes "ad words"). Note - that a word such as "iOS" is not really in camel case per se; it defies any - convention, so this recommendation does not apply.
  3. Now lowercase everything (including acronyms), then uppercase only the first - character of: -
    • ... each word, to yield upper camel case, or
    • ... each word except the first, to yield lower camel case
  4. Finally, join all the words into a single identifier.

Note that the casing of the original words is almost entirely disregarded. Examples:

Prose formCorrectIncorrect
"XML HTTP request"XmlHttpRequestXMLHTTPRequest
"new customer ID"newCustomerIdnewCustomerID
"inner stopwatch"innerStopwatchinnerStopWatch
"supports IPv6 on iOS?"supportsIpv6OnIossupportsIPv6OnIOS
"YouTube importer"YouTubeImporter
YoutubeImporter*

*Acceptable, but not recommended.

Note: Some words are ambiguously hyphenated in the English -language: for example "nonempty" and "non-empty" are both correct, so the method names -checkNonempty and -checkNonEmpty are likewise both correct.

-

6 Programming Practices 

- -

6.1 @Override: always used 

-

A method is marked with the @Override annotation -whenever it is legal. This includes a class method overriding a superclass method, a class method -implementing an interface method, and an interface method respecifying a superinterface -method.

Exception:@Override may be omitted when the parent method is -@Deprecated.

-

6.2 Caught exceptions: not ignored 

-

Except as noted below, it is very rarely correct to do nothing in response to a caught -exception. (Typical responses are to log it, or if it is considered "impossible", rethrow it as an -AssertionError.)

When it truly is appropriate to take no action whatsoever in a catch block, the reason this is -justified is explained in a comment.

-try {
-  int i = Integer.parseInt(response);
-  return handleNumericResponse(i);
-} catch (NumberFormatException ok) {
-  // it's not numeric; that's fine, just continue
-}
-return handleTextResponse(response);
-

Exception: In tests, a caught exception may be ignored -without comment if it is named expected. The -following is a very common idiom for ensuring that the method under test does throw an -exception of the expected type, so a comment is unnecessary here.

-try {
-  emptyStack.pop();
-  fail();
-} catch (NoSuchElementException expected) {
-}
-
-

6.3 Static members: qualified using class 

-

When a reference to a static class member must be qualified, it is qualified with that class's -name, not with a reference or expression of that class's type.

-Foo aFoo = ...;
-Foo.aStaticMethod(); // good
-aFoo.aStaticMethod(); // bad
-somethingThatYieldsAFoo().aStaticMethod(); // very bad
-
-

6.4 Finalizers: not used 

-

It is extremely rare to override Object.finalize.

Tip: Don't do it. If you absolutely must, first read and understand -Effective Java -Item 7, "Avoid Finalizers," very carefully, and then don't do it.

-

7 Javadoc 

- -

7.1 Formatting 

- -

7.1.1 General form 

-

The basic formatting of Javadoc blocks is as seen in this example:

-/**
- * Multiple lines of Javadoc text are written here,
- * wrapped normally...
- */
-public int method(String p1) { ... }
-

... or in this single-line example:

-/** An especially short bit of Javadoc. */
-

The basic form is always acceptable. The single-line form may be substituted when there are no -at-clauses present, and the entirety of the Javadoc block (including comment markers) can fit on a -single line.

-

7.1.2 Paragraphs 

-

One blank line—that is, a line containing only the aligned leading asterisk -(*)—appears between paragraphs, and before the group of "at-clauses" if -present. Each paragraph but the first has <p> immediately before the first word, -with no space after.

-

7.1.3 At-clauses 

-

Any of the standard "at-clauses" that are used appear in the order @param, -@return, @throws, @deprecated, and these four types never -appear with an empty description. When an at-clause doesn't fit on a single line, continuation lines -are indented four (or more) spaces from the position of the @. -

-

7.2 The summary fragment 

-

The Javadoc for each class and member begins with a brief summary fragment. This -fragment is very important: it is the only part of the text that appears in certain contexts such as -class and method indexes.

This is a fragment—a noun phrase or verb phrase, not a complete sentence. It does -not begin with A {@code Foo} is a..., or -This method returns..., nor does it form a complete imperative sentence -like Save the record.. However, the fragment is capitalized and -punctuated as if it were a complete sentence.

Tip: A common mistake is to write simple Javadoc in the form -/** @return the customer ID */. This is incorrect, and should be -changed to /** Returns the customer ID. */.

-

7.3 Where Javadoc is used 

-

At the minimum, Javadoc is present for every -public class, and every -public or -protected member of such a class, with a few exceptions -noted below.

Other classes and members still have Javadoc as needed. Whenever an implementation -comment would be used to define the overall purpose or behavior of a class, method or field, that -comment is written as Javadoc instead. (It's more uniform, and more tool-friendly.)

-

7.3.1 Exception: self-explanatory methods 

-

Javadoc is optional for "simple, obvious" methods like -getFoo, in cases where there really and truly is -nothing else worthwhile to say but "Returns the foo".

Important: it is not appropriate to cite this exception to justify -omitting relevant information that a typical reader might need to know. For example, for a method -named getCanonicalName, don't omit its documentation -(with the rationale that it would say only -/** Returns the canonical name. */) if a typical reader may have no idea -what the term "canonical name" means!

-

7.3.2 Exception: overrides 

-

Javadoc is not always present on a method that overrides a supertype method. -


-
Last changed: March 21, 2014
- - diff --git a/styleguide/javaguidelink.png b/styleguide/javaguidelink.png deleted file mode 100644 index 75d5c7ba8dcc22b100b18b8c800da9ed99afbf7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{1|(OCFP#RYT0C7GLo80eoovW;K!L}#nDP3f zLaS;${>#Z0UFCva2M?aC%WS)H?QB_5g+S7$o~{tDg%>YO@zZ2dVrDwAKuzez0i`Ul zbE@l3-bt1V61%3rbUmT<%Dy%3?@ZTjdAE&Ol|^ce?3NY5cci0UGX8nXab09-b9Pp3 o=^vw)Kdk>g`TD$n)}_KZpTAr6giAPD0^P&l>FVdQ&MBb@0Ar|2 1 and line[0] != " ": - temp.write("\n") - temp.write(line) - - # Copy rest of original file into new one - for line in file: - temp.write(line) - - # Replace old file if it was changed - if self.md5sum(name) != self.md5sum(name + ".tmp"): - os.remove(name) - os.rename(name + ".tmp", name) - else: - os.remove(name + ".tmp") - - # Compute MD5 sum of file - @staticmethod - def md5sum(name): - with open(name, mode = "rb") as file: - digest = hashlib.md5() - for buf in iter(partial(file.read, 128), b""): - digest.update(buf) - return digest.hexdigest() diff --git a/styleguide/lint.py b/styleguide/lint.py deleted file mode 100644 index 64c771a528..0000000000 --- a/styleguide/lint.py +++ /dev/null @@ -1,76 +0,0 @@ -# This task runs cpplint.py on all C++ source files. - -""" -cpplint.py was converted from python 2 to python 3 with the following command -and a patch was applied: - -2to3 -f all -f buffer -f idioms -f set_literal -f ws_comma -nw cpplint.py - -diff --git a/styleguide/cpplint.py b/styleguide/cpplint.py ---- a/styleguide/cpplint.py -+++ b/styleguide/cpplint.py -@@ -5108,7 +5108,8 @@ def CheckCasts(filename, clean_lines, linenum, error): - - if not expecting_function: - CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', -- r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) -+ r'\(((unsigned )?(char|(short |long )?int|long)|float|' -+ 'double|bool|u?int(8_t|16_t|32_t|64_t))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # -@@ -5120,7 +5121,7 @@ def CheckCasts(filename, clean_lines, linenum, error): - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', -- r'\((\w+\s?\*+\s?)\)', error) -+ r'\(((const )?\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 -@@ -6102,13 +6103,6 @@ def ParseArguments(args): - 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) -""" - -import os -import subprocess -import sys -from task import Task - -class Lint(Task): - def getIncludeExtensions(self): - return ["cpp", "h", "inc"] - - def run(self, name): - # Handle running in either the root or styleguide directories - cpplintPrefix = "" - if os.getcwd().rpartition(os.sep)[2] != "styleguide": - cpplintPrefix = "styleguide/" - - # Run cpplint.py - proc = subprocess.Popen(["python", cpplintPrefix + "cpplint.py", - "--filter=" - "-build/c++11," - "-build/header_guard," - "-build/include," - "-build/namespaces," - "-readability/todo," - "-runtime/references," - "-runtime/string", - "--extensions=cpp,h,inc", - name], bufsize = 1, stderr = subprocess.PIPE) - for line in proc.stderr: - if b"Done processing" not in line and b"Total errors" not in line: - print(line.rstrip().decode(sys.stdout.encoding)) diff --git a/styleguide/newline.py b/styleguide/newline.py deleted file mode 100644 index f9224c5c62..0000000000 --- a/styleguide/newline.py +++ /dev/null @@ -1,35 +0,0 @@ -# This task ensures that all source files have exactly one EOF newline. - -import os -from task import Task - -class Newline(Task): - def run(self, name): - newlines = 0 - - # Remove all but one EOF newline, or append one if necessary - eol = os.linesep - if name.endswith("bat"): - eol = "\r\n" - with open(name, "r+", newline = eol) as file: - # Get file size - file.seek(0, os.SEEK_END) - size = file.tell() - - # Seek to last character in file - file.seek(size - 1) - - # While last character is a newline - while file.read(1) == "\n": - newlines = newlines + 1 - - # Seek to character before newline - file.seek(size - 1 - len(eol) * newlines) - - if newlines < 1: - # Append newline to end of file - file.seek(size) - file.write("\n") - elif newlines > 1: - # Truncate all but one newline - file.truncate(size - len(eol) * (newlines - 1)) diff --git a/styleguide/styleguide.css b/styleguide/styleguide.css deleted file mode 100644 index f5d7a63c48..0000000000 --- a/styleguide/styleguide.css +++ /dev/null @@ -1,146 +0,0 @@ -body { - background-color: #fff; - color: #333; - font-family: sans-serif; - font-size: 10pt; - margin-right: 100px; - margin-left: 100px; -} - -h1, h2, h3, h4, h5, h6, .toc_title { - color: #06c; - margin-top: 2em; - margin-bottom: 1em; -} - -h1 { - text-align: center; - font-size: 18pt; -} - -h2, .toc_title { - font-weight: bold; - font-size: 12pt; - margin-left: -40px; -} - -h3, h4, h5, h6 { - font-size: 10pt; - margin-left: -20px; -} - -.toc_category, .toc_stylepoint { - font-size: 10pt; - padding-top: .3em; - padding-bottom: .3em; -} - -table { - border-collapse: collapse; -} - -td, th { - border: 1px solid #ccc; - padding: 2px 12px; - font-size: 10pt; -} - -.toc td, .toc th { - border-width: 1px 5px; -} - -code, samp, var { - color: #060; -} - -pre { - font-size: 10pt; - display: block; - color: #060; - background-color: #f8fff8; - border-color: #f0fff0; - border-style: solid; - border-top-width: 1px; - border-bottom-width: 1px; - border-right-width: 1px; - border-left-width: 5px; - padding-left: 12px; - padding-right: 12px; - padding-top: 4px; - padding-bottom: 4px; -} - -pre.badcode { - color: #c00; - background-color: #fff8f8; - border-color: #fff0f0; -} - -.showhide_button { - float: left; - cursor: pointer; - border-width: 1px; - border-style: solid; - border-color: #ddd #aaa #aaa #ddd; - padding: 0 3px 1px; - margin: 0 4px 8px 0; - border-radius: 3px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; -} - -.link_button { - float: left; - display: none; - background-color: #f8f8ff; - border-color: #f0f0ff; - border-style: solid; - border-width: 1px; - font-size: 75%; - margin-top: 0; - margin-left: -50px; - padding: 4px; - border-radius: 3px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; -} - -address { - text-align: right; -} - -hr { - margin-top: 3.5em; - border-width: 1px; - color: #fff; -} - -.stylepoint_section { - display: block; - margin-bottom: 1em; - color: #5588ff; - font-family: sans-serif; - font-size: 90%; - font-weight: bold; - margin-left: -2%; -} - -.stylepoint_subsection { - color: #667799; - font-family: sans-serif; - font-size: 90%; - font-weight: bold; - margin-left: -1%; -} - -.stylepoint_subsubsection { - color: #667799; - font-family: sans-serif; - font-size: 80%; - font-weight: bold; - margin-left: 0; -} - -.revision { - text-align: right; -} diff --git a/styleguide/styleguide.xsl b/styleguide/styleguide.xsl deleted file mode 100644 index 84cb144db0..0000000000 --- a/styleguide/styleguide.xsl +++ /dev/null @@ -1,923 +0,0 @@ - - - - - - - - - - - - - - - - - - <xsl:value-of select="@title"/> - - - - - - - -

- - - -
- - - - - - - - - - - - -
-

- Each style point has a summary for which additional information is available - by toggling the accompanying arrow button that looks this way: - - . - You may toggle all summaries with the big arrow button: -

-
- - - - - - - Toggle all summaries -
-
- - - - -
- - -

Parting Words

- -
- - -
- -

- - - - - - - - -

- -
-
- - -
- - - - - - - - - - - - - - - - -

- - - - - -

- - - - - javascript:ShowHideByName(' - - ') - - - - ?showone=# - link - - - - - - - - - - - -
-
- - - -
- - -
-
- - - -
- -
- - - - - display: inline - display: none - - - -
-
-
- - -

- - Definition: - -

-
- - -

- - Pros: - -

-
- - -

- - Cons: - -

-
- - -

- - Decision: - -

-
- - -

- -

TODO: - -
-

-
- - -

- - - -

-
- - -

- - - -

-
- - -
- -

-           
-           
-           
-           
-             
-               
-               
-             
-           
-         
-
-
- - -
- -

-           
-           
-           
-           
-             
-               
-               
-             
-           
-         
-
-
- - -
- -

-             
-           
-
-
- - -
- -

-                             
-                           
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
-
Table of Contents
- - - - - - - -
- - -
- - - - - - # - - - - - - - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ - - - - - - - - - - - - - - -
diff --git a/styleguide/task.py b/styleguide/task.py deleted file mode 100644 index aaaa4590cc..0000000000 --- a/styleguide/task.py +++ /dev/null @@ -1,100 +0,0 @@ -# Provides a task base class for use by format.py - -from abc import * -import os -import re - -sep = os.sep -# If directory separator is backslash, escape it for regexes -if sep == "\\": - sep += "\\" - -# There are two groups of regexes which prevent tasks from running on matching -# files: -# 1) generated files (shouldn't be modified) -# 2) modifiable files -# -# format.py excludes matches for the "modifiable" regex before checking for -# modifications to generated files because some of the regexes from each group -# overlap. - -# List of regexes for folders which contain generated files -genFolderExclude = \ - [name + sep for name in [ - "FRC_FPGA_ChipObject", - "NetworkCommunication", - "ctre", - "frccansae", - "gtest", - "i2clib", - "msgs", - "ni-libraries", - "ni" + sep + "vision", - "spilib", - "wpilibj" + sep + "src" + sep + "athena" + sep + "cpp" + sep + "nivision", - "visa"]] - -# List of regexes for generated files -genFileExclude = [name + "$" for name in [ - "CanTalonSRX\.h", - "NIIMAQdx\.h", - "can_proto\.h", - "nivision\.h"]] - -# Regex for generated file exclusions -genRegexExclude = re.compile("|".join(genFolderExclude + genFileExclude)) - -# Regex for folders which contain modifiable files -modifiableFolderExclude = \ - [name + sep for name in [ - "\.git", - "\.gradle", - "__pycache__", - "build", - "wpilibj" + sep + "src" + sep + "athena" + sep + "cpp" + sep + "include", - "wpilibj" + sep + "src" + sep + "athena" + sep + "cpp" + sep + "lib"]] - -# List of regexes for modifiable files -modifiableFileExclude = [name + "$" for name in [ - "\.jar", - "\.patch", - "\.png", - "\.py", - "\.so"]] - -# Regex for modifiable file exclusions -modifiableRegexExclude = \ - re.compile("|".join(modifiableFolderExclude + modifiableFileExclude)) - -class Task(object): - __metaclass__ = ABCMeta - - def __init__(self): - self.regexInclude = re.compile("|".join(["\." + ext + "$" for ext in - self.getIncludeExtensions()])) - - # Extensions of files which should be included in processing - def getIncludeExtensions(self): - return [] - - # Perform task on file with given name - @abstractmethod - def run(self, name): - return - - # Returns True if file is modifiable but should not have tasks run on it - @staticmethod - def isModifiableFile(name): - return modifiableRegexExclude.search(name) - - # Returns True if file isn't generated (generated files are skipped) - @staticmethod - def isGeneratedFile(name): - return genRegexExclude.search(name) - - # Returns True if file has an extension this task can process - def fileMatchesExtension(self, name): - if self.getIncludeExtensions() != []: - return self.regexInclude.search(name) - else: - return True diff --git a/styleguide/whitespace.py b/styleguide/whitespace.py deleted file mode 100644 index c110211823..0000000000 --- a/styleguide/whitespace.py +++ /dev/null @@ -1,23 +0,0 @@ -# This task removes trailing whitespace from all source files. - -import os -from task import Task - -class Whitespace(Task): - def run(self, name): - # Remove trailing whitespace - fileChanged = False - with open(name, "r") as file: - with open(name + ".tmp", "w") as temp: - for line in file: - processedLine = line[0:len(line) - 1].rstrip() + "\n" - if not fileChanged and len(line) != len(processedLine): - fileChanged = True - temp.write(processedLine) - - # Replace old file if it was changed - if fileChanged: - os.remove(name) - os.rename(name + ".tmp", name) - else: - os.remove(name + ".tmp")