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

     1  package sa9002
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"strconv"
     7  
     8  	"github.com/amarpal/go-tools/analysis/code"
     9  	"github.com/amarpal/go-tools/analysis/edit"
    10  	"github.com/amarpal/go-tools/analysis/lint"
    11  	"github.com/amarpal/go-tools/analysis/report"
    12  	"github.com/amarpal/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.Documentation{
    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.IsType(pass.TypesInfo.TypeOf(lit), "os.FileMode") &&
    43  				!typeutil.IsType(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  }