github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/stylecheck/st1019/st1019.go (about)

     1  package st1019
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  
     7  	"github.com/amarpal/go-tools/analysis/facts/generated"
     8  	"github.com/amarpal/go-tools/analysis/lint"
     9  	"github.com/amarpal/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.Documentation{
    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          _ "fmt"
    31      )
    32  
    33  However, this is very rarely done on purpose. Usually, it is a
    34  sign of code that got refactored, accidentally adding duplicate
    35  import statements. It is also a rarely known feature, which may
    36  contribute to confusion.
    37  
    38  Do note that sometimes, this feature may be used
    39  intentionally (see for example
    40  https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d)
    41  – if you want to allow this pattern in your code base, you're
    42  advised to disable this check.`,
    43  		Since:   "2020.1",
    44  		MergeIf: lint.MergeIfAny,
    45  	},
    46  })
    47  
    48  var Analyzer = SCAnalyzer.Analyzer
    49  
    50  func run(pass *analysis.Pass) (interface{}, error) {
    51  	for _, f := range pass.Files {
    52  		// Collect all imports by their import path
    53  		imports := make(map[string][]*ast.ImportSpec, len(f.Imports))
    54  		for _, imp := range f.Imports {
    55  			imports[imp.Path.Value] = append(imports[imp.Path.Value], imp)
    56  		}
    57  
    58  		for path, value := range imports {
    59  			if path[1:len(path)-1] == "unsafe" {
    60  				// Don't flag unsafe. Cgo generated code imports
    61  				// unsafe using the blank identifier, and most
    62  				// user-written cgo code also imports unsafe
    63  				// explicitly.
    64  				continue
    65  			}
    66  			// If there's more than one import per path, we flag that
    67  			if len(value) > 1 {
    68  				s := fmt.Sprintf("package %s is being imported more than once", path)
    69  				opts := []report.Option{report.FilterGenerated()}
    70  				for _, imp := range value[1:] {
    71  					opts = append(opts, report.Related(imp, fmt.Sprintf("other import of %s", path)))
    72  				}
    73  				report.Report(pass, value[0], s, opts...)
    74  			}
    75  		}
    76  	}
    77  	return nil, nil
    78  }