mirror of
				https://github.com/mgechev/revive.git
				synced 2025-10-30 23:37: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