prototool lint
lints your Protobuf files.
Lint rules can be set using the configuration file. See the configuration at
etc/config/example/prototool.yaml for all available
options. There are three pre-configured groups of rules, the setting of which is integral to the
prototool lint
, prototool create
, and prototool format
commands:
uber2
: This lint group follows the V2 Uber Style Guide, and makes some modifications to more closely follow the Google Cloud APIs file structure, as well as adding even more rules to enforce more consistent development patterns. This is the lint group we recommend using.uber1
: This lint group follows the V1 Uber Style Guide. For backwards compatibility reasons, this is the default lint group, however we recommend using theuber2
lint group.google
: This lint group follows the Google Style Guide. This is a small group of rules meant to enforce basic naming. The style guide is copied to etc/style/google/google.proto.
To see the differences between lint groups, use the --diff-lint-groups
flag:
prototool lint --diff-lint-groups uber1,uber2
Configuration of your group can be done by setting the lint.group
option in your prototool.yaml
file:
lint:
group: uber2
See the prototool.yaml
files at
etc/style/google/prototool.yaml and
etc/style/uber1/prototool.yaml for examples.
There is also the special lint group empty
, which has no lint rules. This allows one to specify
only the linters they want in lint.rules.add
:
lint:
group: empty
rules:
add:
- MESSAGE_NAMES_CAMEL_CASE
- MESSAGE_NAMES_CAPITALIZED
You can configure ignoring of lint rules on a per-file basis:
lint:
ignores:
- id: MESSAGE_NAMES_CAMEL_CASE
files:
- foo.proto
- bar/baz.proto
To generate the a YAML configuration for currently-failing lint rules that can be copied into your
configuration file, use --generate-ignores
. This will lint your files, ignoring the existing
setting for lint.ignores
, and print a new value for it. Note that you should make sure not to
touch other settings for lint
in your configuration file as this flag only generates the
lint.ignores
option.
prototool lint path/to/dir --generate-ignores
Linting also understands the concept of file headers, typically license headers. To specify a file
header, add the following to your prototool.yaml
:
lint:
file_header:
path: path/to/header.txt
is_commented: true
Alternatively, directly specify the content:
lint:
file_header:
content: |
//
// Acme, Inc. (c) 2019
//
is_commented: true
The path
option specifies the path to the file that contains the header data. The content
option specifies the content directly. Only one of these can be specified. The is_commented
option specifies whether the header data is already commented, and if not, //
will be added
before all non-empty lines, and //
will be added before all empty lines. is_commented
is
optional and generally will not be set if the file is not commented, for example if path
points
to a text LICENSE file.
If lint.file_header.path
or lint.file_header.content
is set, prototool lint
,
prototool create
, and prototool format --fix
will all take the file header into account.
See internal/cmd/testdata/lint for additional examples of
configurations, and run prototool lint internal/cmd/testdata/lint/DIR
from a checkout of this
repository to see example failures.
Files must be valid Protobuf that can be compiled with protoc
, so prior to linting,
prototool lint
will compile your using protoc
. Note, however, this is very fast - for the two
files in etc/style/uber1, compiling and linting only takes approximately
3/100ths of a second:
$ time prototool lint etc/style/uber1
real 0m0.037s
user 0m0.026s
sys 0m0.017s
For all 694 Protobuf files currently in googleapis, this takes approximately 3/4ths of a second:
$ git remote -v
origin https://github.com/googleapis/googleapis (fetch)
origin https://github.com/googleapis/googleapis (push)
$ cat prototool.yaml
protoc:
allow_unused_imports: true
lint:
group: google
$ time prototool lint
real 0m0.734s
user 0m3.835s
sys 0m0.924s
Following is a list of all lint rules provided by Prototool as well as what lint group they belong to (if applicable).
Rule | Description | Lint Group |
---|---|---|
COMMENTS_NO_C_STYLE | Verifies that there are no /* C-style */ comments. |
uber1, uber2 |
COMMENTS_NO_INLINE | Verifies that there are no inline comments. | uber2 |
ENUM_FIELD_NAMES_UPPER_SNAKE_CASE | Verifies that all enum field names are UPPER_SNAKE_CASE . |
google, uber1, uber2 |
ENUM_FIELD_NAMES_UPPERCASE | Verifies that all enum field names are UPPERCASE . |
none |
ENUM_FIELD_PREFIXES_EXCEPT_MESSAGE | Verifies that all enum fields are prefixed with ENUM_NAME_ . |
uber2 |
ENUM_FIELD_PREFIXES | Verifies that all enum fields are prefixed with [NESTED_MESSAGE_NAME_]ENUM_NAME_ . |
uber1 |
ENUM_FIELDS_HAVE_COMMENTS | Verifies that all enum fields have a comment of the form // FIELD_NAME ... . |
none |
ENUM_FIELDS_HAVE_SENTENCE_COMMENTS | Verifies that all enum fields have a comment that contains at least one complete sentence. | none |
ENUM_NAMES_CAMEL_CASE | Verifies that all enum names are CamelCase. | google, uber1, uber2 |
ENUM_NAMES_CAPITALIZED | Verifies that all enum names are Capitalized. | google, uber1, uber2 |
ENUM_ZERO_VALUES_INVALID_EXCEPT_MESSAGE | Verifies that all enum zero value names are ENUM_NAME_INVALID . |
uber2 |
ENUM_ZERO_VALUES_INVALID | Verifies that all enum zero value names are [NESTED_MESSAGE_NAME_]ENUM_NAME_INVALID . |
uber1 |
ENUMS_HAVE_COMMENTS | Verifies that all enums have a comment of the form // EnumName ... . |
none |
ENUMS_HAVE_SENTENCE_COMMENTS | Verifies that all enums have a comment that contains at least one complete sentence. | uber2 |
ENUMS_NO_ALLOW_ALIAS | Verifies that no enums use the option allow_alias . |
uber1, uber2 |
FIELDS_NOT_RESERVED | Verifies that no message or enum has a reserved field. | uber2 |
FILE_HEADER | Verifies that the file header matches the expected file header if the file_header option is set in the configuration file. | google, uber1, uber2 |
FILE_NAMES_LOWER_SNAKE_CASE | Verifies that the file name is lower_snake_case.proto . |
uber2 |
FILE_OPTIONS_EQUAL_CSHARP_NAMESPACE_CAPITALIZED | Verifies that the file option csharp_namespace is the capitalized version of the package. |
uber2 |
FILE_OPTIONS_EQUAL_GO_PACKAGE_PB_SUFFIX | Verifies that the file option go_package is equal to $(basename PACKAGE)pb . |
uber1 |
FILE_OPTIONS_EQUAL_GO_PACKAGE_V2_SUFFIX | Verifies that the file option go_package is equal to the last two values of the package separated by "."s, or just the package name if there are no "."s. |
uber2 |
FILE_OPTIONS_EQUAL_JAVA_MULTIPLE_FILES_TRUE | Verifies that the file option java_multiple_files is equal to true. |
uber1, uber2 |
FILE_OPTIONS_EQUAL_JAVA _OUTER_CLASSNAME_PROTO_SUFFIX |
Verifies that the file option java_outer_classname is equal to $(upperCamelCase $(basename FILE))Proto. |
uber1, uber2 |
FILE_OPTIONS_EQUAL_JAVA_PACKAGE_COM_PREFIX | Verifies that the file option java_package is equal to com.PACKAGE . |
uber1 |
FILE_OPTIONS_EQUAL_JAVA_PACKAGE_PREFIX | Verifies that the file option java_package is equal to PREFIX.PACKAGE , with PREFIX defaulting to com and configurable in your configuration file. |
uber2 |
FILE_OPTIONS_EQUAL_OBJC_CLASS_PREFIX_ABBR | Verifies that the file option objc_class_prefix is the abbreviated version of the package. |
uber2 |
FILE_OPTIONS_EQUAL_PHP_NAMESPACE_CAPITALIZED | Verifies that the file option "php_namespace" is the capitalized version of the package. | uber2 |
FILE_OPTIONS_REQUIRE_CSHARP_NAMESPACE | Verifies that the file option csharp_namespace is set. |
uber2 |
FILE_OPTIONS_REQUIRE_GO_PACKAGE | Verifies that the file option go_package is set. |
uber1, uber2 |
FILE_OPTIONS_REQUIRE_JAVA_MULTIPLE_FILES | Verifies that the file option java_multiple_files is set. |
uber1, uber2 |
FILE_OPTIONS_REQUIRE_JAVA_OUTER_CLASSNAME | Verifies that the file option java_outer_classname is set. |
uber1, uber2 |
FILE_OPTIONS_REQUIRE_JAVA_PACKAGE | Verifies that the file option java_package is set. |
uber1, uber2 |
FILE_OPTIONS_REQUIRE_OBJC_CLASS_PREFIX | Verifies that the file option objc_class_prefix is set. |
uber2 |
FILE_OPTIONS_REQUIRE_PHP_NAMESPACE | Verifies that the file option php_namespace is set. |
uber2 |
FILE_OPTIONS_REQUIRE_RUBY_PACKAGE | Verifies that the file option ruby_package is set. |
none |
FILE_OPTIONS_CSHARP_NAMESPACE_SAME_IN_DIR | Verifies that the file option csharp_namespace of all files in a directory are the same. |
uber2 |
FILE_OPTIONS_GO_PACKAGE_SAME_IN_DIR | Verifies that the file option go_package of all files in a directory are the same. |
uber1, uber2 |
FILE_OPTIONS_JAVA_MULTIPLE_FILES_SAME_IN_DIR | Verifies that the file option java_multiple_files of all files in a directory are the same. |
uber1, uber2 |
FILE_OPTIONS_JAVA_PACKAGE_SAME_IN_DIR | Verifies that the file option java_package of all files in a directory are the same. |
uber1, uber2 |
FILE_OPTIONS_OBJC_CLASS_PREFIX_SAME_IN_DIR | Verifies that the file option objc_class_prefix of all files in a directory are the same. |
uber2 |
FILE_OPTIONS_PHP_NAMESPACE_SAME_IN_DIR | Verifies that the file option php_namespace of all files in a directory are the same. |
uber2 |
FILE_OPTIONS_UNSET_JAVA_MULTIPLE_FILES | Verifies that the file option java_multiple_files is unset. |
none |
FILE_OPTIONS_UNSET_JAVA_OUTER_CLASSNAME | Verifies that the file option java_outer_classname is unset. |
none |
FILE_OPTIONS_GO_PACKAGE_NOT_LONG_FORM | Verifies that the file option go_package is not of the form go/import/path;package . |
uber1, uber2 |
GOGO_NOT_IMPORTED | Verifies that the gogo.proto file from gogo/protobuf is not imported. |
none |
IMPORTS_NOT_PUBLIC | Verifies that there are no public imports. | uber2 |
IMPORTS_NOT_WEAK | Verifies that there are no weak imports. | uber2 |
MESSAGE_FIELD_NAMES_FILENAME | Verifies that all message field names do not contain file_name as filename should be used instead. |
uber2 |
MESSAGE_FIELD_NAMES_FILEPATH | Verifies that all message field names do not contain file_path as filepath should be used instead. |
uber2 |
MESSAGE_FIELD_NAMES_LOWER_SNAKE_CASE | Verifies that all message field names are lower_snake_case . |
google, uber1, uber2 |
MESSAGE_FIELD_NAMES_LOWERCASE | Verifies that all message field names are lowercase . |
none |
MESSAGE_FIELD_NAMES_NO_DESCRIPTOR | Verifies that all message field names are not descriptor , which results in a collision in Java-generated code. |
uber2 |
MESSAGE_FIELDS_DURATION | Verifies that all non-map fields that contain "duration" in their name are of type google.protobuf.Duration . |
none |
MESSAGE_FIELDS_HAVE_COMMENTS | Verifies that all message fields have a comment of the form // field_name ... . |
none |
MESSAGE_FIELDS_HAVE_SENTENCE_COMMENTS | Verifies that all message fields have a comment that contains at least one complete sentence. | none |
MESSAGE_FIELDS_NO_JSON_NAME | Verifies that no message field has the json_name option set. |
uber2 |
MESSAGE_FIELDS_NOT_FLOATS | Verifies that all message fields are not floats. | none |
MESSAGE_FIELDS_TIME | Verifies that all non-map fields that contain time in their name are of type google.protobuf.Timestamp . |
none |
MESSAGE_NAMES_CAMEL_CASE | Verifies that all non-extended message names are CamelCase. | google, uber1, uber2 |
MESSAGE_NAMES_CAPITALIZED | Verifies that all non-extended message names are Capitalized. | google, uber1, uber2 |
MESSAGES_HAVE_COMMENTS_EXCEPT _REQUEST_RESPONSE_TYPES |
Verifies that all non-extended messages except for request and response types have a comment of the form // MessageName ... . |
none |
MESSAGES_HAVE_COMMENTS | Verifies that all non-extended messages have a comment of the form // MessageName ... . |
none |
MESSAGES_HAVE_SENTENCE_COMMENTS _EXCEPT_REQUEST_RESPONSE_TYPES |
Verifies that all non-extended messages except for request and response types have a comment that contains at least one complete sentence. | uber2 |
MESSAGES_NOT_EMPTY _EXCEPT_REQUEST_RESPONSE_TYPES |
Verifies that all messages except for request and response types are not empty. | none |
NAMES_NO_COMMON | Verifies that no type name contains the word common because common has no semantic meaning, consider using a name that reflects the type instead. |
uber2 |
NAMES_NO_DATA | Verifies that no type name contains the word data because data is a decorator and all types on Protobuf are data. Consider merging this information into a higher-level type, or if you must have such a type, Use info instead. |
uber2 |
NAMES_NO_UUID | Verifies that no type name contains the word uuid because UUIDs in Protobuf are named ID instead of UUID. |
uber2 |
ONEOF_NAMES_LOWER_SNAKE_CASE | Verifies that all oneof names are lower_snake_case . |
uber1, uber2 |
PACKAGE_IS_DECLARED | Verifies that there is one and only one package declaration. |
uber1, uber2 |
PACKAGE_LOWER_CASE | Verifies that there is one and only one package declaration and that the package name only contains characters in the range a-z0-9 and periods. |
uber2 |
PACKAGE_LOWER_SNAKE_CASE | Verifies that there is one and only one package declaration and the package is lower_snake.case. |
uber1 |
PACKAGE_MAJOR_BETA_VERSIONED | Verifies that there is one and only one package declaration and the package is of the form package.vMAJORVERSION or package.vMAJORVERSIONbetaBETAVERSION with versions >=1. |
uber2 |
PACKAGE_NO_KEYWORDS | Verifies that no packages contain one of the following keywords as part of the name when split on "." : internal , public , private , protected , std . |
uber2 |
PACKAGES_SAME_IN_DIR | Verifies that the packages of all files in a directory are the same. | uber1, uber2 |
REQUEST_RESPONSE_NAMES_MATCH_RPC | Verifies that all request names are of the pattern RpcNameRequest and all response names are of the pattern RpcNameResponse . |
uber2 |
REQUEST_RESPONSE_TYPES_AFTER_SERVICE | Verifies that request and response types are defined after any services and the response type is defined after the request type. | uber2 |
REQUEST_RESPONSE_TYPES_IN_SAME_FILE | Verifies that all request and response types are in the same file as their corresponding service and are not nested messages. | uber1, uber2 |
REQUEST_RESPONSE_TYPES_ONLY_IN_FILE | Verifies that only request and response types are the only types in the same file as their corresponding service. | uber2 |
REQUEST_RESPONSE_TYPES_UNIQUE | Verifies that all request and response types are unique to each RPC. | uber1, uber2 |
RPC_NAMES_CAMEL_CASE | Verifies that all RPC names are CamelCase. | google, uber1, uber2 |
RPC_NAMES_CAPITALIZED | Verifies that all RPC names are Capitalized. | google, uber1, uber2 |
RPC_OPTIONS_NO_GOOGLE_API_HTTP | Verifies that the RPC option google.api.http is not used. |
none |
RPCS_HAVE_COMMENTS | Verifies that all rpcs have a comment of the form // RPCName ... . |
none |
RPCS_HAVE_SENTENCE_COMMENTS | Verifies that all rpcs have a comment that contains at least one complete sentence. | uber2 |
RPCS_NO_STREAMING | Verifies that all rpcs are unary. | none |
SERVICE_NAMES_API_SUFFIX | Verifies that all service names end with API . |
uber2 |
SERVICE_NAMES_CAMEL_CASE | Verifies that all service names are CamelCase. | google, uber1, uber2 |
SERVICE_NAMES_CAPITALIZED | Verifies that all service names are Capitalized. | google, uber1, uber2 |
SERVICE_NAMES_MATCH_FILE_NAME | Verifies that there is one service per file and the file name is of the pattern service_name_lower_snake_case.proto . |
uber2 |
SERVICE_NAMES_NO_PLURALS | Verifies that all CamelCase service names do not contain plural components. | none |
SERVICES_HAVE_COMMENTS | Verifies that all services have a comment of the form // ServiceName ... . |
none |
SERVICES_HAVE_SENTENCE_COMMENTS | Verifies that all services have a comment that contains at least one complete sentence. | uber2 |
SYNTAX_PROTO3 | Verifies that the syntax is proto3 . |
uber1, uber2 |
WKT_DIRECTLY_IMPORTED | Verifies that the Well-Known Types are directly imported using google/protobuf/ as the base of the import. |
uber1, uber2 |
WKT_DURATION_SUFFIX | Verifies that all field names of type google.protobuf.Duration are named duration or end in _duration . |
uber2 |
WKT_TIMESTAMP_SUFFIX | Verifies that all field names of type google.protobuf.Timestamp are named time or end in _time_ . |
uber2 |