Skip to content

Commit ec83c21

Browse files
aputmangopherbot
authored andcommitted
go/analysis/passes/modernize: minmax: only remove exact userdefined
Make sure that the min/max logic in a user defined min max functions use the two function parameters. Also refactored the userdefined tests so that they actually test what they intend to (all of the moved tests would also fail the userdefined test just because of the name of their functions, not the reasons stated in the comments). Because of the rules of min max though, there can only be two tests per directory since the functions have to have the names "min" or "max". Fixes golang/go#79210 Change-Id: Id06fd15c54862270e86545e5c562588b91985283 Reviewed-on: https://go-review.googlesource.com/c/tools/+/774760 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com> Auto-Submit: Alex Putman <aputman@google.com>
1 parent 5625353 commit ec83c21

7 files changed

Lines changed: 84 additions & 87 deletions

File tree

go/analysis/passes/modernize/minmax.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -363,18 +363,18 @@ func canUseBuiltinMinMax(fn *types.Func, body *ast.BlockStmt) bool {
363363
return false
364364
}
365365

366-
return hasMinMaxLogic(body, fn.Name())
366+
return hasMinMaxLogic(body, fn.Name(), sig.Params().At(0).Name(), sig.Params().At(1).Name())
367367
}
368368

369369
// hasMinMaxLogic checks if the function body implements simple min/max logic.
370-
func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool {
370+
func hasMinMaxLogic(body *ast.BlockStmt, funcName, param0, param1 string) bool {
371371
// Pattern 1: Single if/else statement
372372
if len(body.List) == 1 {
373373
if ifStmt, ok := body.List[0].(*ast.IfStmt); ok {
374374
// Get the "false" result from the else block
375375
if elseBlock, ok := ifStmt.Else.(*ast.BlockStmt); ok && len(elseBlock.List) == 1 {
376376
if elseRet, ok := elseBlock.List[0].(*ast.ReturnStmt); ok && len(elseRet.Results) == 1 {
377-
return checkMinMaxPattern(ifStmt, elseRet.Results[0], funcName)
377+
return checkMinMaxPattern(ifStmt, elseRet.Results[0], funcName, param0, param1)
378378
}
379379
}
380380
}
@@ -384,7 +384,7 @@ func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool {
384384
if len(body.List) == 2 {
385385
if ifStmt, ok := body.List[0].(*ast.IfStmt); ok && ifStmt.Else == nil {
386386
if retStmt, ok := body.List[1].(*ast.ReturnStmt); ok && len(retStmt.Results) == 1 {
387-
return checkMinMaxPattern(ifStmt, retStmt.Results[0], funcName)
387+
return checkMinMaxPattern(ifStmt, retStmt.Results[0], funcName, param0, param1)
388388
}
389389
}
390390
}
@@ -396,7 +396,8 @@ func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool {
396396
// ifStmt: the if statement to check
397397
// falseResult: the expression returned when the condition is false
398398
// funcName: "min" or "max"
399-
func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName string) bool {
399+
// param0, param1: the two parameter names for the function.
400+
func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName, param0, param1 string) bool {
400401
// Must have condition with comparison
401402
cmp, ok := ifStmt.Cond.(*ast.BinaryExpr)
402403
if !ok {
@@ -419,10 +420,24 @@ func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName strin
419420
return false // Not a comparison operator
420421
}
421422

422-
t := thenRet.Results[0] // "true" result
423-
f := falseResult // "false" result
424-
x := cmp.X // left operand
425-
y := cmp.Y // right operand
423+
t := thenRet.Results[0] // "true" result
424+
f := falseResult // "false" result
425+
x, ok := cmp.X.(*ast.Ident) // left operand
426+
if !ok {
427+
return false // Not a basic min/max comparison
428+
}
429+
y, ok := cmp.Y.(*ast.Ident) // right operand
430+
if !ok {
431+
return false // Not a basic min/max comparison
432+
}
433+
434+
// Check that the min max algorithm uses the function's params
435+
// Which param corresponds to which part of the operation doesn't matter,
436+
// so we have to try both.
437+
if !(param0 == x.Name && param1 == y.Name ||
438+
param0 == y.Name && param1 == x.Name) {
439+
return false
440+
}
426441

427442
// Check operand order and adjust sign accordingly
428443
if astutil.EqualSyntax(t, x) && astutil.EqualSyntax(f, y) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package differentlogic
2+
3+
// User-defined min with different logic - should NOT be removed
4+
func min(a, b int) int {
5+
return a + b // Completely different logic
6+
}
7+
8+
// Function with complex body that doesn't match pattern - should NOT be removed
9+
func max(a, b int) int {
10+
println("choosing min")
11+
if a < b {
12+
return a
13+
} else {
14+
return b
15+
}
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package nanhandling
2+
3+
// User-defined min with float parameters - should NOT be removed due to NaN handling
4+
func min(a, b float64) float64 {
5+
if a < b {
6+
return a
7+
} else {
8+
return b
9+
}
10+
}
11+
12+
// User-defined max with float parameters - should NOT be removed due to NaN handling
13+
func max(a, b float64) float64 {
14+
if a > b {
15+
return a
16+
} else {
17+
return b
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package notuserparams
2+
3+
// User-defined max with correct logic but wrong vars - should NOT be removed
4+
func max(a, b int) int {
5+
if a < 5 {
6+
return a
7+
} else {
8+
return 5
9+
}
10+
}
11+
12+
// User-defined min with correct logic but wrong vars - should NOT be removed
13+
func min(a, b int) int {
14+
if 5 < b {
15+
return 5
16+
} else {
17+
return b
18+
}
19+
}
Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
package userdefined
22

3-
// User-defined min with float parameters - should NOT be removed due to NaN handling
4-
func minFloat(a, b float64) float64 {
5-
if a < b {
6-
return a
7-
} else {
8-
return b
9-
}
10-
}
11-
12-
// User-defined max with float parameters - should NOT be removed due to NaN handling
13-
func maxFloat(a, b float64) float64 {
14-
if a > b {
15-
return a
16-
} else {
17-
return b
18-
}
19-
}
20-
213
// User-defined function with different name - should NOT be removed
224
func minimum(a, b int) int {
235
if a < b {
@@ -27,11 +9,6 @@ func minimum(a, b int) int {
279
}
2810
}
2911

30-
// User-defined min with different logic - should NOT be removed
31-
func minDifferent(a, b int) int {
32-
return a + b // Completely different logic
33-
}
34-
3512
// Method on a type - should NOT be removed
3613
type MyType struct{}
3714

@@ -43,21 +20,6 @@ func (m MyType) min(a, b int) int {
4320
}
4421
}
4522

46-
// Function with wrong signature - should NOT be removed
47-
func minWrongSig(a int) int {
48-
return a
49-
}
50-
51-
// Function with complex body that doesn't match pattern - should NOT be removed
52-
func minComplex(a, b int) int {
53-
println("choosing min")
54-
if a < b {
55-
return a
56-
} else {
57-
return b
58-
}
59-
}
60-
6123
// min returns the smaller of two values.
6224
func min(a, b int) int { // want "user-defined min function is equivalent to built-in min and can be removed"
6325
if a < b {
@@ -74,5 +36,3 @@ func max(a, b int) int { // want "user-defined max function is equivalent to bui
7436
}
7537
return b
7638
}
77-
78-
Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
package userdefined
22

3-
// User-defined min with float parameters - should NOT be removed due to NaN handling
4-
func minFloat(a, b float64) float64 {
5-
if a < b {
6-
return a
7-
} else {
8-
return b
9-
}
10-
}
11-
12-
// User-defined max with float parameters - should NOT be removed due to NaN handling
13-
func maxFloat(a, b float64) float64 {
14-
if a > b {
15-
return a
16-
} else {
17-
return b
18-
}
19-
}
20-
213
// User-defined function with different name - should NOT be removed
224
func minimum(a, b int) int {
235
if a < b {
@@ -27,11 +9,6 @@ func minimum(a, b int) int {
279
}
2810
}
2911

30-
// User-defined min with different logic - should NOT be removed
31-
func minDifferent(a, b int) int {
32-
return a + b // Completely different logic
33-
}
34-
3512
// Method on a type - should NOT be removed
3613
type MyType struct{}
3714

@@ -43,19 +20,4 @@ func (m MyType) min(a, b int) int {
4320
}
4421
}
4522

46-
// Function with wrong signature - should NOT be removed
47-
func minWrongSig(a int) int {
48-
return a
49-
}
50-
51-
// Function with complex body that doesn't match pattern - should NOT be removed
52-
func minComplex(a, b int) int {
53-
println("choosing min")
54-
if a < b {
55-
return a
56-
} else {
57-
return b
58-
}
59-
}
60-
6123

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package wrongsignature
2+
3+
// Function with wrong signature - should NOT be removed
4+
func min(a int) int {
5+
return a
6+
}

0 commit comments

Comments
 (0)