sa9002.go raw

   1  package sa9002
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  	"strconv"
   7  
   8  	"honnef.co/go/tools/analysis/code"
   9  	"honnef.co/go/tools/analysis/edit"
  10  	"honnef.co/go/tools/analysis/lint"
  11  	"honnef.co/go/tools/analysis/report"
  12  	"honnef.co/go/tools/go/types/typeutil"
  13  
  14  	"golang.org/x/tools/go/analysis"
  15  	"golang.org/x/tools/go/analysis/passes/inspect"
  16  )
  17  
  18  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  19  	Analyzer: &analysis.Analyzer{
  20  		Name:     "SA9002",
  21  		Run:      run,
  22  		Requires: []*analysis.Analyzer{inspect.Analyzer},
  23  	},
  24  	Doc: &lint.RawDocumentation{
  25  		Title:    `Using a non-octal \'os.FileMode\' that looks like it was meant to be in octal.`,
  26  		Since:    "2017.1",
  27  		Severity: lint.SeverityWarning,
  28  		MergeIf:  lint.MergeIfAny,
  29  	},
  30  })
  31  
  32  var Analyzer = SCAnalyzer.Analyzer
  33  
  34  func run(pass *analysis.Pass) (interface{}, error) {
  35  	fn := func(node ast.Node) {
  36  		call := node.(*ast.CallExpr)
  37  		for _, arg := range call.Args {
  38  			lit, ok := arg.(*ast.BasicLit)
  39  			if !ok {
  40  				continue
  41  			}
  42  			if !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "os.FileMode") &&
  43  				!typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "io/fs.FileMode") {
  44  				continue
  45  			}
  46  			if len(lit.Value) == 3 &&
  47  				lit.Value[0] != '0' &&
  48  				lit.Value[0] >= '0' && lit.Value[0] <= '7' &&
  49  				lit.Value[1] >= '0' && lit.Value[1] <= '7' &&
  50  				lit.Value[2] >= '0' && lit.Value[2] <= '7' {
  51  
  52  				v, err := strconv.ParseInt(lit.Value, 10, 64)
  53  				if err != nil {
  54  					continue
  55  				}
  56  				report.Report(pass, arg, fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value),
  57  					report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(arg, "0"+lit.Value))))
  58  			}
  59  		}
  60  	}
  61  	code.Preorder(pass, fn, (*ast.CallExpr)(nil))
  62  	return nil, nil
  63  }
  64