Code Monkey home page Code Monkey logo

protolint's Introduction

protolint

Action Release Go Report Card License Docker

protolint is the pluggable linting/fixing utility for Protocol Buffer files (proto2+proto3):

  • Runs fast because this works without compiler.
  • Easy to follow the official style guide. The rules and the style guide correspond to each other exactly.
    • Fixer automatically fixes all the possible official style guide violations.
  • Allows to disable rules with a comment in a Protocol Buffer file.
    • It is useful for projects which must keep API compatibility while enforce the style guide as much as possible.
    • Some rules can be automatically disabled by inserting comments to the spotted violations.
  • Loads plugins to contain your custom lint rules.
  • Undergone testing for all rules.
  • Many integration supports.
    • protoc plugin
    • Editor integration
    • GitHub Action
    • CI Integration

Demo

For example, vim-protolint works like the following.

demo

Installation

Via Homebrew

protolint can be installed for Mac or Linux using Homebrew via the yoheimuta/protolint tap.

brew tap yoheimuta/protolint
brew install protolint

Since homebrew-core includes protolint, you can also install it by just brew install protolint. This is the default tap that is installed by default. It's easier, but not maintained by the same author. To keep it updated, I recommend you run brew tap yoheimuta/protolint first.

Via GitHub Releases

You can also download a pre-built binary from this release page:

In the downloads section of each release, you can find pre-built binaries in .tar.gz packages.

Use the maintained Docker image

protolint ships a Docker image yoheimuta/protolint that allows you to use protolint as part of your Docker workflow.

❯❯❯ docker run --volume "$(pwd):/workspace" --workdir /workspace yoheimuta/protolint lint _example/proto
[_example/proto/invalidFileName.proto:1:1] File name should be lower_snake_case.proto.
[_example/proto/issue_88/oneof_options.proto:11:5] Found an incorrect indentation style "    ". "  " is correct.
[_example/proto/issue_88/oneof_options.proto:12:5] Found an incorrect indentation style "    ". "  " is correct.

From Source

The binary can be installed from source if Go is available. However, I recommend using one of the pre-built binaries instead because it doesn't include the version info.

go install github.com/yoheimuta/protolint/cmd/protolint@latest

Within JavaScript / TypeScript

You can use protolint using your nodejs package manager like npm or yarn.

$ npm install protolint --save-dev

This will add a reference to a development dependency to your local package.json.

During install, the install.mjs script will be called. It will download the matching protolint from github. Just like @electron/get, you can bypass the download using the following environment variables:

Environment Variable Default value Description
PROTOLINT_MIRROR_HOST https://github.com HTTP/Web server base url hosting the binaries
PROTOLINT_MIRROR_REMOTE_PATH yoheimuta/protolint/download/releases Path to the archives on the remote host
PROTOLINT_MIRROR_USERNAME HTTP Basic auth user name
PROTOLINT_MIRROR_PASSWORD HTTP Basic auth password
PROTOLINT_PROXY HTTP(S) Proxy with optional auth data

Within the remote path, the archives from the releases page must be mirrored.

After that, you can use npx protolint (with all supplied protolint arguments) within your dev-scripts.

{
  ...
  "scripts": {
    "protoc": "....",
    "preprotoc": "npx protolint"
  },
  ...
}

You can add a protolint node to your package.json which may contain the content of protolint.yml below the lint node, i.e. the root element of the configuration will be protolint.

If you want to get an output that matches the TSC compiler, use reporter tsc.

Within Python projects

You can use protolint as a linter within your python projects, the wheel protolint-bin on pypi contains the pre-compiled binaries for various platforms. Just add the desired version to your pyproject.toml or requirements.txt.

The wheels downloaded will contain the compiled go binaries for protolint and protoc-gen-protolint. Your platform must be compatible with the supported binary platforms.

You can add the linter configuration to the tools.protolint package in pyproject.toml.

Usage

protolint lint example.proto example2.proto # file mode, specify multiple specific files
protolint lint .                            # directory mode, search for all .proto files recursively
protolint .                                 # same as "protolint lint ."
protolint lint -config_path=path/to/your_protolint.yaml . # use path/to/your_protolint.yaml
protolint lint -config_dir_path=path/to .   # search path/to for .protolint.yaml
protolint lint -fix .                       # automatically fix some of the problems reported by some rules
protolint lint -fix -auto_disable=next .    # this is preferable when you want to fix problems while maintaining the compatibility. Automatically fix some problems and insert disable comments to the other problems. The available values are next and this.
protolint lint -auto_disable=next .         # automatically insert disable comments to the other problems. 
protolint lint -v .                         # with verbose output to investigate the parsing error
protolint lint -no-error-on-unmatched-pattern . # exits with success code even if no file is found (file & directory mode)
protolint lint -reporter junit .            # output results in JUnit XML format
protolint lint -output_file=path/to/out.txt # output results to path/to/out.txt
protolint lint -plugin ./my_custom_rule1 -plugin ./my_custom_rule2 .   # run custom lint rules.
protolint list                              # list all current lint rules being used
protolint version                           # print protolint version

protolint does not require configuration by default, for the majority of projects it should work out of the box.

Version Control Integration

protolint is available as a pre-commit hook. Add this to your .pre-commit-config.yaml in your repository to run protolint with Go:

repos:
  - repo: https://github.com/yoheimuta/protolint
    rev: <version> # Select a release here like v0.44.0
    hooks:
      - id: protolint

or alternatively use this to run protolint with Docker:

repos:
  - repo: https://github.com/yoheimuta/protolint
    rev: <version> # Select a release here like v0.44.0
    hooks:
      - id: protolint-docker

Editor Integration

Visual Studio Code

JetBrains IntelliJ IDEA, GoLand, WebStorm, PHPStorm, PyCharm...

Vim(ALE engine)

Vim(Syntastic)

GitHub Action

A GitHub Action to run protolint in your workflows

CI Integration

Jenkins Plugins

Environment specific output

It is possible to format your linting according to the formatting of the CI/CD environment. The environment must be set using the output format. Currently, the following output is realized:

