qf1005.go (2628B)
1 package qf1005 2 3 import ( 4 "go/ast" 5 "go/constant" 6 "go/token" 7 "go/types" 8 9 "honnef.co/go/tools/analysis/code" 10 "honnef.co/go/tools/analysis/edit" 11 "honnef.co/go/tools/analysis/lint" 12 "honnef.co/go/tools/analysis/report" 13 "honnef.co/go/tools/go/ast/astutil" 14 "honnef.co/go/tools/pattern" 15 16 "golang.org/x/tools/go/analysis" 17 ) 18 19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 20 Analyzer: &analysis.Analyzer{ 21 Name: "QF1005", 22 Run: run, 23 Requires: code.RequiredAnalyzers, 24 }, 25 Doc: &lint.RawDocumentation{ 26 Title: `Expand call to \'math.Pow\'`, 27 Text: `Some uses of \'math.Pow\' can be simplified to basic multiplication.`, 28 Before: `math.Pow(x, 2)`, 29 After: `x * x`, 30 Since: "2021.1", 31 Severity: lint.SeverityHint, 32 }, 33 }) 34 35 var Analyzer = SCAnalyzer.Analyzer 36 37 var mathPowQ = pattern.MustParse(`(CallExpr (Symbol "math.Pow") [x (IntegerLiteral n)])`) 38 39 func run(pass *analysis.Pass) (any, error) { 40 for node, matcher := range code.Matches(pass, mathPowQ) { 41 x := matcher.State["x"].(ast.Expr) 42 if code.MayHaveSideEffects(pass, x, nil) { 43 continue 44 } 45 n, ok := constant.Int64Val(constant.ToInt(matcher.State["n"].(types.TypeAndValue).Value)) 46 if !ok { 47 continue 48 } 49 50 needConversion := false 51 if T, ok := pass.TypesInfo.Types[x]; ok && T.Value != nil { 52 info := types.Info{ 53 Types: map[ast.Expr]types.TypeAndValue{}, 54 } 55 56 // determine if the constant expression would have type float64 if used on its own 57 if err := types.CheckExpr(pass.Fset, pass.Pkg, x.Pos(), x, &info); err != nil { 58 // This should not happen 59 continue 60 } 61 if T, ok := info.Types[x].Type.(*types.Basic); ok { 62 if T.Kind() != types.UntypedFloat && T.Kind() != types.Float64 { 63 needConversion = true 64 } 65 } else { 66 needConversion = true 67 } 68 } 69 70 var replacement ast.Expr 71 switch n { 72 case 0: 73 replacement = &ast.BasicLit{ 74 Kind: token.FLOAT, 75 Value: "1.0", 76 } 77 case 1: 78 replacement = x 79 case 2, 3: 80 r := &ast.BinaryExpr{ 81 X: x, 82 Op: token.MUL, 83 Y: x, 84 } 85 for i := 3; i <= int(n); i++ { 86 r = &ast.BinaryExpr{ 87 X: r, 88 Op: token.MUL, 89 Y: x, 90 } 91 } 92 93 rc, ok := astutil.CopyExpr(r) 94 if !ok { 95 continue 96 } 97 replacement = astutil.SimplifyParentheses(rc) 98 default: 99 continue 100 } 101 if needConversion && n != 0 { 102 replacement = &ast.CallExpr{ 103 Fun: &ast.Ident{Name: "float64"}, 104 Args: []ast.Expr{replacement}, 105 } 106 } 107 report.Report(pass, node, "could expand call to math.Pow", 108 report.Fixes(edit.Fix("Expand call to math.Pow", edit.ReplaceWithNode(pass.Fset, node, replacement)))) 109 } 110 return nil, nil 111 }