Skip to content

Commit 841f0c0

Browse files
sachaosjulz
andauthored
Strict mode (#4)
* Add new test case * Fix wrong test description * Implement strict mode * Add strict flag * Add testcase * Add test case for strict mode * Prepare config instances for each test case * Add README about strict mode * Apply suggestions from code review Co-authored-by: Julian Friedman <[email protected]> * Fix for review * Rename -strict flag to -no-unaliased Co-authored-by: Julian Friedman <[email protected]>
1 parent a22c8f7 commit 841f0c0

File tree

10 files changed

+112
-29
lines changed

10 files changed

+112
-29
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ importas \
1919
./...
2020
~~~~
2121

22+
### `-no-unaliased` option
23+
24+
By default, importas allows non-aliased imports, even when the package is specified by `-alias` flag.
25+
With `-no-unaliased` option, importas does not allow this.
26+
27+
~~~~
28+
importas -no-unaliased \
29+
-alias knative.dev/serving/pkg/apis/autoscaling/v1alpha1:autoscalingv1alpha1 \
30+
-alias knative.dev/serving/pkg/apis/serving/v1:servingv1 \
31+
./...
32+
~~~~
33+
2234
### Use regular expression
2335

2436
You can specify the package path by regular expression, and alias by regular expression replacement syntax like following snippet.

analyzer.go

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,20 @@ import (
1212
"golang.org/x/tools/go/ast/inspector"
1313
)
1414

15+
var config = &Config{
16+
RequiredAlias: make(map[string]string),
17+
}
18+
1519
var Analyzer = &analysis.Analyzer{
1620
Name: "importas",
1721
Doc: "Enforces consistent import aliases",
1822
Run: run,
1923

20-
Flags: flags(),
24+
Flags: flags(config),
2125

2226
Requires: []*analysis.Analyzer{inspect.Analyzer},
2327
}
2428

25-
var config = &Config{
26-
RequiredAlias: make(map[string]string),
27-
}
28-
2929
func run(pass *analysis.Pass) (interface{}, error) {
3030
return runWithConfig(config, pass)
3131
}
@@ -37,18 +37,22 @@ func runWithConfig(config *Config, pass *analysis.Pass) (interface{}, error) {
3737

3838
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
3939
inspect.Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, func(n ast.Node) {
40-
visitImportSpecNode(n.(*ast.ImportSpec), pass)
40+
visitImportSpecNode(config, n.(*ast.ImportSpec), pass)
4141
})
4242

4343
return nil, nil
4444
}
4545

