github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1102/sa1102.go (about) 1 package sa1102 2 3 import ( 4 "go/ast" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/lint" 8 9 "golang.org/x/tools/go/analysis" 10 ) 11 12 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 13 Analyzer: &analysis.Analyzer{ 14 Name: "SA1102", 15 Run: run, 16 Requires: []*analysis.Analyzer{}, 17 }, 18 Doc: &lint.Documentation{ 19 Title: "log.Fatalln should not be called in package not named `main`", 20 Text: `log.Fatalln calls os.Exit(1) which is unexpected in packages not named 'main'`, 21 Since: "Unreleased", 22 Severity: lint.SeverityWarning, 23 }, 24 }) 25 26 var Analyzer = SCAnalyzer.Analyzer 27 28 func run(pass *analysis.Pass) (interface{}, error) { 29 // Check if the package name is 'main'. If it is, then do nothing. 30 if pass.Pkg.Name() == "main" { 31 return nil, nil 32 } 33 // Inspect the nodes in the AST. 34 for _, file := range pass.Files { 35 ast.Inspect(file, func(n ast.Node) bool { 36 // Look for call expressions (e.g., function calls). 37 call, ok := n.(*ast.CallExpr) 38 if !ok { 39 return true // continue to next node 40 } 41 // Check if the call is a function call. 42 fun, ok := call.Fun.(*ast.SelectorExpr) 43 if !ok { 44 return true 45 } 46 // Check if the function is 'Fatalln' and from the 'log' package. 47 if fun.Sel.Name == "Fatalln" { 48 pkgIdent, ok := fun.X.(*ast.Ident) 49 if !ok { 50 return true 51 } 52 obj := pass.TypesInfo.Uses[pkgIdent] 53 pkgName, ok := obj.(*types.PkgName) 54 if ok && pkgName.Imported().Path() == "log" { 55 pass.Reportf(call.Pos(), "use of log.Fatalln in package not named 'main'") 56 } 57 } 58 return true 59 }) 60 } 61 return nil, nil 62 }