Skip to content

Commit 114ac82

Browse files
adonovangopherbot
authored andcommitted
go/analysis: preparatory cleanups
This change contains some minor cleanups split out of the forthcoming three-way merge work. 1. Plumb pass.ReadFile down from a (hidden) checker option. Factor CheckedReadFile helper. 2. In "assign" checker, improve SuggestedFix title. 3. Fix bug in error handling in fix_test.go. 4. Define testenv.RedirectStderr helper to temporarily redirect stderr and log output to t.Log, to declutter the test output. Update golang/go#68765 Update golang/go#67049 Change-Id: Icac62afdeb160a2dfa3cc3637b79fe7d89e92272 Reviewed-on: https://go-review.googlesource.com/c/tools/+/643695 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]> Commit-Queue: Alan Donovan <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent 1c9f002 commit 114ac82

File tree

12 files changed

+101
-35
lines changed

12 files changed

+101
-35
lines changed

go/analysis/analysistest/analysistest.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
167167
act := result.Action
168168

169169
// file -> message -> edits
170+
// TODO(adonovan): this mapping assumes fix.Messages are unique across analyzers.
170171
fileEdits := make(map[*token.File]map[string][]diff.Edit)
171172
fileContents := make(map[*token.File][]byte)
172173

@@ -179,6 +180,8 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
179180
t.Errorf("missing Diagnostic.Category for SuggestedFix without TextEdits (gopls requires the category for the name of the fix command")
180181
}
181182

