Files
odidere/vendor/honnef.co/go/tools/quickfix/qf1005/qf1005.go
2026-06-06 01:17:29 +00:00

112 lines
2.6 KiB
Go

package qf1005
import (
"go/ast"
"go/constant"
"go/token"
"go/types"
"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/ast/astutil"
"honnef.co/go/tools/pattern"
"golang.org/x/tools/go/analysis"
)
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
Analyzer: &analysis.Analyzer{
Name: "QF1005",
Run: run,
Requires: code.RequiredAnalyzers,
},
Doc: &lint.RawDocumentation{
Title: `Expand call to \'math.Pow\'`,
Text: `Some uses of \'math.Pow\' can be simplified to basic multiplication.`,
Before: `math.Pow(x, 2)`,
After: `x * x`,
Since: "2021.1",
Severity: lint.SeverityHint,
},
})
var Analyzer = SCAnalyzer.Analyzer
var mathPowQ = pattern.MustParse(`(CallExpr (Symbol "math.Pow") [x (IntegerLiteral n)])`)
func run(pass *analysis.Pass) (any, error) {
for node, matcher := range code.Matches(pass, mathPowQ) {
x := matcher.State["x"].(ast.Expr)
if code.MayHaveSideEffects(pass, x, nil) {
continue
}
n, ok := constant.Int64Val(constant.ToInt(matcher.State["n"].(types.TypeAndValue).Value))
if !ok {
continue
}
needConversion := false
if T, ok := pass.TypesInfo.Types[x]; ok && T.Value != nil {
info := types.Info{
Types: map[ast.Expr]types.TypeAndValue{},
}
// determine if the constant expression would have type float64 if used on its own
if err := types.CheckExpr(pass.Fset, pass.Pkg, x.Pos(), x, &info); err != nil {
// This should not happen
continue
}
if T, ok := info.Types[x].Type.(*types.Basic); ok {
if T.Kind() != types.UntypedFloat && T.Kind() != types.Float64 {
needConversion = true
}
} else {
needConversion = true
}
}
var replacement ast.Expr
switch n {
case 0:
replacement = &ast.BasicLit{
Kind: token.FLOAT,
Value: "1.0",
}
case 1:
replacement = x
case 2, 3:
r := &ast.BinaryExpr{
X: x,
Op: token.MUL,
Y: x,
}
for i := 3; i <= int(n); i++ {
r = &ast.BinaryExpr{
X: r,
Op: token.MUL,
Y: x,
}
}
rc, ok := astutil.CopyExpr(r)
if !ok {
continue
}
replacement = astutil.SimplifyParentheses(rc)
default:
continue
}
if needConversion && n != 0 {
replacement = &ast.CallExpr{
Fun: &ast.Ident{Name: "float64"},
Args: []ast.Expr{replacement},
}
}
report.Report(pass, node, "could expand call to math.Pow",
report.Fixes(edit.Fix("Expand call to math.Pow", edit.ReplaceWithNode(pass.Fset, node, replacement))))
}
return nil, nil
}