Environment Command Line Value Description Example
Github Actions ci-gh Github Help ::warning file=example.proto,line=10,col=20,title=ENUM_NAMES_UPPER_CAMEL_CASE::EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES
Azure DevOps ci-az Azure DevOps Help ##vso[task.logissue type=warning;sourcepath=example.proto;linenumber=10;columnnumber=20;code=ENUM_NAMES_UPPER_CAMEL_CASE;]EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES
Gitlab CI/CD ci-glab Reverse Engineered from Examples WARNING: ENUM_NAMES_UPPER_CAMEL_CASE example.proto(10,20) : EnumField name \"SECOND.VALUE\" must be CAPITALS_WITH_UNDERSCORES

You can also use the generic ci formatter, which will create a generic problem matcher.

With the ci-env value, you can specify the template from the following environment variables:

Environment Variable Priority Meaning
PROTOLINT_CIREPORTER_TEMPLATE_STRING 1 String containing a Go-template
PROTOLINT_CIREPORTER_TEMPLATE_FILE 2 Path to a file containing a Go-template

The resulting line-feed must not be added, as it will be added automatically.

The following fields are available:

Severity : The severity as string (either note, warning or error)

File : Path to the file containing the error

Line : Line within the file containing the error (starting position)

Column : Column within the file containing the error (starting position)

Rule : The name of the rule that is faulting

Message : The error message that descibes the error

Producing an output file and an CI/CD Error stream

You can create a specific output matching your CI/CD environment and also create an output file, e.g. for your static code analysis tools like github CodeQL or SonarQube.

This can be done by adding the --add-reporter flag. Please note, that the value must be formatted <reporter-name>:<output-file-path> (omitting < and >).

