mirror of
https://github.com/mgechev/revive.git
synced 2025-11-23 22:04:49 +02:00
feature: new rule use-new
This commit is contained in:
@@ -116,6 +116,7 @@ var allRules = append([]lint.Rule{
|
||||
&rule.InefficientMapLookupRule{},
|
||||
&rule.ForbiddenCallInWgGoRule{},
|
||||
&rule.UnnecessaryIfRule{},
|
||||
&rule.UseNewRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
// allFormatters is a list of all available formatters to output the linting results.
|
||||
|
||||
@@ -44,6 +44,8 @@ var (
|
||||
Go124 = goversion.Must(goversion.NewVersion("1.24"))
|
||||
// Go125 is a constant representing the Go version 1.25.
|
||||
Go125 = goversion.Must(goversion.NewVersion("1.25"))
|
||||
// Go126 is a constant representing the Go version 1.26.
|
||||
Go126 = goversion.Must(goversion.NewVersion("1.26"))
|
||||
)
|
||||
|
||||
// Files return package's files.
|
||||
|
||||
85
rule/use_new.go
Normal file
85
rule/use_new.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/mgechev/revive/internal/astutils"
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
// UseNewRule implements a rule that proposes using new(expr) when possible.
|
||||
type UseNewRule struct{}
|
||||
|
||||
// Apply applies the rule to given file.
|
||||
func (r *UseNewRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||
if !file.Pkg.IsAtLeastGoVersion(lint.Go126) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var failures []lint.Failure
|
||||
for _, decl := range file.AST.Decls {
|
||||
funcDecl, ok := decl.(*ast.FuncDecl)
|
||||
if !ok || funcDecl.Body == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
failures = append(failures, r.lintFunction(funcDecl)...)
|
||||
}
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (*UseNewRule) Name() string {
|
||||
return "use-new"
|
||||
}
|
||||
|
||||
func (r *UseNewRule) lintFunction(funcDecl *ast.FuncDecl) []lint.Failure {
|
||||
if !r.isNewValueFunc(funcDecl) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []lint.Failure{
|
||||
{
|
||||
Failure: fmt.Sprintf(`calls to "%s(value)" can be replaced by a call to "new(value)"`, funcDecl.Name.Name),
|
||||
Confidence: 1,
|
||||
Node: funcDecl,
|
||||
Category: lint.FailureCategoryOptimization,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// isNewValueFunc checks if the function is of the form:
|
||||
//
|
||||
// func foo(p Type) *Type {
|
||||
// return &p
|
||||
// }
|
||||
func (*UseNewRule) isNewValueFunc(funcDecl *ast.FuncDecl) bool {
|
||||
if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 {
|
||||
return false // not one return value
|
||||
}
|
||||
|
||||
if funcDecl.Type.Params == nil || len(funcDecl.Type.Params.List) != 1 {
|
||||
return false // not one parameter
|
||||
}
|
||||
|
||||
if len(funcDecl.Body.List) != 1 {
|
||||
return false // not one statement
|
||||
}
|
||||
|
||||
paramTypes := astutils.GetTypeNames(funcDecl.Type.Params)
|
||||
resultTypes := astutils.GetTypeNames(funcDecl.Type.Results)
|
||||
if "*"+paramTypes[0] != resultTypes[0] {
|
||||
return false // return type is not pointer to parameter type
|
||||
}
|
||||
|
||||
retStmt, ok := funcDecl.Body.List[0].(*ast.ReturnStmt)
|
||||
if !ok || len(retStmt.Results) != 1 {
|
||||
return false // not a return statement with one result
|
||||
}
|
||||
|
||||
returnExpr := astutils.GoFmt(retStmt.Results[0]) // TODO use more efficient way to retrieve the id
|
||||
|
||||
return returnExpr == "&"+funcDecl.Type.Params.List[0].Names[0].Name
|
||||
}
|
||||
13
test/use_new_test.go
Normal file
13
test/use_new_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
"github.com/mgechev/revive/rule"
|
||||
)
|
||||
|
||||
func TestUseNew(t *testing.T) {
|
||||
testRule(t, "use_new", &rule.UseNewRule{}, &lint.RuleConfig{})
|
||||
testRule(t, "go1.26/use_new", &rule.UseNewRule{}, &lint.RuleConfig{})
|
||||
}
|
||||
3
testdata/go1.26/go.mod
vendored
Normal file
3
testdata/go1.26/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/mgechev/revive/testdata
|
||||
|
||||
go 1.26
|
||||
5
testdata/go1.26/use_new.go
vendored
Normal file
5
testdata/go1.26/use_new.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package fixtures
|
||||
|
||||
func useNewOne(a int) *int { // MATCH /calls to "useNewOne(value)" can be replaced by a call to "new(value)"/
|
||||
return &a
|
||||
}
|
||||
5
testdata/use_new.go
vendored
Normal file
5
testdata/use_new.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package fixtures
|
||||
|
||||
func useNewOne(a int) *int {
|
||||
return &a
|
||||
}
|
||||
Reference in New Issue
Block a user