package sa9002 import ( "fmt" "go/ast" "strconv" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/edit" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/types/typeutil" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "SA9002", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: `Using a non-octal \'os.FileMode\' that looks like it was meant to be in octal.`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, }, }) var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) for _, arg := range call.Args { lit, ok := arg.(*ast.BasicLit) if !ok { continue } if !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "os.FileMode") && !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "io/fs.FileMode") { continue } if len(lit.Value) == 3 && lit.Value[0] != '0' && lit.Value[0] >= '0' && lit.Value[0] <= '7' && lit.Value[1] >= '0' && lit.Value[1] <= '7' && lit.Value[2] >= '0' && lit.Value[2] <= '7' { v, err := strconv.ParseInt(lit.Value, 10, 64) if err != nil { continue } report.Report(pass, arg, fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value), report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(arg, "0"+lit.Value)))) } } } code.Preorder(pass, fn, (*ast.CallExpr)(nil)) return nil, nil }