$ protolint --reporter ci-gh --add-reporter sarif:/path/to/my/output.sarif.json proto/*.proto

Use as a protoc plugin

protolint also maintains a binary protoc-gen-protolint that performs the lint functionality as a protoc plugin. See cmd/protoc-gen-protolint/README.md in detail.

This is useful in situations where you already have a protoc plugin workflow.

Call from Go code

You can also use protolint from Go code. See Go Documentation and lib/lint_test.go in detail.

args := []string{"-config_path", "path/to/your_protolint.yaml", "."}
var stdout bytes.Buffer
var stderr bytes.Buffer

err := lib.Lint(test.inputArgs, &stdout, &stderr)

Rules

See internal/addon/rules in detail.

The rule set follows:

  • Official Style Guide. This is enabled by default. Basically, these rules can fix the violations by appending -fix option.
  • Unofficial Style Guide. This is disabled by default. You can enable each rule with .protolint.yaml.

The -fix option on the command line can automatically fix all the problems reported by fixable rules. See Fixable columns below.

The -auto_disable option on the command line can automatically disable all the problems reported by auto-disable rules. This feature is helpful when fixing the existing violations breaks the compatibility. See AutoDisable columns below.

  • *1: These rules are not supposed to support AutoDisable because the fixes don't break their compatibilities. You should run the protolint with -fix.
Official Fixable AutoDisable ID Purpose
Yes ENUM_FIELD_NAMES_PREFIX Verifies that enum field names are prefixed with its ENUM_NAME_UPPER_SNAKE_CASE.
Yes ENUM_FIELD_NAMES_UPPER_SNAKE_CASE Verifies that all enum field names are CAPITALS_WITH_UNDERSCORES.
Yes ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH Verifies that the zero value enum should have the suffix (e.g. "UNSPECIFIED", "INVALID"). The default is "UNSPECIFIED". You can configure the specific suffix with .protolint.yaml.
Yes ENUM_NAMES_UPPER_CAMEL_CASE Verifies that all enum names are CamelCase (with an initial capital).
Yes *1 FILE_NAMES_LOWER_SNAKE_CASE Verifies that all file names are lower_snake_case.proto. You can configure the excluded files with .protolint.yaml.
Yes FIELD_NAMES_LOWER_SNAKE_CASE Verifies that all field names are underscore_separated_names.
Yes *1 IMPORTS_SORTED Verifies that all imports are sorted.
Yes MESSAGE_NAMES_UPPER_CAMEL_CASE Verifies that all message names are CamelCase (with an initial capital).
Yes *1 ORDER Verifies that all files should be ordered in the specific manner.
Yes *1 PACKAGE_NAME_LOWER_CASE Verifies that the package name should only contain lowercase letters.
Yes RPC_NAMES_UPPER_CAMEL_CASE Verifies that all rpc names are CamelCase (with an initial capital).
Yes SERVICE_NAMES_UPPER_CAMEL_CASE Verifies that all service names are CamelCase (with an initial capital).
Yes REPEATED_FIELD_NAMES_PLURALIZED Verifies that repeated field names are pluralized names.
Yes *1 QUOTE_CONSISTENT Verifies that the use of quote for strings is consistent. The default is double quoted. You can configure the specific quote with .protolint.yaml.
Yes *1 INDENT Enforces a consistent indentation style. The default style is 2 spaces. Inserting appropriate new lines is also forced by default. You can configure the detail with .protolint.yaml.
Yes *1 PROTO3_FIELDS_AVOID_REQUIRED Verifies that all fields should avoid required for proto3.
Yes _ PROTO3_GROUPS_AVOID Verifies that all groups should be avoided for proto3.
Yes _ *1 MAX_LINE_LENGTH Enforces a maximum line length. The length of a line is defined as the number of Unicode characters in the line. The default is 80 characters. You can configure the detail with .protolint.yaml.
No _ - SERVICE_NAMES_END_WITH Enforces a consistent suffix for service names. You can configure the specific suffix with .protolint.yaml.
No _ - FIELD_NAMES_EXCLUDE_PREPOSITIONS Verifies that all field names don't include prepositions (e.g. "for", "during", "at"). You can configure the specific prepositions and excluded keywords with .protolint.yaml.
No _ - MESSAGE_NAMES_EXCLUDE_PREPOSITIONS Verifies that all message names don't include prepositions (e.g. "With", "For"). You can configure the specific prepositions and excluded keywords with .protolint.yaml.
No _ - RPC_NAMES_CASE Verifies that all rpc names conform to the specified convention. You need to configure the specific convention with .protolint.yaml.
No _ - MESSAGES_HAVE_COMMENT Verifies that all messages have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - SERVICES_HAVE_COMMENT Verifies that all services have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - RPCS_HAVE_COMMENT Verifies that all rps have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - FIELDS_HAVE_COMMENT Verifies that all fields have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - ENUMS_HAVE_COMMENT Verifies that all enums have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - ENUM_FIELDS_HAVE_COMMENT Verifies that all enum fields have a comment. You can configure to enforce Golang Style comments with .protolint.yaml.
No _ - FILE_HAS_COMMENT Verifies that a file starts with a doc comment.
No _ - SYNTAX_CONSISTENT Verifies that syntax is a specified version. The default is proto3. You can configure the version with .protolint.yaml.

I recommend that you add all_default: true in .protolint.yaml, because all linters above are automatically enabled so that you can always enjoy maximum benefits whenever protolint is updated.

Here are some examples that show good style enabled by default. - is a bad style, + is a good style:

ENUM_FIELD_NAMES_PREFIX

enum FooBar {
-  UNSPECIFIED = 0;
+  FOO_BAR_UNSPECIFIED = 0;
}

ENUM_FIELD_NAMES_UPPER_SNAKE_CASE

enum Foo {
-  firstValue = 0;
+  FIRST_VALUE = 0;
-  second_value = 1;
+  SECOND_VALUE = 1;
}

ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH

enum Foo {
-  FOO_FIRST = 0;
+  FOO_UNSPECIFIED = 0;
}

ENUM_NAMES_UPPER_CAMEL_CASE

- enum foobar {
+ enum FooBar {
  FIRST_VALUE = 0;
  SECOND_VALUE = 1;
}

FIELD_NAMES_LOWER_SNAKE_CASE

message SongServerRequest {
-  required string SongName = 1;
+  required string song_name = 1;
}

IMPORTS_SORTED

- import public "new.proto";
+ import "myproject/other_protos.proto";
- import "myproject/other_protos.proto";
+ import public "new.proto";

import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

MESSAGE_NAMES_UPPER_CAMEL_CASE

- message song_server_request {
+ message SongServerRequest {
  required string SongName = 1;
  required string song_name = 1;
}

ORDER

- option java_package = "com.example.foo";
- syntax = "proto3";
- package examplePb;
- message song_server_request { }
- import "other.proto";
+ syntax = "proto3";
+ package examplePb;
+ import "other.proto";
+ option java_package = "com.example.foo";
+ message song_server_request { }

PACKAGE_NAME_LOWER_CASE

- package myPackage
+ package my.package

RPC_NAMES_UPPER_CAMEL_CASE

service FooService {
-  rpc get_something(FooRequest) returns (FooResponse);
+  rpc GetSomething(FooRequest) returns (FooResponse);
}

RPC_NAMES_UPPER_CAMEL_CASE

- service foo_service {
+ service FooService {
  rpc get_something(FooRequest) returns (FooResponse);
  rpc GetSomething(FooRequest) returns (FooResponse);
}

REPEATED_FIELD_NAMES_PLURALIZED

-  repeated string song_name = 1;
+  repeated string song_names = 1;

INDENT

 enum enumAllowingAlias {
   UNKNOWN = 0;
-        option allow_alias = true;
+  option allow_alias = true;
   STARTED = 1;
-     RUNNING = 2 [(custom_option) = "hello world"];
+  RUNNING = 2 [(custom_option) = "hello world"];
- }
+}
-   message TestMessage { string test_field = 1; }
+ message TestMessage {
+  string test_field = 1;
+}

QUOTE_CONSISTENT

 option java_package = "com.example.foo";
- option go_package = 'example';
+ option go_package = "example";

Creating your custom rules

protolint is the pluggable linter so that you can freely create custom lint rules.

A complete sample project (aka plugin) is included in this repo under the _example/plugin directory.

Reporters

protolint comes with several built-in reporters(aka. formatters) to control the appearance of the linting results.

You can specify a reporter using the -reporter flag on the command line. For example, -reporter junit uses the junit reporter.

The built-in reporter options are:

  • plain (default)
  • junit
  • json
  • sarif
  • sonar (SonarQube generic issue format)
  • unix
  • tsc (compatible to TypeScript compiler)

Configuring

Disable rules in a Protocol Buffer file

Rules can be disabled with a comment inside a Protocol Buffer file with the following format. The rules will be disabled until the end of the file or until the linter sees a matching enable comment:

// protolint:disable <ruleID1> [<ruleID2> <ruleID3>...]
...
// protolint:enable <ruleID1> [<ruleID2> <ruleID3>...]

It's also possible to modify a disable command by appending :next or :this for only applying the command to this(current) or the next line respectively.

For example:

enum Foo {
  // protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
  firstValue = 0;    // no error
  second_value = 1;  // protolint:disable:this ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
  THIRD_VALUE = 2;   // spits out an error
}

Setting the command-line option -auto_disable to next or this inserts disable commands whenever spotting problems.

You can specify -fix option together. The rules supporting auto_disable suppress the violations instead of fixing them that cause a schema incompatibility.

Config file

protolint can operate using a config file named .protolint.yaml.

Refer to _example/config/.protolint.yaml for the config file specification.

protolint will automatically search a current working directory for the config file by default and successive parent directories all the way up to the root directory of the filesystem. And it can search the specified directory with -config_dir_path flag. It can also search the specified file with --config_path flag.

Exit codes

When linting files, protolint will exit with one of the following exit codes:

  • 0: Linting was successful and there are no linting errors.
  • 1: Linting was successful and there is at least one linting error.
  • 2: Linting was unsuccessful due to all other errors, such as parsing, internal, and runtime errors.

Motivation

There exists the similar protobuf linters as of 2018/12/20.

One is a plug-in for Google's Protocol Buffers compiler.

  • When you just want to lint the files, it may be tedious to create the compilation environment.
  • And it generally takes a lot of time to compile the files than to parse the files.

Other is a command line tool which also lints Protocol Buffer files.

  • While it has a lot of features other than lint, it seems cumbersome for users who just want the linter.
  • The lint rule slants towards to be opinionated.
  • Further more, the rule set and the official style guide don't correspond to each other exactly. It requires to understand both rules and the guide in detail, and then to combine the rules accurately.

Other tools

I wrote an article comparing various Protocol Buffer Linters, including protolint, on 2019/12/17.

Dependencies

License

The MIT License (MIT)

Acknowledgement

Thank you to the prototool package: https://github.com/uber/prototool

I referred to the package for the good proven design, interface and some source code.

protolint's People

Contributors

acgreek avatar benmathews avatar carstencodes avatar davidjlynn avatar dependabot[bot] avatar hf-kklein avatar jd1378 avatar jedevc avatar jpreese avatar paulsonoflars avatar perrydunn avatar tetienne avatar wwuck avatar yoheimuta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

protolint's Issues

Protolint linting Proto files in node_modules

Environment

  • MacOS catalina

  • Protolint Version 0.28.2

When I run protolint lint . I get a very weird error

found "\"package\"(Token=30, Pos=node_modules/grpc/node_modules/protobufjs/examples/protoify/json.proto:2:1)" but expected [syntax] at /go/pkg/mod/github.com/yoheimuta/go-protoparser/[email protected]/parser/syntax.go:59. Use -v for more details

Please I have been having this issues for some days now, I have tried ignoring the node_modules folder entirely in my protolint.yml config file but to no success.

Please help!

PS

This is the content of my protolint.yml file

lint:
  files:
    exclude:
      - node_modules/grpc/node_modules/protobufjs/examples/protoify/json.proto
  directories:
    # The specific directories to exclude.
    exclude:
      # NOTE: UNIX paths will be properly accepted by both UNIX and Windows.
      # - identity-service-proto/node_modules/
      - node_modules/
  rules:
    no_default: false # we can choose if we want the default linting in addition to our custom lintings too
    all_default: false

    add:
      # - MESSAGE_NAMES_UPPER_CAMEL_CASE
      # - SERVICE_NAMES_UPPER_CAMEL_CASE
      # - MESSAGES_HAVE_COMMENT
      # - REPEATED_FIELD_NAMES_PLURALIZED
      # - SERVICES_HAVE_COMMENT
      # - ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
      # - ENUM_NAMES_UPPER_CAMEL_CASE
      # - FIELD_NAMES_LOWER_SNAKE_CASE
      # - PACKAGE_NAME_LOWER_CASE
      # - RPC_NAMES_UPPER_CAMEL_CASE

    remove:
      - MAX_LINE_LENGTH
      - MESSAGES_HAVE_COMMENT

False-positive by indent rule

Maybe related to fixing #66 I encountered a false-positive of the indent rule in the newest version 0.16.0 which is reported if the last rpc method of a service is having a statement block:

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse) {}
} // -> Found an incorrect indentation style "". "  " is correct.

In my case the rpc statement block is containing an option, but it's not needed to get the issue.

Protolint doesn't work with -plugin in windows

I have a following proto file:

syntax = "proto3";
// A broken example of the official reference
// See https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#proto_file
package examplePb;

option java_package = "com.example.foo";

import "other.proto";
import public "new.proto";

import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

import "myproject/other_protos.proto";
import "myproject/main_protos.proto";

enum enumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 2 [(custom_option) = "hello world"];
}
message outer {
  option (my_option).a = true;
  // inner is an inner message.
  message innerAAA {   // Level 2
    int64 ival = 1;
  }
  repeated inner inner_message = 2;
  EnumAllowingAlias enum_field =3;
  map<int32, string> my_map = 4;
  string reason_for_error = 5;
  string  end_of_support_version= 6;
  message AccountForAdmin {}
  message SpecialEndOfSupport {}
  required inner inner_message = 7;
  group Result = 8 {
    string url = 9;
  }
  repeated group Result = 10 {
  }
  repeated inner paper = 11;
  repeated group Regular = 12 {
  }
}
service SearchApi {
  rpc search (SearchRequest) returns (SearchResponse) {};
};

So I create a custom linter rule custumrule/enumrule.go:

package custumrule

import (
	"github.com/yoheimuta/go-protoparser/v4/parser"
	"github.com/yoheimuta/protolint/linter/report"
	"github.com/yoheimuta/protolint/linter/strs"
	"github.com/yoheimuta/protolint/linter/visitor"
)

// EnumNamesLowerSnakeCaseRule verifies that all enum names are LowerSnakeCase.
type EnumNamesLowerSnakeCaseRule struct{}

// NewEnumNamesLowerSnakeCaseRule creates a new EnumNamesLowerSnakeCaseRule.
func NewEnumNamesLowerSnakeCaseRule() EnumNamesLowerSnakeCaseRule {
	return EnumNamesLowerSnakeCaseRule{}
}

// ID returns the ID of this rule.
func (r EnumNamesLowerSnakeCaseRule) ID() string {
	return "ENUM_NAMES_LOWER_SNAKE_CASE"
}

// Purpose returns the purpose of this rule.
func (r EnumNamesLowerSnakeCaseRule) Purpose() string {
	return "Verifies that all enum names are LowerSnakeCase."
}

// IsOfficial decides whether or not this rule belongs to the official guide.
func (r EnumNamesLowerSnakeCaseRule) IsOfficial() bool {
	return true
}

// Apply applies the rule to the proto.
func (r EnumNamesLowerSnakeCaseRule) Apply(proto *parser.Proto) ([]report.Failure, error) {
	v := &enumNamesLowerSnakeCaseVisitor{
		BaseAddVisitor: visitor.NewBaseAddVisitor(r.ID()),
	}
	return visitor.RunVisitor(v, proto, r.ID())
}

type enumNamesLowerSnakeCaseVisitor struct {
	*visitor.BaseAddVisitor
}

// VisitEnum checks the enum field.
func (v *enumNamesLowerSnakeCaseVisitor) VisitEnum(e *parser.Enum) bool {
	if !strs.IsLowerSnakeCase(e.EnumName) {
		v.AddFailuref(e.Meta.Pos, "Enum name %q must be underscore_separated_names", e.EnumName)
	}
	return false
}

and main.go:

package main

import (
	"example.com/m/custumrule"
	"github.com/yoheimuta/protolint/plugin"
)

func main() {
	plugin.RegisterCustomRules(
		custumrule.NewEnumNamesLowerSnakeCaseRule(),
	)
}

I build project on windows and Linux subsystem by go buil main.go and try to run the command: protolint -plugin ./main.exe .. However, I receive failed client.Client(), err=exec: "sh": executable file not found in %PATH%

found "extend" but expected [;]

Found this while running protolint on https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto

protolint -v ./annotations.proto results:

...
2019/06/12 14:13:29 [DEBUG] Text=[;], Token=[8], Pos=[proto/googleapis/google/api/annotations.proto:26:34] called from option.go:80                                                                      
2019/06/12 14:13:29 [DEBUG] Text=[extend], Token=[2], Pos=[proto/googleapis/google/api/annotations.proto:28:1] called from lexer.go:127                                                                  
2019/06/12 14:13:29 [DEBUG] Text=[extend], Token=[2], Pos=[proto/googleapis/google/api/annotations.proto:28:1] called from lexer.go:127                                                                  
2019/06/12 14:13:29 [DEBUG] Text=[extend], Token=[2], Pos=[proto/googleapis/google/api/annotations.proto:28:1] called from parser.go:44                                                                  
2019/06/12 14:13:29 [DEBUG] Text=[extend], Token=[2], Pos=[proto/googleapis/google/api/annotations.proto:28:1] called from lexer.go:127                                                                  
2019/06/12 14:13:29 [DEBUG] Text=[extend], Token=[2], Pos=[proto/googleapis/google/api/annotations.proto:28:1] called from emptyStatement.go:10                                                          
found "extend" but expected [;] at /Users/alexander/go/src/github.com/yoheimuta/go-protoparser/internal/lexer/emptyStatement.go:16  

I think extend keyword is missing but could not find it in proto styleguide.

protolint:disable not working for enums

I´m trying to use the disable marker on some proto files but i cannot get it to work on ENUMS rules.
For example, this file:

syntax = "proto3";
// protolint:disable ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
// protolint:disable ENUM_FIELD_NAMES_UPPER_SNAKE_CASE

package test;

import "google/protobuf/wrappers.proto";

option java_package = "com.test.example";
option java_outer_classname = "InfoProto";

/**
 * Some enum
*/
enum Strategy {
  DEFAULT= 0;
  bY_LINE_ID = 1;
  NONE = 2;
}

