Lint

March 31, 2020 ยท View on GitHub

Back to README.md

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 the uber2 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

Available Lint Rules

Following is a list of all lint rules provided by Prototool as well as what lint group they belong to (if applicable).

RuleDescriptionLint Group
COMMENTS_NO_C_STYLEVerifies that there are no /* C-style */ comments.uber1, uber2
COMMENTS_NO_INLINEVerifies that there are no inline comments.uber2
ENUM_FIELD_NAMES_UPPER_SNAKE_CASEVerifies that all enum field names are UPPER_SNAKE_CASE.google, uber1, uber2
ENUM_FIELD_NAMES_UPPERCASEVerifies that all enum field names are UPPERCASE.none
ENUM_FIELD_PREFIXES_EXCEPT_MESSAGEVerifies that all enum fields are prefixed with ENUM_NAME_.uber2
ENUM_FIELD_PREFIXESVerifies that all enum fields are prefixed with [NESTED_MESSAGE_NAME_]ENUM_NAME_.uber1
ENUM_FIELDS_HAVE_COMMENTSVerifies that all enum fields have a comment of the form // FIELD_NAME ....none
ENUM_FIELDS_HAVE_SENTENCE_COMMENTSVerifies that all enum fields have a comment that contains at least one complete sentence.none
ENUM_NAMES_CAMEL_CASEVerifies that all enum names are CamelCase.google, uber1, uber2
ENUM_NAMES_CAPITALIZEDVerifies that all enum names are Capitalized.google, uber1, uber2
ENUM_ZERO_VALUES_INVALID_EXCEPT_MESSAGEVerifies that all enum zero value names are ENUM_NAME_INVALID.uber2
ENUM_ZERO_VALUES_INVALIDVerifies that all enum zero value names are [NESTED_MESSAGE_NAME_]ENUM_NAME_INVALID.uber1
ENUMS_HAVE_COMMENTSVerifies that all enums have a comment of the form // EnumName ....none
ENUMS_HAVE_SENTENCE_COMMENTSVerifies that all enums have a comment that contains at least one complete sentence.uber2
ENUMS_NO_ALLOW_ALIASVerifies that no enums use the option allow_alias.uber1, uber2
FIELDS_NOT_RESERVEDVerifies that no message or enum has a reserved field.uber2
FILE_HEADERVerifies 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_CASEVerifies that the file name is lower_snake_case.proto.uber2
FILE_OPTIONS_EQUAL_CSHARP_NAMESPACE_CAPITALIZEDVerifies that the file option csharp_namespace is the capitalized version of the package.uber2
FILE_OPTIONS_EQUAL_GO_PACKAGE_PB_SUFFIXVerifies that the file option go_package is equal to $(basename PACKAGE)pb.uber1
FILE_OPTIONS_EQUAL_GO_PACKAGE_V2_SUFFIXVerifies 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_TRUEVerifies 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_PREFIXVerifies that the file option java_package is equal to com.PACKAGE.uber1
FILE_OPTIONS_EQUAL_JAVA_PACKAGE_PREFIXVerifies 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_ABBRVerifies that the file option objc_class_prefix is the abbreviated version of the package.uber2
FILE_OPTIONS_EQUAL_PHP_NAMESPACE_CAPITALIZEDVerifies that the file option "php_namespace" is the capitalized version of the package.uber2
FILE_OPTIONS_REQUIRE_CSHARP_NAMESPACEVerifies that the file option csharp_namespace is set.uber2
FILE_OPTIONS_REQUIRE_GO_PACKAGEVerifies that the file option go_package is set.uber1, uber2
FILE_OPTIONS_REQUIRE_JAVA_MULTIPLE_FILESVerifies that the file option java_multiple_files is set.uber1, uber2
FILE_OPTIONS_REQUIRE_JAVA_OUTER_CLASSNAMEVerifies that the file option java_outer_classname is set.uber1, uber2
FILE_OPTIONS_REQUIRE_JAVA_PACKAGEVerifies that the file option java_package is set.uber1, uber2
FILE_OPTIONS_REQUIRE_OBJC_CLASS_PREFIXVerifies that the file option objc_class_prefix is set.uber2
FILE_OPTIONS_REQUIRE_PHP_NAMESPACEVerifies that the file option php_namespace is set.uber2
FILE_OPTIONS_REQUIRE_RUBY_PACKAGEVerifies that the file option ruby_package is set.none
FILE_OPTIONS_CSHARP_NAMESPACE_SAME_IN_DIRVerifies that the file option csharp_namespace of all files in a directory are the same.uber2
FILE_OPTIONS_GO_PACKAGE_SAME_IN_DIRVerifies that the file option go_package of all files in a directory are the same.uber1, uber2
FILE_OPTIONS_JAVA_MULTIPLE_FILES_SAME_IN_DIRVerifies that the file option java_multiple_files of all files in a directory are the same.uber1, uber2
FILE_OPTIONS_JAVA_PACKAGE_SAME_IN_DIRVerifies that the file option java_package of all files in a directory are the same.uber1, uber2
FILE_OPTIONS_OBJC_CLASS_PREFIX_SAME_IN_DIRVerifies that the file option objc_class_prefix of all files in a directory are the same.uber2
FILE_OPTIONS_PHP_NAMESPACE_SAME_IN_DIRVerifies that the file option php_namespace of all files in a directory are the same.uber2
FILE_OPTIONS_UNSET_JAVA_MULTIPLE_FILESVerifies that the file option java_multiple_files is unset.none
FILE_OPTIONS_UNSET_JAVA_OUTER_CLASSNAMEVerifies that the file option java_outer_classname is unset.none
FILE_OPTIONS_GO_PACKAGE_NOT_LONG_FORMVerifies that the file option go_package is not of the form go/import/path;package.uber1, uber2
GOGO_NOT_IMPORTEDVerifies that the gogo.proto file from gogo/protobuf is not imported.none
IMPORTS_NOT_PUBLICVerifies that there are no public imports.uber2
IMPORTS_NOT_WEAKVerifies that there are no weak imports.uber2
MESSAGE_FIELD_NAMES_FILENAMEVerifies that all message field names do not contain file_name as filename should be used instead.uber2
MESSAGE_FIELD_NAMES_FILEPATHVerifies that all message field names do not contain file_path as filepath should be used instead.uber2
MESSAGE_FIELD_NAMES_LOWER_SNAKE_CASEVerifies that all message field names are lower_snake_case.google, uber1, uber2
MESSAGE_FIELD_NAMES_LOWERCASEVerifies that all message field names are lowercase.none
MESSAGE_FIELD_NAMES_NO_DESCRIPTORVerifies that all message field names are not descriptor, which results in a collision in Java-generated code.uber2
MESSAGE_FIELDS_DURATIONVerifies that all non-map fields that contain "duration" in their name are of type google.protobuf.Duration.none
MESSAGE_FIELDS_HAVE_COMMENTSVerifies that all message fields have a comment of the form // field_name ....none
MESSAGE_FIELDS_HAVE_SENTENCE_COMMENTSVerifies that all message fields have a comment that contains at least one complete sentence.none
MESSAGE_FIELDS_NO_JSON_NAMEVerifies that no message field has the json_name option set.uber2
MESSAGE_FIELDS_NOT_FLOATSVerifies that all message fields are not floats.none
MESSAGE_FIELDS_TIMEVerifies that all non-map fields that contain time in their name are of type google.protobuf.Timestamp.none
MESSAGE_NAMES_CAMEL_CASEVerifies that all non-extended message names are CamelCase.google, uber1, uber2
MESSAGE_NAMES_CAPITALIZEDVerifies 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_COMMENTSVerifies 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_COMMONVerifies 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_DATAVerifies 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_UUIDVerifies that no type name contains the word uuid because UUIDs in Protobuf are named ID instead of UUID.uber2
ONEOF_NAMES_LOWER_SNAKE_CASEVerifies that all oneof names are lower_snake_case.uber1, uber2
PACKAGE_IS_DECLAREDVerifies that there is one and only one package declaration.uber1, uber2
PACKAGE_LOWER_CASEVerifies 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_CASEVerifies that there is one and only one package declaration and the package is lower_snake.case.uber1
PACKAGE_MAJOR_BETA_VERSIONEDVerifies 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_KEYWORDSVerifies 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_DIRVerifies that the packages of all files in a directory are the same.uber1, uber2
REQUEST_RESPONSE_NAMES_MATCH_RPCVerifies that all request names are of the pattern RpcNameRequest and all response names are of the pattern RpcNameResponse.uber2
REQUEST_RESPONSE_TYPES_AFTER_SERVICEVerifies 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_FILEVerifies 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_FILEVerifies that only request and response types are the only types in the same file as their corresponding service.uber2
REQUEST_RESPONSE_TYPES_UNIQUEVerifies that all request and response types are unique to each RPC.uber1, uber2
RPC_NAMES_CAMEL_CASEVerifies that all RPC names are CamelCase.google, uber1, uber2
RPC_NAMES_CAPITALIZEDVerifies that all RPC names are Capitalized.google, uber1, uber2
RPC_OPTIONS_NO_GOOGLE_API_HTTPVerifies that the RPC option google.api.http is not used.none
RPCS_HAVE_COMMENTSVerifies that all rpcs have a comment of the form // RPCName ....none
RPCS_HAVE_SENTENCE_COMMENTSVerifies that all rpcs have a comment that contains at least one complete sentence.uber2
RPCS_NO_STREAMINGVerifies that all rpcs are unary.none
SERVICE_NAMES_API_SUFFIXVerifies that all service names end with API.uber2
SERVICE_NAMES_CAMEL_CASEVerifies that all service names are CamelCase.google, uber1, uber2
SERVICE_NAMES_CAPITALIZEDVerifies that all service names are Capitalized.google, uber1, uber2
SERVICE_NAMES_MATCH_FILE_NAMEVerifies 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_PLURALSVerifies that all CamelCase service names do not contain plural components.none
SERVICES_HAVE_COMMENTSVerifies that all services have a comment of the form // ServiceName ....none
SERVICES_HAVE_SENTENCE_COMMENTSVerifies that all services have a comment that contains at least one complete sentence.uber2
SYNTAX_PROTO3Verifies that the syntax is proto3.uber1, uber2
WKT_DIRECTLY_IMPORTEDVerifies that the Well-Known Types are directly imported using google/protobuf/ as the base of the import.uber1, uber2
WKT_DURATION_SUFFIXVerifies that all field names of type google.protobuf.Duration are named duration or end in _duration.uber2
WKT_TIMESTAMP_SUFFIXVerifies that all field names of type google.protobuf.Timestamp are named time or end in _time_.uber2