GoPLS Viewer

Home|gopls/go/analysis/internal/checker/checker_test.go
1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package checker_test
6
7import (
8    "fmt"
9    "go/ast"
10    "io/ioutil"
11    "path/filepath"
12    "testing"
13
14    "golang.org/x/tools/go/analysis"
15    "golang.org/x/tools/go/analysis/analysistest"
16    "golang.org/x/tools/go/analysis/internal/checker"
17    "golang.org/x/tools/go/analysis/passes/inspect"
18    "golang.org/x/tools/go/ast/inspector"
19    "golang.org/x/tools/internal/testenv"
20)
21
22func TestApplyFixes(t *testing.T) {
23    testenv.NeedsGoPackages(t)
24
25    files := map[string]string{
26        "rename/test.go"`package rename
27
28func Foo() {
29    bar := 12
30    _ = bar
31}
32
33// the end
34`}
35    want := `package rename
36
37func Foo() {
38    baz := 12
39    _ = baz
40}
41
42// the end
43`
44
45    testdatacleanuperr := analysistest.WriteFiles(files)
46    if err != nil {
47        t.Fatal(err)
48    }
49    path := filepath.Join(testdata"src/rename/test.go")
50    checker.Fix = true
51    checker.Run([]string{"file=" + path}, []*analysis.Analyzer{analyzer})
52
53    contentserr := ioutil.ReadFile(path)
54    if err != nil {
55        t.Fatal(err)
56    }
57
58    got := string(contents)
59    if got != want {
60        t.Errorf("contents of rewritten file\ngot: %s\nwant: %s"gotwant)
61    }
62
63    defer cleanup()
64}
65
66var analyzer = &analysis.Analyzer{
67    Name:     "rename",
68    Requires: []*analysis.Analyzer{inspect.Analyzer},
69    Run:      run,
70}
71
72func run(pass *analysis.Pass) (interface{}, error) {
73    const (
74        from = "bar"
75        to   = "baz"
76    )
77    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
78    nodeFilter := []ast.Node{(*ast.Ident)(nil)}
79    inspect.Preorder(nodeFilter, func(n ast.Node) {
80        ident := n.(*ast.Ident)
81        if ident.Name == from {
82            msg := fmt.Sprintf("renaming %q to %q"fromto)
83            pass.Report(analysis.Diagnostic{
84                Pos:     ident.Pos(),
85                End:     ident.End(),
86                Messagemsg,
87                SuggestedFixes: []analysis.SuggestedFix{{
88                    Messagemsg,
89                    TextEdits: []analysis.TextEdit{{
90                        Pos:     ident.Pos(),
91                        End:     ident.End(),
92                        NewText: []byte(to),
93                    }},
94                }},
95            })
96        }
97    })
98
99    return nilnil
100}
101
102func TestRunDespiteErrors(t *testing.T) {
103    testenv.NeedsGoPackages(t)
104
105    files := map[string]string{
106        "rderr/test.go"`package rderr
107
108// Foo deliberately has a type error
109func Foo(s string) int {
110    return s + 1
111}
112`}
113
114    testdatacleanuperr := analysistest.WriteFiles(files)
115    if err != nil {
116        t.Fatal(err)
117    }
118    path := filepath.Join(testdata"src/rderr/test.go")
119
120    // A no-op analyzer that should finish regardless of
121    // parse or type errors in the code.
122    noop := &analysis.Analyzer{
123        Name:     "noop",
124        Requires: []*analysis.Analyzer{inspect.Analyzer},
125        Run: func(pass *analysis.Pass) (interface{}, error) {
126            return nilnil
127        },
128        RunDespiteErrorstrue,
129    }
130
131    // A no-op analyzer that should finish regardless of
132    // parse or type errors in the code.
133    noopWithFact := &analysis.Analyzer{
134        Name:     "noopfact",
135        Requires: []*analysis.Analyzer{inspect.Analyzer},
136        Run: func(pass *analysis.Pass) (interface{}, error) {
137            return nilnil
138        },
139        RunDespiteErrorstrue,
140        FactTypes:        []analysis.Fact{&EmptyFact{}},
141    }
142
143    for _test := range []struct {
144        name      string
145        pattern   []string
146        analyzers []*analysis.Analyzer
147        code      int
148    }{
149        // parse/type errors
150        {name"skip-error"pattern: []string{"file=" + path}, analyzers: []*analysis.Analyzer{analyzer}, code1},
151        // RunDespiteErrors allows a driver to run an Analyzer even after parse/type errors.
152        //
153        // The noop analyzer doesn't use facts, so the driver loads only the root
154        // package from source. For the rest, it asks 'go list' for export data,
155        // which fails because the compiler encounters the type error.  Since the
156        // errors come from 'go list', the driver doesn't run the analyzer.
157        {name"despite-error"pattern: []string{"file=" + path}, analyzers: []*analysis.Analyzer{noop}, code1},
158        // The noopfact analyzer does use facts, so the driver loads source for
159        // all dependencies, does type checking itself, recognizes the error as a
160        // type error, and runs the analyzer.
161        {name"despite-error-fact"pattern: []string{"file=" + path}, analyzers: []*analysis.Analyzer{noopWithFact}, code0},
162        // combination of parse/type errors and no errors
163        {name"despite-error-and-no-error"pattern: []string{"file=" + path"sort"}, analyzers: []*analysis.Analyzer{analyzernoop}, code1},
164        // non-existing package error
165        {name"no-package"pattern: []string{"xyz"}, analyzers: []*analysis.Analyzer{analyzer}, code1},
166        {name"no-package-despite-error"pattern: []string{"abc"}, analyzers: []*analysis.Analyzer{noop}, code1},
167        {name"no-multi-package-despite-error"pattern: []string{"xyz""abc"}, analyzers: []*analysis.Analyzer{noop}, code1},
168        // combination of type/parsing and different errors
169        {name"different-errors"pattern: []string{"file=" + path"xyz"}, analyzers: []*analysis.Analyzer{analyzernoop}, code1},
170        // non existing dir error
171        {name"no-match-dir"pattern: []string{"file=non/existing/dir"}, analyzers: []*analysis.Analyzer{analyzernoop}, code1},
172        // no errors
173        {name"no-errors"pattern: []string{"sort"}, analyzers: []*analysis.Analyzer{analyzernoop}, code0},
174    } {
175        if test.name == "despite-error" && testenv.Go1Point() < 20 {
176            // The behavior in the comment on the despite-error test only occurs for Go 1.20+.
177            continue
178        }
179        if got := checker.Run(test.patterntest.analyzers); got != test.code {
180            t.Errorf("got incorrect exit code %d for test %s; want %d"gottest.nametest.code)
181        }
182    }
183
184    defer cleanup()
185}
186
187type EmptyFact struct{}
188
189func (f *EmptyFactAFact() {}
190
MembersX
TestRunDespiteErrors.noopWithFact
TestApplyFixes.t
TestApplyFixes.want
TestApplyFixes.cleanup
TestApplyFixes.err
TestApplyFixes.contents
run
run.nodeFilter
filepath
analysistest
run.BlockStmt.BlockStmt.msg
TestRunDespiteErrors.path
TestRunDespiteErrors.RangeStmt_3090.BlockStmt.got
run.to
TestRunDespiteErrors.t
TestRunDespiteErrors.err
EmptyFact.AFact.f
ast
testing
checker
inspector
TestApplyFixes.path
TestApplyFixes.got
testenv
TestApplyFixes.testdata
run.from
TestRunDespiteErrors
TestRunDespiteErrors.testdata
TestRunDespiteErrors.RangeStmt_3090.test
inspect
EmptyFact.AFact
TestApplyFixes.files
TestRunDespiteErrors.files
TestRunDespiteErrors.cleanup
TestRunDespiteErrors.noop
TestApplyFixes
run.pass
EmptyFact
Members
X