github.com/songshiyun/revive@v1.1.5-0.20220323112655-f8433a19b3c5/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/songshiyun/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 packageNameIdent: fileAst.Name, 27 importNames: importNames, 28 onFailure: func(failure lint.Failure) { 29 failures = append(failures, failure) 30 }, 31 alreadySeen: map[*ast.Object]struct{}{}, 32 } 33 34 ast.Walk(walker, fileAst) 35 36 return failures 37 } 38 39 // Name returns the rule name. 40 func (r *ImportShadowingRule) Name() string { 41 return "import-shadowing" 42 } 43 44 func getName(imp *ast.ImportSpec) string { 45 const pathSep = "/" 46 const strDelim = `"` 47 if imp.Name != nil { 48 return imp.Name.Name 49 } 50 51 path := imp.Path.Value 52 i := strings.LastIndex(path, pathSep) 53 if i == -1 { 54 return strings.Trim(path, strDelim) 55 } 56 57 return strings.Trim(path[i+1:], strDelim) 58 } 59 60 type importShadowing struct { 61 packageNameIdent *ast.Ident 62 importNames map[string]struct{} 63 onFailure func(lint.Failure) 64 alreadySeen map[*ast.Object]struct{} 65 } 66 67 // Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name 68 func (w importShadowing) Visit(n ast.Node) ast.Visitor { 69 switch n := n.(type) { 70 case *ast.AssignStmt: 71 if n.Tok == token.DEFINE { 72 return w // analyze variable declarations of the form id := expr 73 } 74 75 return nil // skip assigns of the form id = expr (not an id declaration) 76 case *ast.CallExpr, // skip call expressions (not an id declaration) 77 *ast.ImportSpec, // skip import section subtree because we already have the list of imports 78 *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 79 *ast.ReturnStmt, // skip skipping analysis of returns, ids in expression were already analyzed 80 *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 81 *ast.StructType: // skip analysis of struct type because struct fields can not shadow an import name 82 return nil 83 case *ast.Ident: 84 if n == w.packageNameIdent { 85 return nil // skip the ident corresponding to the package name of this file 86 } 87 88 id := n.Name 89 if id == "_" { 90 return w // skip _ id 91 } 92 93 _, isImportName := w.importNames[id] 94 _, alreadySeen := w.alreadySeen[n.Obj] 95 if isImportName && !alreadySeen { 96 w.onFailure(lint.Failure{ 97 Confidence: 1, 98 Node: n, 99 Category: "namming", 100 Failure: fmt.Sprintf("The name '%s' shadows an import name", id), 101 }) 102 103 w.alreadySeen[n.Obj] = struct{}{} 104 } 105 } 106 107 return w 108 }