will always output these errors:

➜  ~ protolint lint example.proto
[example.proto:17:3] EnumField name "bY_LINE_ID" must be CAPITALS_WITH_UNDERSCORES
[example.proto:16:3] EnumField name "DEFAULT" with zero value should have the suffix "UNSPECIFIED"

I´ve tried with just one disable and I get the same result. I´ve found that other rules do work (e.g. protolint:enable MAX_LINE_LENGTH)

Feature: unused imports lint/fixer

I am getting a warning from protoc but not from the linter (using the vscode extension).

For example:

warning: Import google/protobuf/wrappers.proto but not used.

protolint disable not working

I added the following to my proto file on the line before an 81 character long line (not really any good way to shorten the go_package option when you want to specify the full go package path).

// protolint:disable MAX_LINE_LENGTH

And yet I still get a linter error for the next line down:

[test.proto:8:1] The line length is 81, but it must be shorter than 80

Does protolint:disable not work with MAX_LINE_LENGTH? Or have I mucked up the syntax?

Parsing error when using nested values

The following proto raises errors around the "additional_bindings" line, even though this is a valid proto (another example here).

syntax = "proto3";

package test;

message Test {
}

service RunnerService {
    rpc StreamTest (Test) returns (stream Test) {
        option (google.api.http) = {
            get: "/v1/messages/{message_id}"
            additional_bindings {
                get: "/v1/users/{user_id}/messages/{message_id}"
            }
        };
    }
}