183+
// TODO(adonovan): factor in common with go/analysis/internal/checker.validateEdits.
184+
182185
for _, edit := range fix.TextEdits {
183186
start, end := edit.Pos, edit.End
184187
if !end.IsValid() {
@@ -275,7 +278,7 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
275278
}
276279
} else {
277280
// all suggested fixes are represented by a single file
278-
281+
// TODO(adonovan): fix: this makes no sense if len(fixes) > 1.
279282
var catchallEdits []diff.Edit
280283
for _, edits := range fixes {
281284
catchallEdits = append(catchallEdits, edits...)

go/analysis/checker/checker.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"go/types"
3636
"io"
3737
"log"
38+
"os"
3839
"reflect"
3940
"sort"
4041
"strings"
@@ -55,9 +56,10 @@ type Options struct {
5556
SanityCheck bool // check fact encoding is ok and deterministic
5657
FactLog io.Writer // if non-nil, log each exported fact to it
5758

58-
// TODO(adonovan): add ReadFile so that an Overlay specified
59+
// TODO(adonovan): expose ReadFile so that an Overlay specified
5960
// in the [packages.Config] can be communicated via
6061
// Pass.ReadFile to each Analyzer.
62+
readFile analysisinternal.ReadFileFunc
6163
}
6264

6365
// Graph holds the results of a round of analysis, including the graph
@@ -344,7 +346,11 @@ func (act *Action) execOnce() {
344346
AllObjectFacts: act.AllObjectFacts,
345347
AllPackageFacts: act.AllPackageFacts,
346348
}
347-
pass.ReadFile = analysisinternal.MakeReadFile(pass)
349+
readFile := os.ReadFile
350+
if act.opts.readFile != nil {
351+
readFile = act.opts.readFile
352+
}
353+
pass.ReadFile = analysisinternal.CheckedReadFile(pass, readFile)
348354
act.pass = pass
349355

350356
act.Result, act.Err = func() (any, error) {

go/analysis/diagnostic.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ type RelatedInformation struct {
6565
// user can choose to apply to their code. Usually the SuggestedFix is
6666
// meant to fix the issue flagged by the diagnostic.
6767
//
68-
// The TextEdits must not overlap, nor contain edits for other packages.
68+
// The TextEdits must not overlap, nor contain edits for other
69+
// packages. Edits need not be totally ordered, but the order
70+
// determines how insertions at the same point will be applied.
6971
type SuggestedFix struct {
7072
// A verb phrase describing the fix, to be shown to
7173
// a user trying to decide whether to accept it.

go/analysis/internal/checker/checker.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"go/token"
2121
"io"
2222
"io/ioutil"
23+
2324
"log"
2425
"os"
2526
"runtime"
@@ -139,6 +140,9 @@ func Run(args []string, analyzers []*analysis.Analyzer) int {
139140
return 1
140141
}
141142

143+
// TODO(adonovan): simplify exit code logic by using a single
144+
// exit code variable and applying "code = max(code, X)" each
145+
// time an error of code X occurs.
142146
pkgsExitCode := 0
143147
// Print package and module errors regardless of RunDespiteErrors.
144148
// Do not exit if there are errors, yet.

go/analysis/internal/checker/checker_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
func TestApplyFixes(t *testing.T) {
2727
testenv.NeedsGoPackages(t)
28+
testenv.RedirectStderr(t) // associated checker.Run output with this test
2829

2930
files := map[string]string{
3031
"rename/test.go": `package rename
@@ -114,10 +115,12 @@ func run(pass *analysis.Pass) (interface{}, error) {
114115
{Pos: ident.Pos(), End: ident.End(), NewText: []byte("lorem ipsum")},
115116
}...)
116117
case duplicate:
118+
// Duplicate (non-insertion) edits are disallowed,
119+
// so this is a buggy analyzer, and validatedFixes should reject it.
117120
edits = append(edits, edits...)
118121
case other:
119122
if pass.Analyzer.Name == other {
120-
edits[0].Pos = edits[0].Pos + 1 // shift by one to mismatch analyzer and other
123+
edits[0].Pos++ // shift by one to mismatch analyzer and other
121124
}
122125
}
123126
pass.Report(analysis.Diagnostic{
@@ -133,6 +136,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
133136

134137
func TestRunDespiteErrors(t *testing.T) {
135138
testenv.NeedsGoPackages(t)
139+
testenv.RedirectStderr(t) // associate checker.Run output with this test
136140

137141
files := map[string]string{
138142
"rderr/test.go": `package rderr
@@ -360,4 +364,7 @@ hello from other
360364
if !ran {
361365
t.Error("analyzer did not run")
362366
}
367+
368+
// TODO(adonovan): test that fixes are applied to the
369+
// pass.ReadFile virtual file tree.
363370
}

go/analysis/internal/checker/fix_test.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,23 @@ func fix(t *testing.T, dir, analyzers string, wantExit int, patterns ...string)
7777
return strings.ReplaceAll(s, os.TempDir(), "os.TempDir/")
7878
}
7979
outBytes, err := cmd.CombinedOutput()
80-
out := clean(string(outBytes))
81-
t.Logf("$ %s\n%s", clean(fmt.Sprint(cmd)), out)
82-
if err, ok := err.(*exec.ExitError); !ok {
83-
t.Fatalf("failed to execute multichecker: %v", err)
84-
} else if err.ExitCode() != wantExit {
85-
// plan9 ExitCode() currently only returns 0 for success or 1 for failure
86-
if !(runtime.GOOS == "plan9" && wantExit != exitCodeSuccess && err.ExitCode() != exitCodeSuccess) {
87-
t.Errorf("exit code was %d, want %d", err.ExitCode(), wantExit)
80+
switch err := err.(type) {
81+
case nil:
82+
// success
83+
case *exec.ExitError:
84+
if code := err.ExitCode(); code != wantExit {
85+
// plan9 ExitCode() currently only returns 0 for success or 1 for failure
86+
if !(runtime.GOOS == "plan9" && wantExit != exitCodeSuccess && code != exitCodeSuccess) {
87+
t.Errorf("exit code was %d, want %d", code, wantExit)
88+
}
8889
}
90+
default:
91+
t.Fatalf("failed to execute multichecker: %v", err)
8992
}
93+
94+
out := clean(string(outBytes))
95+
t.Logf("$ %s\n%s", clean(fmt.Sprint(cmd)), out)
96+
9097
return out
9198
}
9299

@@ -253,6 +260,7 @@ func Foo() {
253260
}
254261
defer cleanup()
255262

263+
// The 'rename' and 'other' analyzers suggest conflicting fixes.
256264
out := fix(t, dir, "rename,other", exitCodeFailed, "other")
257265

258266
pattern := `.*conflicting edits from other and rename on .*foo.go`

go/analysis/internal/checker/start_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
// of the file takes effect.
2323
func TestStartFixes(t *testing.T) {
2424
testenv.NeedsGoPackages(t)
25+
testenv.RedirectStderr(t) // associated checker.Run output with this test
2526

2627
files := map[string]string{
2728
"comment/doc.go": `/* Package comment */

go/analysis/passes/assign/assign.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,12 @@ func run(pass *analysis.Pass) (any, error) {
6363
if le == re {
6464
pass.Report(analysis.Diagnostic{
6565
Pos: stmt.Pos(), Message: fmt.Sprintf("self-assignment of %s to %s", re, le),
66-
SuggestedFixes: []analysis.SuggestedFix{
67-
{Message: "Remove", TextEdits: []analysis.TextEdit{
68-
{Pos: stmt.Pos(), End: stmt.End(), NewText: []byte{}},
69-
}},
66+
SuggestedFixes: []analysis.SuggestedFix{{
67+
Message: "Remove self-assignment",
68+
TextEdits: []analysis.TextEdit{{
69+
Pos: stmt.Pos(),
70+
End: stmt.End(),
71+
}}},
7072
},
7173
})
7274
}

go/analysis/unitchecker/unitchecker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
386386
AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
387387
Module: module,
388388
}
389-
pass.ReadFile = analysisinternal.MakeReadFile(pass)
389+
pass.ReadFile = analysisinternal.CheckedReadFile(pass, os.ReadFile)
390390

391391
t0 := time.Now()
392392
act.result, act.err = a.Run(pass)

go/analysis/unitchecker/unitchecker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func _() {
133133
"message": "self-assignment of i to i",
134134
"suggested_fixes": \[
135135
\{
136-
"message": "Remove",
136+
"message": "Remove self-assignment",
137137
"edits": \[
138138
\{
139139
"filename": "([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?c/c.go",

0 commit comments

Comments
 (0)