Repository: uber/prototool
Pull request #315 (2019-01-29 23:18:38)

Add linter to check that file names are lower snake case
Re: #301.

codecov-io: # [Codecov](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=h1) Report
> Merging [#315](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=desc) into [dev](https://codecov.io/gh/uber/prototool/commit/eb1bad07113b02ec73cbbf12e02de38e9010fc17?src=pr&el=desc) will **increase** coverage by `0.06%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/uber/prototool/pull/315/graphs/tree.svg?width=650&token=A9V0DApqZg&height=150&src=pr)](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##              dev     #315      +/-   ##
==========================================
+ Coverage   73.09%   73.15%   +0.06%     
==========================================
  Files         137      138       +1     
  Lines        6195     6202       +7     
==========================================
+ Hits         4528     4537       +9     
+ Misses       1247     1246       -1     
+ Partials      420      419       -1
```


| [Impacted Files](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/lint/lint.go](https://codecov.io/gh/uber/prototool/pull/315/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9saW50Lmdv) | `71.01% <ø> (ø)` | :arrow_up: |
| [internal/lint/check\_file\_names\_lower\_snake\_case.go](https://codecov.io/gh/uber/prototool/pull/315/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9jaGVja19maWxlX25hbWVzX2xvd2VyX3NuYWtlX2Nhc2UuZ28=) | `100% <100%> (ø)` | |
| [...sentence\_comments\_except\_request\_response\_types.go](https://codecov.io/gh/uber/prototool/pull/315/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9jaGVja19tZXNzYWdlc19oYXZlX3NlbnRlbmNlX2NvbW1lbnRzX2V4Y2VwdF9yZXF1ZXN0X3Jlc3BvbnNlX3R5cGVzLmdv) | `85.71% <0%> (+5.71%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=footer). Last update [eb1bad0...8494a54](https://codecov.io/gh/uber/prototool/pull/315?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/internal/lint/BUILD.bazel b/internal/lint/BUILD.bazel
index 79619f0..4cd69c7 100644
--- a/internal/lint/BUILD.bazel
+++ b/internal/lint/BUILD.bazel
@@ -20,6 +20,7 @@ go_library(
         "check_enums_no_allow_alias.go",
         "check_fields_not_reserved.go",
         "check_file_header.go",
+        "check_file_names_lower_snake_case.go",
         "check_file_options_equal.go",
         "check_file_options_required.go",
         "check_file_options_same_in_dir.go",
diff --git a/internal/lint/check_file_names_lower_snake_case.go b/internal/lint/check_file_names_lower_snake_case.go
new file mode 100644
index 0000000..d17b724
--- /dev/null
+++ b/internal/lint/check_file_names_lower_snake_case.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package lint
+
+import (
+	"path/filepath"
+	"strings"
+	"text/scanner"
+
+	"github.com/uber/prototool/internal/strs"
+	"github.com/uber/prototool/internal/text"
+)
+
+var fileNamesLowerSnakeCaseLinter = NewLinter(
+	"FILE_NAMES_LOWER_SNAKE_CASE",
+	"Verifies that the file name is lower_snake_case.proto.",
+	checkFileNamesLowerSnakeCase,
+)
+
+func checkFileNamesLowerSnakeCase(add func(*text.Failure), dirPath string, descriptors []*FileDescriptor) error {
+	return runVisitor(fileNamesLowerSnakeCaseVisitor{baseAddVisitor: newBaseAddVisitor(add)}, descriptors)
+}
+
+type fileNamesLowerSnakeCaseVisitor struct {
+	baseAddVisitor
+}
+
+func (v fileNamesLowerSnakeCaseVisitor) OnStart(descriptor *FileDescriptor) error {
+	filename := filepath.Base(descriptor.Filename)
+	if !strs.IsLowerSnakeCase(strings.TrimSuffix(filename, filepath.Ext(filename))) {
+		v.AddFailuref(scanner.Position{Filename: descriptor.Filename}, "File name should be lower_snake_case.proto.")
+	}
+	return nil
+}
diff --git a/internal/lint/lint.go b/internal/lint/lint.go
index e9a80cf..c26ddfa 100644
--- a/internal/lint/lint.go
+++ b/internal/lint/lint.go
@@ -53,6 +53,7 @@ var (
 		enumsNoAllowAliasLinter,
 		fieldsNotReservedLinter,
 		fileHeaderLinter,
+		fileNamesLowerSnakeCaseLinter,
 		fileOptionsCSharpNamespaceSameInDirLinter,
 		fileOptionsEqualCSharpNamespaceCapitalizedLinter,
 		fileOptionsEqualGoPackageV2SuffixLinter,
@@ -191,6 +192,7 @@ var (
 		enumsNoAllowAliasLinter,
 		fieldsNotReservedLinter,
 		fileHeaderLinter,
+		fileNamesLowerSnakeCaseLinter,
 		fileOptionsCSharpNamespaceSameInDirLinter,
 		fileOptionsEqualCSharpNamespaceCapitalizedLinter,
 		fileOptionsEqualGoPackageV2SuffixLinter,


Test patch:
diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go
index 465421c..394de44 100644
--- a/internal/cmd/cmd_test.go
+++ b/internal/cmd/cmd_test.go
@@ -581,6 +581,13 @@ func TestLint(t *testing.T) {
 		"testdata/lint/nokeywords/foo/public/public_suppressed.proto",
 	)
 
+	assertDoLintFile(
+		t,
+		false,
+		`1:1:FILE_NAMES_LOWER_SNAKE_CASE`,
+		"testdata/lint/filename/fileNameInvalid.proto",
+	)
+
 	assertDoLintFile(
 		t,
 		false,
diff --git a/internal/cmd/testdata/lint/filename/fileNameInvalid.proto b/internal/cmd/testdata/lint/filename/fileNameInvalid.proto
new file mode 100644
index 0000000..0e85a63
--- /dev/null
+++ b/internal/cmd/testdata/lint/filename/fileNameInvalid.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package foo.v1;
+
+option csharp_namespace = "Foo.V1";
+option go_package = "foov1";
+option java_multiple_files = true;
+option java_outer_classname = "FileNameInvalidProto";
+option java_package = "com.foo.v1";
+option objc_class_prefix = "FXX";
+option php_namespace = "Foo\\V1";
diff --git a/internal/cmd/testdata/lint/filename/prototool.yaml b/internal/cmd/testdata/lint/filename/prototool.yaml
new file mode 100644
index 0000000..092dbf5
--- /dev/null
+++ b/internal/cmd/testdata/lint/filename/prototool.yaml
@@ -0,0 +1,2 @@
+lint:
+  group: uber2