Repository: uber/prototool
Pull request #299 (2019-01-14 17:12:00)

Expose error-format flag
This exposes the`--error-format` flag, formerly an internal-only flag named `--print-fields`. This allows you do to e.g:

`prototool lint --error-format filename:id`

Which instead of resulting in:

```
uber/bar/v1/bar.proto:33:1:Message "Hello" needs a comment with a complete sentence that starts on the first line of the comment.
```

Will result in:

```
uber/bar/v1/bar.proto:MESSAGES_HAVE_SENTENCE_COMMENTS_EXCEPT_REQUEST_RESPONSE_TYPES
```

This is useful for using `prototool` in automated scripts.

The `--print-fields` flag was the last item that was part of `develMode`, which was an internal `bool` that signified to expose something only internally for testing (a concept we added before releasing v1.0 so we could keep certain commands around without exposing them), so the concept of `develMode` is now removed.

`--print-fields` was a global flag, but now `--error-format` is only attached to the commands where it is relevant, so some work had to be done in `internal/cmd/cmd_test.go` to only call `--error-format` when it was needed.

This also sneaks in a fix to the `*_HAVE_SENTENCE_COMMENTS*` linters that allows comments to start with a digit as well as an uppercase letter.

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

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

```diff
@@            Coverage Diff             @@
##              dev     #299      +/-   ##
==========================================
+ Coverage   72.85%   72.89%   +0.03%     
==========================================
  Files         136      136              
  Lines        6039     6046       +7     
==========================================
+ Hits         4400     4407       +7     
  Misses       1223     1223              
  Partials      416      416
```