46-
func visitImportSpecNode(node *ast.ImportSpec, pass *analysis.Pass) {
47-
if node.Name == nil {
48-
return // not aliased at all, ignore. (Maybe add strict mode for this?).
46+
func visitImportSpecNode(config *Config, node *ast.ImportSpec, pass *analysis.Pass) {
47+
if !config.DisallowUnaliased && node.Name == nil {
48+
return
49+
}
50+
51+
alias := ""
52+
if node.Name != nil {
53+
alias = node.Name.String()
4954
}
5055

51-
alias := node.Name.String()
5256
if alias == "." {
5357
return // Dot aliases are generally used in tests, so ignore.
5458
}
@@ -63,10 +67,15 @@ func visitImportSpecNode(node *ast.ImportSpec, pass *analysis.Pass) {
6367
}
6468

6569
if required, exists := config.AliasFor(path); exists && required != alias {
70+
message := fmt.Sprintf("import %q imported as %q but must be %q according to config", path, alias, required)
71+
if alias == "" {
72+
message = fmt.Sprintf("import %q imported without alias but must be with alias %q according to config", path, required)
73+
}
74+
6675
pass.Report(analysis.Diagnostic{
6776
Pos: node.Pos(),
6877
End: node.End(),
69-
Message: fmt.Sprintf("import %q imported as %q but must be %q according to config", path, alias, required),
78+
Message: message,
7079
SuggestedFixes: []analysis.SuggestedFix{{
7180
Message: "Use correct alias",
7281
TextEdits: findEdits(node, pass.TypesInfo.Uses, path, alias, required),
@@ -96,13 +105,11 @@ func findEdits(node ast.Node, uses map[*ast.Ident]types.Object, importPath, orig
96105
continue
97106
}
98107

99-
if original == pkgName.Name() {
100-
result = append(result, analysis.TextEdit{
101-
Pos: use.Pos(),
102-
End: use.End(),
103-
NewText: []byte(required),
104-
})
105-
}
108+
result = append(result, analysis.TextEdit{
109+
Pos: use.Pos(),
110+
End: use.End(),
111+
NewText: []byte(required),
112+
})
106113
}
107114

108115
return result

analyzer_test.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@ import (
55
"os"
66
"os/exec"
77
"path/filepath"
8+
"strconv"
89
"testing"
910

11+
"golang.org/x/tools/go/analysis"
1012
"golang.org/x/tools/go/analysis/analysistest"
13+
"golang.org/x/tools/go/analysis/passes/inspect"
1114
)
1215

1316
func TestAnalyzer(t *testing.T) {
1417
testdata := analysistest.TestData()
1518

1619
testCases := []struct {
17-
desc string
18-
pkg string
19-
aliases stringMap
20+
desc string
21+
pkg string
22+
aliases stringMap
23+
disallowUnaliased bool
2024
}{
2125
{
22-
desc: "Valid imports",
26+
desc: "Invalid imports",
2327
pkg: "a",
2428
aliases: stringMap{
2529
"fmt": "fff",
2630
"os": "stdos",
31+
"io": "iio",
2732
},
2833
},
2934
{
30-
desc: "Invalid imports",
35+
desc: "Valid imports",
3136
pkg: "b",
3237
aliases: stringMap{
3338
"fmt": "fff",
@@ -49,6 +54,16 @@ func TestAnalyzer(t *testing.T) {
4954
"knative.dev/serving/pkg/apis/(\\w+)/(v[\\w\\d]+)": "$1$2",
5055
},
5156
},
57+
{
58+
desc: "disallow unaliased mode",
59+
pkg: "e",
60+
aliases: stringMap{
61+
"fmt": "fff",
62+
"os": "stdos",
63+
"io": "iio",
64+
},
65+
disallowUnaliased: true,
66+
},
5267
}
5368

5469
for _, test := range testCases {
@@ -71,7 +86,17 @@ func TestAnalyzer(t *testing.T) {
7186
}
7287
}
7388

74-
a := Analyzer
89+
cnf := Config{
90+
RequiredAlias: make(map[string]string),
91+
}
92+
flgs := flags(&cnf)
93+
a := &analysis.Analyzer{
94+
Flags: flgs,
95+
Run: func(pass *analysis.Pass) (interface{}, error) {
96+
return runWithConfig(&cnf, pass)
97+
},
98+
Requires: []*analysis.Analyzer{inspect.Analyzer},
99+
}
75100

76101
flg := a.Flags.Lookup("alias")
77102
for k, v := range test.aliases {
@@ -81,7 +106,12 @@ func TestAnalyzer(t *testing.T) {
81106
}
82107
}
83108

84-
analysistest.RunWithSuggestedFixes(t, testdata, Analyzer, test.pkg)
109+
noUnaliasedFlg := a.Flags.Lookup("no-unaliased")
110+
if err := noUnaliasedFlg.Value.Set(strconv.FormatBool(test.disallowUnaliased)); err != nil {
111+
t.Fatal(err)
112+
}
113+
114+
analysistest.RunWithSuggestedFixes(t, testdata, a, test.pkg)
85115
})
86116
}
87117
}

config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
)
88

99
type Config struct {
10-
RequiredAlias map[string]string
11-
Rules []*Rule
10+
RequiredAlias map[string]string
11+
Rules []*Rule
12+
DisallowUnaliased bool
1213
}
1314

1415
func (c *Config) CompileRegexp() error {

flags.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import (
77
"strings"
88
)
99

10-
func flags() flag.FlagSet {
10+
func flags(config *Config) flag.FlagSet {
1111
fs := flag.FlagSet{}
1212
fs.Var(stringMap(config.RequiredAlias), "alias", "required import alias in form path:alias")
13+
fs.BoolVar(&config.DisallowUnaliased, "no-unaliased", false, "do not allow unaliased imports of aliased packages")
1314
return fs
1415
}
1516

testdata/src/a/a.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package a
22

33
import (
44
wrong_alias "fmt" // want `import "fmt" imported as "wrong_alias" but must be "fff" according to config`
5+
"io"
56
wrong_alias_again "os" // want `import "os" imported as "wrong_alias_again" but must be "stdos" according to config`
67
)
78

89
func ImportAsWrongAlias() {
910
wrong_alias.Println("foo")
1011
wrong_alias_again.Stdout.WriteString("bar")
12+
io.Pipe()
1113
}

testdata/src/a/a.go.golden

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package a
22

33
import (
4-
fff "fmt" // want `import "fmt" imported as "wrong_alias" but must be "fff" according to config`
4+
fff "fmt" // want `import "fmt" imported as "wrong_alias" but must be "fff" according to config`
5+
"io"
56
stdos "os" // want `import "os" imported as "wrong_alias_again" but must be "stdos" according to config`
67
)
78

89
func ImportAsWrongAlias() {
910
fff.Println("foo")
1011
stdos.Stdout.WriteString("bar")
12+
io.Pipe()
1113
}

testdata/src/b/b.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package b
33
import (
44
fff "fmt"
55
stdos "os"
6+
"io"
67
)
78

89
func ImportAsWrongAlias() {
910
fff.Println("foo")
1011
stdos.Stdout.WriteString("bar")
12+
io.Pipe()
1113
}

testdata/src/e/e.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package e
2+
3+
import (
4+
wrong_alias "fmt" // want `import "fmt" imported as "wrong_alias" but must be "fff" according to config`
5+
"io" // want `import "io" imported without alias but must be with alias "iio" according to config`
6+
wrong_alias_again "os" // want `import "os" imported as "wrong_alias_again" but must be "stdos" according to config`
7+
)
8+
9+
func ImportAsWrongAlias() {
10+
wrong_alias.Println("foo")
11+
wrong_alias_again.Stdout.WriteString("bar")
12+
io.Pipe()
13+
}

testdata/src/e/e.go.golden

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package e
2+
3+
import (
4+
fff "fmt" // want `import "fmt" imported as "wrong_alias" but must be "fff" according to config`
5+
iio "io" // want `import "io" imported without alias but must be with alias "iio" according to config`
6+
stdos "os" // want `import "os" imported as "wrong_alias_again" but must be "stdos" according to config`
7+
)
8+
9+
func ImportAsWrongAlias() {
10+
fff.Println("foo")
11+
stdos.Stdout.WriteString("bar")
12+
iio.Pipe()
13+
}

0 commit comments

Comments
 (0)