st1019.go (2676B)
1 package st1019 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "honnef.co/go/tools/analysis/facts/generated" 8 "honnef.co/go/tools/analysis/lint" 9 "honnef.co/go/tools/analysis/report" 10 11 "golang.org/x/tools/go/analysis" 12 ) 13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 15 Analyzer: &analysis.Analyzer{ 16 Name: "ST1019", 17 Run: run, 18 Requires: []*analysis.Analyzer{generated.Analyzer}, 19 }, 20 Doc: &lint.RawDocumentation{ 21 Title: `Importing the same package multiple times`, 22 Text: `Go allows importing the same package multiple times, as long as 23 different import aliases are being used. That is, the following 24 bit of code is valid: 25 26 import ( 27 "fmt" 28 fumpt "fmt" 29 format "fmt" 30 ) 31 32 However, this is very rarely done on purpose. Usually, it is a 33 sign of code that got refactored, accidentally adding duplicate 34 import statements. It is also a rarely known feature, which may 35 contribute to confusion. 36 37 Do note that sometimes, this feature may be used 38 intentionally (see for example 39 https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d) 40 – if you want to allow this pattern in your code base, you're 41 advised to disable this check. 42 43 It is acceptable to import the same package twice if one of the imports 44 uses the blank identifier. This is allowed in order to increase 45 resilience against erroneous changes when using the same package for its 46 side effects as well as its exported API.`, 47 Since: "2020.1", 48 MergeIf: lint.MergeIfAny, 49 }, 50 }) 51 52 var Analyzer = SCAnalyzer.Analyzer 53 54 func run(pass *analysis.Pass) (any, error) { 55 for _, f := range pass.Files { 56 // Collect all imports by their import path 57 imports := make(map[string][]*ast.ImportSpec, len(f.Imports)) 58 for _, imp := range f.Imports { 59 if imp.Name != nil && imp.Name.Name == "_" { 60 // Allow blank imports to coexist with one normal import. 61 // 62 // We don't have to count the number of blank imports, 63 // goimports removes duplicates. 64 continue 65 } 66 imports[imp.Path.Value] = append(imports[imp.Path.Value], imp) 67 } 68 69 for path, value := range imports { 70 if path[1:len(path)-1] == "unsafe" { 71 // Don't flag unsafe. Cgo generated code imports 72 // unsafe as _cgo_unsafe, in addition to the user's import. 73 continue 74 } 75 // If there's more than one import per path, we flag that 76 if len(value) > 1 { 77 s := fmt.Sprintf("package %s is being imported more than once", path) 78 opts := []report.Option{report.FilterGenerated()} 79 for _, imp := range value[1:] { 80 opts = append(opts, report.Related(imp, fmt.Sprintf("other import of %s", path))) 81 } 82 report.Report(pass, value[0], s, opts...) 83 } 84 } 85 } 86 return nil, nil 87 }