woodpecker/vendor/github.com/julz/importas/analyzer.go
Lukas c28f7cb29f
Add golangci-lint (#502)
Initial part of #435
2021-11-14 21:01:54 +01:00

116 lines
2.8 KiB
Go

package importas
import (
"fmt"
"go/ast"
"go/types"
"strconv"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
var config = &Config{
RequiredAlias: make(map[string]string),
}
var Analyzer = &analysis.Analyzer{
Name: "importas",
Doc: "Enforces consistent import aliases",
Run: run,
Flags: flags(config),
Requires: []*analysis.Analyzer{inspect.Analyzer},
}
func run(pass *analysis.Pass) (interface{}, error) {
return runWithConfig(config, pass)
}
func runWithConfig(config *Config, pass *analysis.Pass) (interface{}, error) {
if err := config.CompileRegexp(); err != nil {
return nil, err
}
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
inspect.Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, func(n ast.Node) {
visitImportSpecNode(config, n.(*ast.ImportSpec), pass)
})
return nil, nil
}
func visitImportSpecNode(config *Config, node *ast.ImportSpec, pass *analysis.Pass) {
if !config.DisallowUnaliased && node.Name == nil {
return
}
alias := ""
if node.Name != nil {
alias = node.Name.String()
}
if alias == "." {
return // Dot aliases are generally used in tests, so ignore.
}
if strings.HasPrefix(alias, "_") {
return // Used by go test and for auto-includes, not a conflict.
}
path, err := strconv.Unquote(node.Path.Value)
if err != nil {
pass.Reportf(node.Pos(), "import not quoted")
}
if required, exists := config.AliasFor(path); exists && required != alias {
message := fmt.Sprintf("import %q imported as %q but must be %q according to config", path, alias, required)
if alias == "" {
message = fmt.Sprintf("import %q imported without alias but must be with alias %q according to config", path, required)
}
pass.Report(analysis.Diagnostic{
Pos: node.Pos(),
End: node.End(),
Message: message,
SuggestedFixes: []analysis.SuggestedFix{{
Message: "Use correct alias",
TextEdits: findEdits(node, pass.TypesInfo.Uses, path, alias, required),
}},
})
}
}
func findEdits(node ast.Node, uses map[*ast.Ident]types.Object, importPath, original, required string) []analysis.TextEdit {
// Edit the actual import line.
result := []analysis.TextEdit{{
Pos: node.Pos(),
End: node.End(),
NewText: []byte(required + " " + strconv.Quote(importPath)),
}}
// Edit all the uses of the alias in the code.
for use, pkg := range uses {
pkgName, ok := pkg.(*types.PkgName)
if !ok {
// skip identifiers that aren't pointing at a PkgName.
continue
}
if pkgName.Pos() != node.Pos() {
// skip identifiers pointing to a different import statement.
continue
}
result = append(result, analysis.TextEdit{
Pos: use.Pos(),
End: use.End(),
NewText: []byte(required),
})
}
return result
}