| [Impacted Files](https://codecov.io/gh/uber/prototool/pull/299?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/exec/exec.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvZXhlYy9leGVjLmdv) | `57.14% <100%> (ø)` | :arrow_up: |
| [internal/lint/lint.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9saW50Lmdv) | `72.14% <100%> (ø)` | :arrow_up: |
| [internal/cmd/flags.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvY21kL2ZsYWdzLmdv) | `100% <100%> (ø)` | :arrow_up: |
| [internal/cmd/templates.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvY21kL3RlbXBsYXRlcy5nbw==) | `82.74% <100%> (+0.63%)` | :arrow_up: |
| [internal/cmd/cmd.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvY21kL2NtZC5nbw==) | `67.16% <36.36%> (-0.96%)` | :arrow_down: |
| [internal/exec/runner.go](https://codecov.io/gh/uber/prototool/pull/299/diff?src=pr&el=tree#diff-aW50ZXJuYWwvZXhlYy9ydW5uZXIuZ28=) | `46.84% <60%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/uber/prototool/pull/299?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/299?src=pr&el=footer). Last update [6929c28...0e6c09f](https://codecov.io/gh/uber/prototool/pull/299?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index dcd714b..f8b889e 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -48,26 +48,26 @@ var genManTime = time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)
 
 // Do runs the command logic.
 func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
-	return do(false, args, stdin, stdout, stderr)
+	return do(args, stdin, stdout, stderr)
 }
 
-func do(develMode bool, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
-	return runRootCommand(develMode, args, stdin, stdout, stderr, (*cobra.Command).Execute)
+func do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
+	return runRootCommand(args, stdin, stdout, stderr, (*cobra.Command).Execute)
 }
 
 // GenBashCompletion generates a bash completion file to the writer.
 func GenBashCompletion(stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
-	return runRootCommandOutput(false, []string{}, stdin, stdout, stderr, (*cobra.Command).GenBashCompletion)
+	return runRootCommandOutput([]string{}, stdin, stdout, stderr, (*cobra.Command).GenBashCompletion)
 }
 
 // GenZshCompletion generates a zsh completion file to the writer.
 func GenZshCompletion(stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
-	return runRootCommandOutput(false, []string{}, stdin, stdout, stderr, (*cobra.Command).GenZshCompletion)
+	return runRootCommandOutput([]string{}, stdin, stdout, stderr, (*cobra.Command).GenZshCompletion)
 }
 
 // GenManpages generates the manpages to the given directory.
 func GenManpages(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
-	return runRootCommand(false, args, stdin, stdout, stderr, func(cmd *cobra.Command) error {
+	return runRootCommand(args, stdin, stdout, stderr, func(cmd *cobra.Command) error {
 		if len(args) != 1 {
 			return fmt.Errorf("usage: %s dirPath", os.Args[0])
 		}
@@ -80,23 +80,21 @@ func GenManpages(args []string, stdin io.Reader, stdout io.Writer, stderr io.Wri
 	})
 }
 
-// develMode turns on sub-commands and potentially flags that we do not expose during the build of the prototool binary
-func runRootCommandOutput(develMode bool, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer, f func(*cobra.Command, io.Writer) error) int {
-	return runRootCommand(develMode, args, stdin, stdout, stderr, func(cmd *cobra.Command) error { return f(cmd, stdout) })
+func runRootCommandOutput(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer, f func(*cobra.Command, io.Writer) error) int {
+	return runRootCommand(args, stdin, stdout, stderr, func(cmd *cobra.Command) error { return f(cmd, stdout) })
 }
 
-// develMode turns on sub-commands and potentially flags that we do not expose during the build of the prototool binary
-func runRootCommand(develMode bool, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer, f func(*cobra.Command) error) (exitCode int) {
+func runRootCommand(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer, f func(*cobra.Command) error) (exitCode int) {
 	if err := checkOS(); err != nil {
 		return printAndGetErrorExitCode(err, stdout)
 	}
-	if err := f(getRootCommand(&exitCode, develMode, args, stdin, stdout, stderr)); err != nil {
+	if err := f(getRootCommand(&exitCode, args, stdin, stdout, stderr)); err != nil {
 		return printAndGetErrorExitCode(err, stdout)
 	}
 	return exitCode
 }
 
-func getRootCommand(exitCodeAddr *int, develMode bool, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command {
+func getRootCommand(exitCodeAddr *int, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command {
 	flags := &flags{}
 
 	rootCmd := &cobra.Command{Use: "prototool"}
@@ -128,10 +126,6 @@ func getRootCommand(exitCodeAddr *int, develMode bool, args []string, stdin io.R
 	// flags bound to rootCmd are global flags
 	flags.bindDebug(rootCmd.PersistentFlags())
 
-	if develMode {
-		flags.bindPrintFields(rootCmd.PersistentFlags())
-	}
-
 	rootCmd.SetArgs(args)
 	rootCmd.SetOutput(stdout)
 
diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go
index 1ecb7cc..b5572d8 100644
--- a/internal/cmd/flags.go
+++ b/internal/cmd/flags.go
@@ -38,6 +38,7 @@ type flags struct {
 	disableFormat     bool
 	disableLint       bool
 	dryRun            bool
+	errorFormat       string
 	fix               bool
 	gitBranch         string
 	gitTag            string
@@ -54,7 +55,6 @@ type flags struct {
 	name              string
 	overwrite         bool
 	pkg               string
-	printFields       string
 	protocBinPath     string
 	protocWKTPath     string
 	protocURL         string
@@ -114,6 +114,10 @@ func (f *flags) bindDryRun(flagSet *pflag.FlagSet) {
 	flagSet.BoolVar(&f.dryRun, "dry-run", false, "Print the protoc commands that would have been run without actually running them.")
 }
 
+func (f *flags) bindErrorFormat(flagSet *pflag.FlagSet) {
+	flagSet.StringVar(&f.errorFormat, "error-format", "filename:line:column:message", `The colon-separated fields to print out on error. Valid values are "filename:line:column:id:message".`)
+}
+
 func (f *flags) bindGitBranch(flagSet *pflag.FlagSet) {
 	flagSet.StringVar(&f.gitBranch, "git-branch", "", "The git branch to check against. The default is the default branch.")
 }
@@ -174,10 +178,6 @@ func (f *flags) bindPackage(flagSet *pflag.FlagSet) {
 	flagSet.StringVar(&f.pkg, "package", "", "The Protobuf package to use in the created file.")
 }
 
-func (f *flags) bindPrintFields(flagSet *pflag.FlagSet) {
-	flagSet.StringVar(&f.printFields, "print-fields", "filename:line:column:message", "The colon-separated fields to print out on error.")
-}
-
 func (f *flags) bindProtocURL(flagSet *pflag.FlagSet) {
 	flagSet.StringVar(&f.protocURL, "protoc-url", "", "The url to use to download the protoc zip file, otherwise uses GitHub Releases. Setting this option will ignore the config protoc.version setting.")
 }
diff --git a/internal/cmd/templates.go b/internal/cmd/templates.go
index 8807051..ce8c802 100644
--- a/internal/cmd/templates.go
+++ b/internal/cmd/templates.go
@@ -49,6 +49,7 @@ var (
 			flags.bindConfigData(flagSet)
 			flags.bindDisableFormat(flagSet)
 			flags.bindDisableLint(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindJSON(flagSet)
 			flags.bindFix(flagSet)
 			flags.bindProtocURL(flagSet)
@@ -133,6 +134,7 @@ Artifacts are downloaded to the following directories based on flags and environ
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
 			flags.bindDryRun(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindJSON(flagSet)
 			flags.bindProtocURL(flagSet)
 			flags.bindProtocBinPath(flagSet)
@@ -234,6 +236,7 @@ If Vim integration is set up, files will be generated when you open a new Protob
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
 			flags.bindDiffMode(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindJSON(flagSet)
 			flags.bindLintMode(flagSet)
 			flags.bindOverwrite(flagSet)
@@ -255,6 +258,7 @@ If Vim integration is set up, files will be generated when you open a new Protob
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
 			flags.bindDryRun(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindJSON(flagSet)
 			flags.bindProtocURL(flagSet)
 			flags.bindProtocBinPath(flagSet)
@@ -344,6 +348,7 @@ $ cat input.json | prototool grpc example \
 			flags.bindCallTimeout(flagSet)
 			flags.bindConnectTimeout(flagSet)
 			flags.bindData(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindHeaders(flagSet)
 			flags.bindKeepaliveTime(flagSet)
 			flags.bindMethod(flagSet)
@@ -364,6 +369,7 @@ $ cat input.json | prototool grpc example \
 		BindFlags: func(flagSet *pflag.FlagSet, flags *flags) {
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindProtocURL(flagSet)
 			flags.bindProtocBinPath(flagSet)
 			flags.bindProtocWKTPath(flagSet)
@@ -380,6 +386,7 @@ $ cat input.json | prototool grpc example \
 		BindFlags: func(flagSet *pflag.FlagSet, flags *flags) {
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindName(flagSet)
 			flags.bindProtocURL(flagSet)
 			flags.bindProtocBinPath(flagSet)
@@ -397,6 +404,7 @@ $ cat input.json | prototool grpc example \
 		BindFlags: func(flagSet *pflag.FlagSet, flags *flags) {
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindName(flagSet)
 			flags.bindProtocURL(flagSet)
 			flags.bindProtocBinPath(flagSet)
@@ -464,6 +472,7 @@ sys	0m0.924s`,
 		BindFlags: func(flagSet *pflag.FlagSet, flags *flags) {
 			flags.bindCachePath(flagSet)
 			flags.bindConfigData(flagSet)
+			flags.bindErrorFormat(flagSet)
 			flags.bindJSON(flagSet)
 			flags.bindListAllLinters(flagSet)
 			flags.bindListLinters(flagSet)
@@ -592,10 +601,10 @@ func getRunner(stdin io.Reader, stdout io.Writer, stderr io.Writer, flags *flags
 			exec.RunnerWithProtocWKTPath(flags.protocWKTPath),
 		)
 	}
-	if flags.printFields != "" {
+	if flags.errorFormat != "" {
 		runnerOptions = append(
 			runnerOptions,
-			exec.RunnerWithPrintFields(flags.printFields),
+			exec.RunnerWithErrorFormat(flags.errorFormat),
 		)
 	}
 	if flags.protocURL != "" {
diff --git a/internal/exec/exec.go b/internal/exec/exec.go
index 56ac477..d2f3a26 100644
--- a/internal/exec/exec.go
+++ b/internal/exec/exec.go
@@ -96,11 +96,11 @@ func RunnerWithJSON() RunnerOption {
 	}
 }
 
-// RunnerWithPrintFields returns a RunnerOption that uses the given colon-separated
-// print fields. The default is filename:line:column:message.
-func RunnerWithPrintFields(printFields string) RunnerOption {
+// RunnerWithErrorFormat returns a RunnerOption that uses the given colon-separated
+// error format. The default is filename:line:column:message.
+func RunnerWithErrorFormat(errorFormat string) RunnerOption {
 	return func(runner *runner) {
-		runner.printFields = printFields
+		runner.errorFormat = errorFormat
 	}
 }
 
diff --git a/internal/exec/runner.go b/internal/exec/runner.go
index dbd3257..111271f 100644
--- a/internal/exec/runner.go
+++ b/internal/exec/runner.go
@@ -70,7 +70,7 @@ type runner struct {
 	protocBinPath string
 	protocWKTPath string
 	protocURL     string
-	printFields   string
+	errorFormat   string
 	json          bool
 }
 
@@ -112,7 +112,7 @@ func (r *runner) cloneForWorkDirPath(workDirPath string) *runner {
 		protocBinPath:    r.protocBinPath,
 		protocWKTPath:    r.protocWKTPath,
 		protocURL:        r.protocURL,
-		printFields:      r.printFields,
+		errorFormat:      r.errorFormat,
 		json:             r.json,
 	}
 }
@@ -640,7 +640,7 @@ func (r *runner) BreakCheck(args []string, gitBranch string, gitTag string, incl
 		return err
 	}
 	if len(failures) > 0 {
-		if err := r.printFailuresForPrintFields("message", "", nil, failures...); err != nil {
+		if err := r.printFailuresForErrorFormat("message", "", nil, failures...); err != nil {
 			return err
 		}
 		return newExitErrorf(255, "")
@@ -902,16 +902,16 @@ func (r *runner) getMeta(args []string) (*meta, error) {
 // if set, it will update the Failures to have this filename
 // will be sorted
 func (r *runner) printFailures(filename string, meta *meta, failures ...*text.Failure) error {
-	return r.printFailuresForPrintFields(r.printFields, filename, meta, failures...)
+	return r.printFailuresForErrorFormat(r.errorFormat, filename, meta, failures...)
 }
 
-func (r *runner) printFailuresForPrintFields(printFields string, filename string, meta *meta, failures ...*text.Failure) error {
+func (r *runner) printFailuresForErrorFormat(errorFormat string, filename string, meta *meta, failures ...*text.Failure) error {
 	for _, failure := range failures {
 		if filename != "" {
 			failure.Filename = filename
 		}
 	}
-	failureFields, err := text.ParseColonSeparatedFailureFields(printFields)
+	failureFields, err := text.ParseColonSeparatedFailureFields(errorFormat)
 	if err != nil {
 		return err
 	}
diff --git a/internal/lint/lint.go b/internal/lint/lint.go
index 2b0cc52..e92b085 100644
--- a/internal/lint/lint.go
+++ b/internal/lint/lint.go
@@ -502,7 +502,7 @@ func commentStartsWithUppercaseLetter(comment *proto.Comment) bool {
 	if firstLine == "" {
 		return false
 	}
-	return unicode.IsUpper(rune(firstLine[0]))
+	return unicode.IsUpper(rune(firstLine[0])) || unicode.IsDigit(rune(firstLine[0]))
 }
 
 func commentContainsPeriod(comment *proto.Comment) bool {


Test patch:
diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go
index a11d82e..b6703e6 100644
--- a/internal/cmd/cmd_test.go
+++ b/internal/cmd/cmd_test.go
@@ -134,8 +134,8 @@ func TestInit(t *testing.T) {
 		_ = os.RemoveAll(tmpDir)
 	}()
 
-	assertDo(t, 0, "", "config", "init", tmpDir)
-	assertDo(t, 1, fmt.Sprintf("%s already exists", filepath.Join(tmpDir, settings.DefaultConfigFilename)), "config", "init", tmpDir)
+	assertDo(t, false, 0, "", "config", "init", tmpDir)
+	assertDo(t, false, 1, fmt.Sprintf("%s already exists", filepath.Join(tmpDir, settings.DefaultConfigFilename)), "config", "init", tmpDir)
 }
 
 func TestLint(t *testing.T) {
@@ -616,6 +616,7 @@ func TestLintConfigDataOverride(t *testing.T) {
 	)
 	assertExact(
 		t,
+		true,
 		1,
 		`json: unknown field "unknown_key"`,
 		"lint",
@@ -1040,16 +1041,17 @@ func TestGRPC(t *testing.T) {
 }
 
 func TestVersion(t *testing.T) {
-	assertRegexp(t, 0, fmt.Sprintf("Version:.*%s\nDefault protoc version:.*%s\n", vars.Version, vars.DefaultProtocVersion), "version")
+	assertRegexp(t, false, 0, fmt.Sprintf("Version:.*%s\nDefault protoc version:.*%s\n", vars.Version, vars.DefaultProtocVersion), "version")
 }
 
 func TestVersionJSON(t *testing.T) {
-	assertRegexp(t, 0, fmt.Sprintf(`(?s){.*"version":.*"%s",.*"default_protoc_version":.*"%s".*}`, vars.Version, vars.DefaultProtocVersion), "version", "--json")
+	assertRegexp(t, false, 0, fmt.Sprintf(`(?s){.*"version":.*"%s",.*"default_protoc_version":.*"%s".*}`, vars.Version, vars.DefaultProtocVersion), "version", "--json")
 }
 
 func TestInspectPackages(t *testing.T) {
 	assertExact(
 		t,
+		true,
 		0,
 		`bar
 foo
@@ -1061,6 +1063,7 @@ google.protobuf`,
 func TestInspectPackageDeps(t *testing.T) {
 	assertExact(
 		t,
+		true,
 		0,
 		`bar
 google.protobuf`,
@@ -1068,12 +1071,14 @@ google.protobuf`,
 	)
 	assertExact(
 		t,
+		true,
 		0,
 		``,
 		"inspect", "package-deps", "testdata/foo", "--name", "bar",
 	)
 	assertExact(
 		t,
+		true,
 		0,
 		``,
 		"inspect", "package-deps", "testdata/foo", "--name", "google.protobuf",
@@ -1083,18 +1088,21 @@ google.protobuf`,
 func TestInspectPackageImporters(t *testing.T) {
 	assertExact(
 		t,
+		true,
 		0,
 		``,
 		"inspect", "package-importers", "testdata/foo", "--name", "foo",
 	)
 	assertExact(
 		t,
+		true,
 		0,
 		`foo`,
 		"inspect", "package-importers", "testdata/foo", "--name", "bar",
 	)
 	assertExact(
 		t,
+		true,
 		0,
 		`foo`,
 		"inspect", "package-importers", "testdata/foo", "--name", "google.protobuf",
@@ -1111,7 +1119,7 @@ func TestListAllLinters(t *testing.T) {
 }
 
 func TestListAllLintGroups(t *testing.T) {
-	assertExact(t, 0, "google\nuber\nuber2", "lint", "--list-all-lint-groups")
+	assertExact(t, true, 0, "google\nuber\nuber2", "lint", "--list-all-lint-groups")
 }
 
 func TestListLintGroup(t *testing.T) {
@@ -1123,6 +1131,7 @@ func TestListLintGroup(t *testing.T) {
 func TestDiffLintGroups(t *testing.T) {
 	assertExact(
 		t,
+		true,
 		0,
 		`> COMMENTS_NO_C_STYLE
 > ENUMS_NO_ALLOW_ALIAS
@@ -1158,7 +1167,7 @@ func assertLinters(t *testing.T, linters []lint.Linter, args ...string) {
 		linterIDs = append(linterIDs, linter.ID())
 	}
 	sort.Strings(linterIDs)
-	assertDo(t, 0, strings.Join(linterIDs, "\n"), args...)
+	assertDo(t, true, 0, strings.Join(linterIDs, "\n"), args...)
 }
 
 func assertDoCompileFiles(t *testing.T, expectSuccess bool, asJSON bool, expectedLinePrefixes string, filePaths ...string) {
@@ -1171,7 +1180,7 @@ func assertDoCompileFiles(t *testing.T, expectSuccess bool, asJSON bool, expecte
 	if asJSON {
 		cmd = append(cmd, "--json")
 	}
-	assertDo(t, expectedExitCode, strings.Join(lines, "\n"), append(cmd, filePaths...)...)
+	assertDo(t, true, expectedExitCode, strings.Join(lines, "\n"), append(cmd, filePaths...)...)
 }
 
 func assertDoCreateFile(t *testing.T, expectSuccess bool, remove bool, filePath string, pkgOverride string, expectedFileData string) {
@@ -1183,7 +1192,7 @@ func assertDoCreateFile(t *testing.T, expectSuccess bool, remove bool, filePath
 	if pkgOverride != "" {
 		args = append(args, "--package", pkgOverride)
 	}
-	_, exitCode := testDo(t, args...)
+	_, exitCode := testDo(t, false, args...)
 	if expectSuccess {
 		assert.Equal(t, 0, exitCode)
 		fileData, err := ioutil.ReadFile(filePath)
@@ -1203,7 +1212,7 @@ func assertDoLintFile(t *testing.T, expectSuccess bool, expectedLinePrefixesWith
 	if !expectSuccess {
 		expectedExitCode = 255
 	}
-	assertDo(t, expectedExitCode, strings.Join(lines, "\n"), append([]string{"lint", filePath}, args...)...)
+	assertDo(t, true, expectedExitCode, strings.Join(lines, "\n"), append([]string{"lint", filePath}, args...)...)
 }
 
 func assertDoLintFiles(t *testing.T, expectSuccess bool, expectedLinePrefixes string, filePaths ...string) {
@@ -1212,7 +1221,7 @@ func assertDoLintFiles(t *testing.T, expectSuccess bool, expectedLinePrefixes st
 	if !expectSuccess {
 		expectedExitCode = 255
 	}
-	assertDo(t, expectedExitCode, strings.Join(lines, "\n"), append([]string{"lint"}, filePaths...)...)
+	assertDo(t, true, expectedExitCode, strings.Join(lines, "\n"), append([]string{"lint"}, filePaths...)...)
 }
 
 func assertGoldenFormat(t *testing.T, expectSuccess bool, fix bool, filePath string) {
@@ -1221,7 +1230,7 @@ func assertGoldenFormat(t *testing.T, expectSuccess bool, fix bool, filePath str
 		args = append(args, "--fix")
 	}
 	args = append(args, filePath)
-	output, exitCode := testDo(t, args...)
+	output, exitCode := testDo(t, true, args...)
 	expectedExitCode := 0
 	if !expectSuccess {
 		expectedExitCode = 255
@@ -1235,39 +1244,39 @@ func assertGoldenFormat(t *testing.T, expectSuccess bool, fix bool, filePath str
 func assertGRPC(t *testing.T, expectedExitCode int, expectedLinePrefixes string, filePath string, method string, jsonData string) {
 	excitedTestCase := startExcitedTestCase(t)
 	defer excitedTestCase.Close()
-	assertDoStdin(t, strings.NewReader(jsonData), expectedExitCode, expectedLinePrefixes, "grpc", filePath, "--address", excitedTestCase.Address(), "--method", method, "--stdin")
+	assertDoStdin(t, strings.NewReader(jsonData), true, expectedExitCode, expectedLinePrefixes, "grpc", filePath, "--address", excitedTestCase.Address(), "--method", method, "--stdin")
 }
 
-func assertRegexp(t *testing.T, expectedExitCode int, expectedRegexp string, args ...string) {
-	stdout, exitCode := testDo(t, args...)
+func assertRegexp(t *testing.T, extraErrorFormat bool, expectedExitCode int, expectedRegexp string, args ...string) {
+	stdout, exitCode := testDo(t, extraErrorFormat, args...)
 	assert.Equal(t, expectedExitCode, exitCode)
 	matched, err := regexp.MatchString(expectedRegexp, stdout)
 	assert.NoError(t, err)
 	assert.True(t, matched, "Expected regex %s but got %s", expectedRegexp, stdout)
 }
 
-func assertExact(t *testing.T, expectedExitCode int, expectedStdout string, args ...string) {
-	stdout, exitCode := testDo(t, args...)
+func assertExact(t *testing.T, extraErrorFormat bool, expectedExitCode int, expectedStdout string, args ...string) {
+	stdout, exitCode := testDo(t, extraErrorFormat, args...)
 	assert.Equal(t, expectedExitCode, exitCode)
 	assert.Equal(t, expectedStdout, stdout)
 }
 
-func assertDoStdin(t *testing.T, stdin io.Reader, expectedExitCode int, expectedLinePrefixes string, args ...string) {
-	assertDoInternal(t, stdin, expectedExitCode, expectedLinePrefixes, args...)
+func assertDoStdin(t *testing.T, stdin io.Reader, extraErrorFormat bool, expectedExitCode int, expectedLinePrefixes string, args ...string) {
+	assertDoInternal(t, stdin, extraErrorFormat, expectedExitCode, expectedLinePrefixes, args...)
 }
 
-func assertDo(t *testing.T, expectedExitCode int, expectedLinePrefixes string, args ...string) {
-	assertDoInternal(t, nil, expectedExitCode, expectedLinePrefixes, args...)
+func assertDo(t *testing.T, extraErrorFormat bool, expectedExitCode int, expectedLinePrefixes string, args ...string) {
+	assertDoInternal(t, nil, extraErrorFormat, expectedExitCode, expectedLinePrefixes, args...)
 }
 
-func testDoStdin(t *testing.T, stdin io.Reader, args ...string) (string, int) {
+func testDoStdin(t *testing.T, stdin io.Reader, extraErrorFormat bool, args ...string) (string, int) {
 	testDownload(t)
-	return testDoInternal(stdin, args...)
+	return testDoInternal(stdin, extraErrorFormat, args...)
 }
 
-func testDo(t *testing.T, args ...string) (string, int) {
+func testDo(t *testing.T, extraErrorFormat bool, args ...string) (string, int) {
 	testDownload(t)
-	return testDoInternal(nil, args...)
+	return testDoInternal(nil, extraErrorFormat, args...)
 }
 
 func getCleanLines(output string) []string {
@@ -1369,8 +1378,8 @@ func (s *excitedServer) ExclamationBidiStream(streamServer grpcpb.ExcitedService
 
 // do not use these in tests
 
-func assertDoInternal(t *testing.T, stdin io.Reader, expectedExitCode int, expectedLinePrefixes string, args ...string) {
-	stdout, exitCode := testDoStdin(t, stdin, args...)
+func assertDoInternal(t *testing.T, stdin io.Reader, extraErrorFormat bool, expectedExitCode int, expectedLinePrefixes string, args ...string) {
+	stdout, exitCode := testDoStdin(t, stdin, extraErrorFormat, args...)
 	outputSplit := getCleanLines(stdout)
 	assert.Equal(t, expectedExitCode, exitCode, strings.Join(outputSplit, "\n"))
 	expectedLinePrefixesSplit := getCleanLines(expectedLinePrefixes)
@@ -1386,20 +1395,22 @@ func testDownload(t *testing.T) {
 	// download checks if protoc is already downloaded to the cache location
 	// if it is, then this is effectively a no-op
 	// if it isn't, then this downloads to the cache
-	stdout, exitCode := testDoInternal(nil, "cache", "update")
+	stdout, exitCode := testDoInternal(nil, false, "cache", "update")
 	require.Equal(t, 0, exitCode, "had non-zero exit code when downloading: %s", stdout)
 }
 
-func testDoInternal(stdin io.Reader, args ...string) (string, int) {
-	args = append(args,
-		"--print-fields", "filename:line:column:id:message",
-	)
+func testDoInternal(stdin io.Reader, extraErrorFormat bool, args ...string) (string, int) {
 	if stdin == nil {
 		stdin = os.Stdin
 	}
+	if extraErrorFormat {
+		args = append(args,
+			"--error-format", "filename:line:column:id:message",
+		)
+	}
 	buffer := bytes.NewBuffer(nil)
 	// develMode is on, so we have access to all commands
-	exitCode := do(true, args, stdin, buffer, buffer)
+	exitCode := do(args, stdin, buffer, buffer)
 	return strings.TrimSpace(buffer.String()), exitCode
 }