github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/import-shadowing.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "strings" 8 9 "github.com/mgechev/revive/lint" 10 ) 11 12 // ImportShadowingRule lints given else constructs. 13 type ImportShadowingRule struct{} 14 15 // Apply applies the rule to given file. 16 func (r *ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 17 var failures []lint.Failure 18 19 importNames := map[string]struct{}{} 20 for _, imp := range file.AST.Imports { 21 importNames[getName(imp)] = struct{}{} 22 } 23 24 fileAst := file.AST 25 walker := importShadowing{ 26 importNames: importNames, 27 onFailure: func(failure lint.Failure) { 28 failures = append(failures, failure) 29 }, 30 alreadySeen: map[*ast.Object]struct{}{}, 31 } 32 33 ast.Walk(walker, fileAst) 34 35 return failures 36 } 37 38 // Name returns the rule name. 39 func (r *ImportShadowingRule) Name() string { 40 return "import-shadowing" 41 } 42 43 func getName(imp *ast.ImportSpec) string { 44 const pathSep = "/" 45 const strDelim = `"` 46 if imp.Name != nil { 47 return imp.Name.Name 48 } 49 50 path := imp.Path.Value 51 i := strings.LastIndex(path, pathSep) 52 if i == -1 { 53 return strings.Trim(path, strDelim) 54 } 55 56 return strings.Trim(path[i+1:], strDelim) 57 } 58 59 type importShadowing struct { 60 importNames map[string]struct{} 61 onFailure func(lint.Failure) 62 alreadySeen map[*ast.Object]struct{} 63 } 64 65 // Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name 66 func (w importShadowing) Visit(n ast.Node) ast.Visitor { 67 switch n := n.(type) { 68 case *ast.AssignStmt: 69 if n.Tok == token.DEFINE { 70 return w // analyze variable declarations of the form id := expr 71 } 72 73 return nil // skip assigns of the form id = expr (not an id declaration) 74 case *ast.CallExpr, // skip call expressions (not an id declaration) 75 *ast.ImportSpec, // skip import section subtree because we already have the list of imports 76 *ast.KeyValueExpr, // skip analysis of key-val expressions ({key:value}): ids of such expressions, even the same of an import name, do not shadow the import name 77 *ast.ReturnStmt, // skip skipping analysis of returns, ids in expression were already analyzed 78 *ast.SelectorExpr, // skip analysis of selector expressions (anId.otherId): because if anId shadows an import name, it was already detected, and otherId does not shadows the import name 79 *ast.StructType: // skip analysis of struct type because struct fields can not shadow an import name 80 return nil 81 case *ast.Ident: 82 id := n.Name 83 if id == "_" { 84 return w // skip _ id 85 } 86 87 _, isImportName := w.importNames[id] 88 _, alreadySeen := w.alreadySeen[n.Obj] 89 if isImportName && !alreadySeen { 90 w.onFailure(lint.Failure{ 91 Confidence: 1, 92 Node: n, 93 Category: "namming", 94 Failure: fmt.Sprintf("The name '%s' shadows an import name", id), 95 }) 96 97 w.alreadySeen[n.Obj] = struct{}{} 98 } 99 } 100 101 return w 102 }