I have also seen the following syntax be used instead, but this also raises an error. However, this one seems to be thrown in the Lexer, rather than the Parser (as determined from the error message constants).

            additional_bindings: {
                get: "/v1/users/{user_id}/messages/{message_id}"
            };

PS: Sorry for the large amount of PRs/Issues raised 😅

Return with exit code 0 if no files are found

Hi,
I'm currently trying to use this on gitlab ci, and currently don't have any files to lint, but I think since the linter exits with non zero the job fails.
isn't it better to exit with 0 when there's nothing to lint ?
here is what I use on gitlab:

protobuf-lint:
  image: 
    name: yoheimuta/protolint:v0.25.1
    entrypoint: [""]
  stage: test
  script:
    - protolint lint -v -reporter junit -output_file=junit.xml .
  artifacts:
    reports:
      junit: junit.xml
    expire_in: 1 week

Feature: check for usage of reserved names

It would be great if a rule exists that prevents that the generated code for a specific programming language is invalid because of the use of reserved names for fields.

I had a field named "default" which produces errors when generating code for c/c++

Unexpected `ORDER` rule violation

Certain protos contain simple option assignments at the top of the file, before the imports. Linting these results in the following lint violation:
The order of Import is invalid. Check if the file is ordered in the correct manner.

These are usually used for language-specific generated code (java, python, go, csharp), to help specify optional importpaths.

My opinion is that there should be a toggle available to edit the ORDER setting, which allows simple option assignments to be placed before imports. This could be done in the following ways:

  • Through a global setting, where all simple top-level option assignments are placed before the import when using the --fix mode.
  • Through a whitelist system, where users can specify the specific option names to be placed at the top of the file.

Incorrect "invalid indentation style" error

Issue:

protolint reports an invalid indentation error, which breaks protos when using the --fix flag.

Expected outcome:

Valid proto output, as well as a correct error. This is not an indentation error, but a formatting error. The linter should be recommending the removal of the ; after the enum close.

Versions:

OS: macOS Mojave 10.14.6
protolint: 0.20.0 and 0.21.0

Steps to reproduce:

