woodpecker/vendor/github.com/julz/importas/analyzer.go
6543 56a854fe14
Update deps (#789)
* update github.com/docker/cli

* update github.com/docker/distribution

* update github.com/docker/docker

* update github.com/gin-gonic/gin

* update github.com/golang-jwt/jwt/v4

* update github.com/golangci/golangci-lint

* update github.com/gorilla/securecookie

* update github.com/mattn/go-sqlite3

* update github.com/moby/moby

* update github.com/prometheus/client_golang

* update github.com/xanzy/go-gitlab
2022-02-24 17:33:24 +01:00

141 lines
3.5 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),
}},
})
} else if !exists && config.DisallowExtraAliases {
pass.Report(analysis.Diagnostic{
Pos: node.Pos(),
End: node.End(),
Message: fmt.Sprintf("import %q has alias %q which is not part of config", path, alias),
SuggestedFixes: []analysis.SuggestedFix{{
Message: "remove alias",
TextEdits: findEdits(node, pass.TypesInfo.Uses, path, alias, ""),
}},
})
}
}
func findEdits(node ast.Node, uses map[*ast.Ident]types.Object, importPath, original, required string) []analysis.TextEdit {
// Edit the actual import line.
importLine := strconv.Quote(importPath)
if required != "" {
importLine = required + " " + importLine
}
result := []analysis.TextEdit{{
Pos: node.Pos(),
End: node.End(),
NewText: []byte(importLine),
}}
packageReplacement := required
if required == "" {
packageParts := strings.Split(importPath, "/")
if len(packageParts) != 0 {
packageReplacement = packageParts[len(packageParts)-1]
} else {
// fall back to original
packageReplacement = original
}
}
// 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(packageReplacement),
})
}
return result
}