protolint lint test.proto where test.proto is:

syntax = "proto3";

package test;

message Test {
  enum State {
    UNKNOWN_STATE = 0;
    CLEAR = 1;
    PULLED = 2;
    STARTED = 3;
  };
}

message TestResponse {
}

service RunnerService {
  rpc ReceiveTest (Test) returns (TestResponse);
}

Protolint reports [test.proto:11:4] Found an incorrect indentation style " }". " " is correct., which breaks proto syntax.

Protolint substituting "message" with " sage"

I am running protolint version 0.26.1(84c57fd) running on macOS 11.0.1, and it's making this wrong transformation:

image

Looks like it's making a wrong transformation. To reporoduce the issue, save the below text into a .proto file:

syntax = "proto3";

package foobar;

option java_package = "com.test.foo.bar";

message TestMessage { string test_field = 1; }

Run:

protolint lint -v -fix test.proto

It prints the below output to console:

2020/12/04 21:05:04 [DEBUG] Text=[syntax], Token=[2], Pos=[test.proto:1:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[syntax], Token=[22], Pos=[test.proto:1:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[=], Token=[10], Pos=[test.proto:1:8] called from syntax.go:63
2020/12/04 21:05:04 [DEBUG] Text=["], Token=[11], Pos=[test.proto:1:10] called from syntax.go:68
2020/12/04 21:05:04 [DEBUG] Text=[proto3], Token=[2], Pos=[test.proto:1:11] called from syntax.go:73
2020/12/04 21:05:04 [DEBUG] Text=["], Token=[11], Pos=[test.proto:1:17] called from syntax.go:79
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:1:18] called from syntax.go:84
2020/12/04 21:05:04 [DEBUG] Text=[package], Token=[2], Pos=[test.proto:3:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[package], Token=[2], Pos=[test.proto:3:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[package], Token=[2], Pos=[test.proto:3:1] called from parser.go:44
2020/12/04 21:05:04 [DEBUG] Text=[package], Token=[29], Pos=[test.proto:3:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[package], Token=[29], Pos=[test.proto:3:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[foobar], Token=[2], Pos=[test.proto:3:9] called from fullIdent.go:8
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:3:15] called from fullIdent.go:15
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:3:15] called from package.go:55
2020/12/04 21:05:04 [DEBUG] Text=[option], Token=[2], Pos=[test.proto:5:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[option], Token=[2], Pos=[test.proto:5:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[option], Token=[2], Pos=[test.proto:5:1] called from parser.go:44
2020/12/04 21:05:04 [DEBUG] Text=[option], Token=[30], Pos=[test.proto:5:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[option], Token=[30], Pos=[test.proto:5:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[java_package], Token=[2], Pos=[test.proto:5:8] called from option.go:153
2020/12/04 21:05:04 [DEBUG] Text=[=], Token=[10], Pos=[test.proto:5:21] called from option.go:175
2020/12/04 21:05:04 [DEBUG] Text=[=], Token=[10], Pos=[test.proto:5:21] called from option.go:58
2020/12/04 21:05:04 [DEBUG] Text=["], Token=[11], Pos=[test.proto:5:23] called from lexer.go:154
2020/12/04 21:05:04 [DEBUG] Text=["com.test.foo.bar"], Token=[6], Pos=[test.proto:5:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:5:41] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:5:41] called from option.go:68
2020/12/04 21:05:04 [DEBUG] Text=[message], Token=[2], Pos=[test.proto:7:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[message], Token=[2], Pos=[test.proto:7:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[message], Token=[2], Pos=[test.proto:7:1] called from parser.go:44
2020/12/04 21:05:04 [DEBUG] Text=[message], Token=[26], Pos=[test.proto:7:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[message], Token=[26], Pos=[test.proto:7:1] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[TestMessage], Token=[2], Pos=[test.proto:7:9] called from message.go:76
2020/12/04 21:05:04 [DEBUG] Text=[{], Token=[14], Pos=[test.proto:7:21] called from message.go:106
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from message.go:114
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[string], Token=[2], Pos=[test.proto:7:23] called from field.go:210
2020/12/04 21:05:04 [DEBUG] Text=[test_field], Token=[2], Pos=[test.proto:7:30] called from field.go:79
2020/12/04 21:05:04 [DEBUG] Text=[=], Token=[10], Pos=[test.proto:7:41] called from field.go:85
2020/12/04 21:05:04 [DEBUG] Text=[1], Token=[3], Pos=[test.proto:7:43] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:7:44] called from field.go:119
2020/12/04 21:05:04 [DEBUG] Text=[;], Token=[8], Pos=[test.proto:7:44] called from field.go:100
2020/12/04 21:05:04 [DEBUG] Text=[}], Token=[15], Pos=[test.proto:7:46] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[}], Token=[15], Pos=[test.proto:7:46] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[}], Token=[15], Pos=[test.proto:7:46] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[}], Token=[15], Pos=[test.proto:7:46] called from message.go:151
2020/12/04 21:05:04 [DEBUG] Text=[], Token=[1], Pos=[test.proto:7:47] called from lexer.go:189
2020/12/04 21:05:04 [DEBUG] Text=[], Token=[1], Pos=[test.proto:7:47] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[], Token=[1], Pos=[test.proto:7:47] called from lexer.go:139
2020/12/04 21:05:04 [DEBUG] Text=[], Token=[1], Pos=[test.proto:7:47] called from parser.go:44
[test.proto:7:23] Found an incorrect indentation style "   ". "  " is correct.

Block followed by semicolon causing runtime error

Running protolint on one of my v3 proto files resulted in the following error:
panic: runtime error: invalid memory address or nil pointer dereference

After some tests the cause was found: the protolint parser doesn't accept a semicolon after a statement block in curly brackets, e.g. after service/rpc/method:
service LazyService {}; // Semicolon causing runtime error

According to the Protobuf language specification the semicolon is not allowed behind curly brackets, but the Protobuf generator does accept it. Moreover the error message is providing no hint about the cause (even if using the verbose flag). Therefore I'd recommend to adapt the parser to accept the semicolon even if not specified.

Output format not supported by CI tools

Hi, excellent tool, we're considering using it as part of our CI environment.

The one issue at have is that the output format is not supported by any of the jenkins plugins we use (and junit isn't really the right format for a linter to output).

I'm considering adding protolint parsers to the plugins we use (warnings-ng and any that use violatons lib), if you think that's OK? Unless there is a more common standard linting format that this tool could use?

Thanks

Field Types not validated?

I was looking for a protobuf schema validator (without creating language class files 🙃 ) and found this project.

I tried validation a simple message with unknown field types but it did not invalidate the file. Is there an option/config I missed?

Feature Request of Diff Feature

Thank you for developing a great tool. I recently started to use this tool because I want to use the fixer feature. However, I realized there's no way to check diff without actually fixing my files. Could you consider adding diff feature so I can go ahead applying the changes without worries?

swagger annotations break the protolinter

I have tried to run protolinter on the proto files with swagger annotations, but itseems to break the linter with below error:

found ""max_length"(Token=2, Pos=..\protos\test.proto:179:5)" but expected [}] at C:/Users/uname/go/src/github.com/yoheimuta/go-protoparser/parser/field.go:243:found "ma
x_length" but expected at C:/Users/uname/go/src/github.com/yoheimuta/go-protoparser/internal/lexer/emptyStatement.go:16

Below is a sample proto message with annotations:

message UserCredentials {
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = {
example: { value: '{ "email_id": "[email protected]", "password": "password@123"}' }
};
string email_id = 1[(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {
pattern: "^-!#$%&'*+\/0-9=?A-Z^_a-z{|}~@a-zA-Z0-9\.a-zA-Z+$"
max_length: 254
min_length: 1
description: "Enter user email"
}];
string password = 2[(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {
description: "Enter user password"
}];
}

Linter not working on directory with multiple proto folders

Hi, Tried to apply linter on a directory with multiple folders with proto files, no response from linter.

Example:
Main Directory->
-Folder1-> protofile1
-Folder2-> protofile2
-Folder3-> protofile3
It works when i remove folder2 and folder3. Let me know whether I missed something

Feature request: Improve parse error handling

Currently, the behaviour when a parsing error occurs is to fail with a message such as found "{" but expected [constant]. Use -v for more details and an exit code of 1. There is no indication of which file has failed without using the -v flag, which is incredibly verbose, and goes to stderr. The exit code of 1 also makes it hard to differentiate from a "normal" linting error.

For use in CI and integration with other linting tools, it would be nice if:

  • error codes differed based on error type; eg 1 for linting errors, 2 for parsing, 3 for (other relevant issue), etc. If both a lint and a parse error occurs, return the higher one. For extra complexity, use fancy binary logic, so 3 might be linting+parsing errors (01 + 10), in which case 4 would be the next relevant issue.
  • "failure to parse" reports the filename, even without -v; in a similar output to the current error parsing format.
  • "failure to parse" errors didn't stop the linter. It's fine if the linter can't handle the rest of the current file, but when linting an entire directory, there could be other files which have errors to be reported.

A stretch goal and dream for me would also be if you allowed multiple output modes; similar to what shellcheck offers, with json/gcc/diff options; but of course, thats no easy task, and so I'll just leave that as an afterthought. (EDIT: JSON added in #104)

Otherwise, thank you for the great tool!

Indentations in square brackets aren't fixed

The following proto does not get any indentation warnings raised, even though the values in the [] are all over the place. Is this intended?

syntax = "proto3";

message Foo {
  string bar = 1 [
                     // comment
               deprecated=true
                                 ];
}

Feature: accept "_" in package names (PACKAGE_NAME_LOWER_CASE)

Why rule named PACKAGE_NAME_LOWER_CASE additionally checks extraneous rules?

Verifies that the package name only contains lowercase letters [ digits and/or periods].

According to the https://developers.google.com/protocol-buffers/docs/style.

Package name should be in lowercase, and should correspond to the directory hierarchy. e.g., 
if a file is in my/package/, then the package name should be my.package.

We have directories like my/some-service and package my.some_service. But protolint prohibits it without proofs :(

P.S. I suggest renaming the rule to PACKAGE_NAME_FORMAT and let it be configured using the regex from the .protolint.yaml.

Seg fault with example plugin

I started to implement a custom rule within my own plugin but got a segmentation fault when trying to use it with protolint. To make sure that it is a problem of my own plugin I compiled your example plugin, but I got the same error.
My system:

  • Ubuntu 18.04.2
  • go 1.12.9 (linux/amd64)

My steps in detail:

  • Build and install protolint
  • Build example plugin: go build -buildmode=plugin
  • Make sure protolint and plugin.so have execution permission
  • Run protolint: protolint lint -v -plugin ./plugin.so .

Error output:

2019-08-30T13:43:46.993+0200 [DEBUG] plugin: starting plugin: path=/bin/sh args=[sh, -c, ./plugin.so]
2019-08-30T13:43:46.993+0200 [DEBUG] plugin: plugin started: path=/bin/sh pid=3763
2019-08-30T13:43:46.993+0200 [DEBUG] plugin: waiting for RPC address: path=/bin/sh
2019-08-30T13:43:47.131+0200 [DEBUG] plugin.sh: Segmentation fault (core dumped)
invalid value "./plugin.so" for flag -plugin: failed client.Client(), err=Unrecognized remote plugin message: 

This usually means that the plugin is either invalid or simply
needs to be recompiled to support the latest protocol.
Usage of lint:
  -config_dir_path string
    	path/to/protolint.yaml
  -fix
    	mode that the command line can automatically fix some of the problems
  -plugin value
    	plugins to provide custom lint rule set. Note that it's necessary to specify it as path format'
  -reporter value
    	formatter to output results in the specific format. Available reporters are "plain"(default) and "junit".
  -v	verbose output that includes parsing process details

Did I miss anything? Do you have an idea why I'm getting the segmentation fault?

Feature request: junit output

It would be nice if protolint could output it's error messages in JUnit xml format.

This would make it easy to integrate with Jenkins and other CI tools for reporting linting errors.

protolint: command not found

I installed the protolint in my macOS v10.15 with
go get -u -v github.com/yoheimuta/protolint/cmd/protolint

but after run protolint command in the terminal I received:
protolint: command not found

I also tried restart the terminal and mac but didn't work.

Any help?

Fix parsing on trailing commas

The trailing comma after the field2 value in this scenario breaks parsing.
This is a valid proto, and should not be the case.

    option (item) = {
      field: "data",
      field2: "data2",
    };

Command line argument for version info

It would be nice to have protolint output it's version string when outputting the help information.

Some possibilities:

protolint output could be amended to output version information.
protolint -version could output just the version information.
protolint -help could output the help/usage information and version information.

DEBUG log entries from plugin contained in JUnitReporter output

I have a protolint plugin with some custom defined rules. The protolint output should be written to a file. I use the following bash script to execute protolint (just a part of the script):

...
exec 2>$PROTOLINT_RESULT_FILE
protolint lint -plugin <custom plugin> -reporter junit .
...

After executing the script the $PROTOLINT_RESULT_FILE contains the following:

2019-10-07T09:35:25.066Z [DEBUG] plugin: starting plugin: path=/bin/sh args=[sh, -c, /usr/local/bin/kuka_rules]
2019-10-07T09:35:25.069Z [DEBUG] plugin: plugin started: path=/bin/sh pid=19
2019-10-07T09:35:25.069Z [DEBUG] plugin: waiting for RPC address: path=/bin/sh
2019-10-07T09:35:25.079Z [DEBUG] plugin.sh: plugin address: address=/tmp/plugin587097189 network=unix timestamp=2019-10-07T09:35:25.079Z
2019-10-07T09:35:25.081Z [DEBUG] plugin: using plugin: version=1
<?xml version="1.0" encoding="UTF-8"?>
  <testsuites>
      <testsuite tests="1" failures="0" time="0">
          <package>net.protolint</package>
          <testcase classname="net.protolint.ALL_RULES" name="All Rules" time="0"></testcase>
      </testsuite>
  </testsuites>

I saw that the plugin mechanism of protolint uses the hclog logger. Its default output is stderr. The jUnitReporter of protolint also uses stderr. Which collides in my opinion.

How can I get rid of the go-plugin DEBUG log output in the protolint result file?

Used versions:

  • protolint v0.17.0
  • Go 1.12

Do you have any idea?

Notice: vscode-protobuflint

Again, thanks for putting this project together.

As I mentioned in PR #24, the next logical step would be to get this into Visual Studio Code so that users can lint their .proto files as they type.

I put together a MVP here (https://github.com/jpreese/vscode-protobuflint). It uses your tool on the backend, and renders any linting errors to the editor.

I haven't cut a release to the marketplace yet, but just wanted to let you know it's in the works.

Linter fails on negative enum values

Given the following proto file:

syntax = "proto2";

enum TestNegativeValue {
  NEGATIVE_CONSTANT = -1;
  ZERO_CONSTANT = 0;
  POSITIVE_CONSTANT = 1;
}

protolint fails on the negative enum constant:

$ protolint negative_enum_val.proto 
found "\"-\"(Token=0, Pos=negative_enum_val.proto:4:23)" but expected [intLit] at /go/pkg/mod/github.com/yoheimuta/go-protoparser/[email protected]/parser/enum.go:242:found "-" but expected [;]. Use -v for more details

$ echo $?
2

Using the Linux 0.26.0 release protolint_0.26.0_Linux_x86_64.tar.gz

Not an issue for protoc. For now I'm excluding certain proto files that have negative enums in the protolint config file.

protolint.yaml: File paths are not uniformly handled in UNIX and Windows

When linting proto files in other than root folder, overrides to separate file paths must be written 2 times to ensure proper handling in both OS versions.

This variant is properly handled in UNIX only:

# Lint directives.
lint:
  # Linter files to ignore.
  ignores:
    - id: MESSAGE_NAMES_UPPER_CAMEL_CASE
      files:
        - ../proto/simple.proto

This variant is properly handled in Windows only:

# Lint directives.
lint:
  # Linter files to ignore.
  ignores:
    - id: MESSAGE_NAMES_UPPER_CAMEL_CASE
      files:
        - ..\proto\simple.proto

So to make it uniform one have to duplicate each path entry like this:

# Lint directives.
lint:
  # Linter files to ignore.
  ignores:
    - id: MESSAGE_NAMES_UPPER_CAMEL_CASE
      files:
        - ../proto/simple.proto
        - ..\proto\simple.proto

It would be great if UNIX paths will be properly accepted by Windows version of protolint.

Parsing breaks on empty fields within options within RPCs

Parsing breaks when including an option with an empty message within the option. The below snippet compiles fine in protoc, but doesn't parse with protolint.

syntax = "proto3";

service Foo {
  rpc Bar(Baz) returns (Baz) {
    option (opt) = {
      empty : {}
    };
  }
}

message Baz {}

Feature: rule verification

It would be nice if the linter was able to check whether a rule is defined or not.
This would likely have helped avoid #116, where rules seemed to be ignored because of trailing whitespace.

This should be used to check the rules specified in .add and .remove in the config, as well as protolint:disable directives.

Feature: Format command rewriting the file completely according to some rules

ref. #139 (comment)

vs. -fix option:

  • -fix option is so conservative that it doesn't force an opinionated format by following the explicitly specified rules with granular settings. While it's useful, sometimes it's too complicated to implement editing only a small fraction of the file content.
  • format command is supposed to be an opinionated feature so that it can fully rewrite the file content according to the choice of some predefined styles. It would be expected to complement the -fix option.

For example, I presume it results in easier support for the file structure guidelines.

Feature Request: accept long non-splittable texts with MAX_LINE_LENGTH

It'd be nice to have some exceptions for lines exceeding max line length (without explicit disable macros) since it's sometimes not feasible to split in proto files for cases like long URLs and import paths.

Just for reference, Google's C++ Style Guide and Java Style Guide both specify some exceptions and the same discussion would apply in protos although the Official Protocol Buffers Style Guide suggests to keep the line length without mentioning exceptions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.