[tools] go/analysis/passes/modernize: directly remove user-defined min/max functions

6 views
Skip to first unread message

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:00:06 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

shuang cui has uploaded the change for review

Commit message

go/analysis/passes/modernize: directly remove user-defined min/max functions

If the parameters, return values, and logic of the user-defined min/max are
identical to those of the standard library min/max, then remove the
user-defined functions.
Change-Id: I881e463489e963f4eb033188e77ee205675d0738

Change diff

diff --git a/go/analysis/passes/modernize/minmax.go b/go/analysis/passes/modernize/minmax.go
index bd815c6..bb0486e 100644
--- a/go/analysis/passes/modernize/minmax.go
+++ b/go/analysis/passes/modernize/minmax.go
@@ -31,9 +31,10 @@
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#minmax",
}

-// The minmax pass replaces if/else statements with calls to min or max.
+// The minmax pass replaces if/else statements with calls to min or max,
+// and removes user-defined min/max functions that are equivalent to built-ins.
//
-// Patterns:
+// If/else replacement patterns:
//
// 1. if a < b { x = a } else { x = b } => x = min(a, b)
// 2. x = a; if a < b { x = b } => x = max(a, b)
@@ -42,6 +43,11 @@
// is not Nan. Since this is hard to prove, we reject floating-point
// numbers.
//
+// Function removal:
+// User-defined min/max functions are suggested for removal if they have
+// the same signature and logic as built-in min/max, except when they
+// accept floating-point parameters (due to different NaN handling).
+//
// Variants:
// - all four ordered comparisons
// - "x := a" or "x = a" or "var x = a" in pattern 2
@@ -49,6 +55,9 @@
func minmax(pass *analysis.Pass) (any, error) {
skipGenerated(pass)

+ // Check for user-defined min/max functions that can be removed
+ checkUserDefinedMinMax(pass)
+
// check is called for all statements of this form:
// if a < b { lhs = rhs }
check := func(file *ast.File, curIfStmt inspector.Cursor, compare *ast.BinaryExpr) {
@@ -275,6 +284,279 @@
return false
}

+// checkUserDefinedMinMax looks for user-defined min/max functions that are
+// equivalent to the built-in functions and suggests removing them.
+func checkUserDefinedMinMax(pass *analysis.Pass) {
+ inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+ inspect.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node) {
+ funcDecl := n.(*ast.FuncDecl)
+
+ // Skip methods and functions without names
+ if funcDecl.Recv != nil || funcDecl.Name == nil {
+ return
+ }
+
+ funcName := funcDecl.Name.Name
+ if funcName != "min" && funcName != "max" {
+ return
+ }
+
+ // Check if this function matches the built-in min/max signature and behavior
+ if isEquivalentToBuiltinMinMax(pass, funcDecl, funcName) {
+ pass.Report(analysis.Diagnostic{
+ Pos: funcDecl.Pos(),
+ End: funcDecl.End(),
+ Message: fmt.Sprintf("user-defined %s function is equivalent to built-in %s and can be removed", funcName, funcName),
+ SuggestedFixes: []analysis.SuggestedFix{{
+ Message: fmt.Sprintf("Remove user-defined %s function", funcName),
+ TextEdits: []analysis.TextEdit{{
+ Pos: funcDecl.Pos(),
+ End: funcDecl.End(),
+ NewText: []byte(""),
+ }},
+ }},
+ })
+ }
+ })
+}
+
+// isEquivalentToBuiltinMinMax checks if a user-defined function is equivalent
+// to the built-in min/max function. Returns false if the function accepts
+// floating-point parameters due to NaN handling differences.
+func isEquivalentToBuiltinMinMax(pass *analysis.Pass, funcDecl *ast.FuncDecl, funcName string) bool {
+ // Must have a function type
+ if funcDecl.Type == nil {
+ return false
+ }
+
+ // Must have at least 2 parameters
+ if funcDecl.Type.Params == nil {
+ return false
+ }
+
+ // Count total parameter names
+ paramCount := 0
+ for _, param := range funcDecl.Type.Params.List {
+ paramCount += len(param.Names)
+ }
+
+ if paramCount < 2 {
+ return false
+ }
+
+ // Check if any parameter might be floating-point
+ for _, param := range funcDecl.Type.Params.List {
+ if paramType := pass.TypesInfo.TypeOf(param.Type); paramType != nil && maybeNaN(paramType) {
+ return false // Don't suggest removal for float types due to NaN handling
+ }
+ }
+
+ // Must have exactly one return value of the same type as parameters
+ if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 {
+ return false
+ }
+
+ // Check that the function body implements the expected min/max logic
+ if funcDecl.Body == nil {
+ return false
+ }
+
+ return hasMinMaxLogic(funcDecl.Body, funcName)
+}
+
+// hasMinMaxLogic checks if the function body implements typical min/max logic.
+// This is a heuristic check for common patterns like:
+// - if a < b { return a } else { return b } (for min)
+// - if a > b { return a } else { return b } (for max)
+// - if a < b { return a }; return b (for min)
+// - if a > b { return a }; return b (for max)
+// - for loops with comparisons for variadic versions
+func hasMinMaxLogic(body *ast.BlockStmt, funcName string) bool {
+ if len(body.List) == 0 {
+ return false
+ }
+
+ // Look for simple if/else return pattern
+ for _, stmt := range body.List {
+ if ifStmt, ok := stmt.(*ast.IfStmt); ok {
+ if hasSimpleMinMaxIfElse(ifStmt, funcName) {
+ return true
+ }
+ }
+
+ // Look for for-loop with min/max logic (variadic case)
+ if forStmt, ok := stmt.(*ast.ForStmt); ok {
+ if hasMinMaxForLoop(forStmt, funcName) {
+ return true
+ }
+ }
+
+ // Look for range loop with min/max logic
+ if rangeStmt, ok := stmt.(*ast.RangeStmt); ok {
+ if hasMinMaxRangeLoop(rangeStmt, funcName) {
+ return true
+ }
+ }
+ }
+
+ // Check for pattern: if a < b { return a }; return b
+ if len(body.List) == 2 {
+ if ifStmt, ok := body.List[0].(*ast.IfStmt); ok {
+ if retStmt, ok := body.List[1].(*ast.ReturnStmt); ok {
+ return hasMinMaxIfReturnPattern(ifStmt, retStmt, funcName)
+ }
+ }
+ }
+
+ return false
+}
+
+// hasSimpleMinMaxIfElse checks for patterns like:
+// if a < b { return a } else { return b }
+// or: if a < b { return a }; return b
+func hasSimpleMinMaxIfElse(ifStmt *ast.IfStmt, funcName string) bool {
+ // Must have condition with comparison
+ binExpr, ok := ifStmt.Cond.(*ast.BinaryExpr)
+ if !ok {
+ return false
+ }
+
+ // Check comparison operator matches function name
+ expectedOp := token.LSS // for min: if a < b
+ if funcName == "max" {
+ expectedOp = token.GTR // for max: if a > b
+ }
+
+ if binExpr.Op != expectedOp && binExpr.Op != token.LEQ && binExpr.Op != token.GEQ {
+ return false
+ }
+
+ // Check if then branch returns one of the compared values
+ if ifStmt.Body == nil || len(ifStmt.Body.List) != 1 {
+ return false
+ }
+
+ thenRet, ok := ifStmt.Body.List[0].(*ast.ReturnStmt)
+ if !ok || len(thenRet.Results) != 1 {
+ return false
+ }
+
+ // Check for explicit else block
+ if elseBlock, ok := ifStmt.Else.(*ast.BlockStmt); ok {
+ if len(elseBlock.List) != 1 {
+ return false
+ }
+
+ elseRet, ok := elseBlock.List[0].(*ast.ReturnStmt)
+ if !ok || len(elseRet.Results) != 1 {
+ return false
+ }
+
+ // Check that the returns match the comparison operands
+ // For min: if a < b { return a } else { return b }
+ // For max: if a > b { return a } else { return b }
+ return (equalSyntax(thenRet.Results[0], binExpr.X) && equalSyntax(elseRet.Results[0], binExpr.Y)) ||
+ (equalSyntax(thenRet.Results[0], binExpr.Y) && equalSyntax(elseRet.Results[0], binExpr.X))
+ }
+
+ // If no explicit else, the pattern should be checked at the function level
+ // This will be handled by hasMinMaxLogic
+ return false
+}
+
+// hasMinMaxForLoop checks for for-loop patterns that implement min/max logic
+func hasMinMaxForLoop(forStmt *ast.ForStmt, funcName string) bool {
+ // This is a simplified check - in practice, you'd want more sophisticated
+ // pattern matching for variadic min/max implementations
+ if forStmt.Body == nil {
+ return false
+ }
+
+ // Look for assignment statements with comparisons in the loop body
+ for _, stmt := range forStmt.Body.List {
+ if ifStmt, ok := stmt.(*ast.IfStmt); ok {
+ if binExpr, ok := ifStmt.Cond.(*ast.BinaryExpr); ok {
+ expectedOp := token.LSS
+ if funcName == "max" {
+ expectedOp = token.GTR
+ }
+ if binExpr.Op == expectedOp || binExpr.Op == token.LEQ || binExpr.Op == token.GEQ {
+ return true
+ }
+ }
+ }
+ }
+
+ return false
+}
+
+// hasMinMaxRangeLoop checks for range-loop patterns that implement min/max logic
+func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
+ // Similar to hasMinMaxForLoop but for range statements
+ if rangeStmt.Body == nil {
+ return false
+ }
+
+ for _, stmt := range rangeStmt.Body.List {
+ if ifStmt, ok := stmt.(*ast.IfStmt); ok {
+ if binExpr, ok := ifStmt.Cond.(*ast.BinaryExpr); ok {
+ expectedOp := token.LSS
+ if funcName == "max" {
+ expectedOp = token.GTR
+ }
+ if binExpr.Op == expectedOp || binExpr.Op == token.LEQ || binExpr.Op == token.GEQ {
+ return true
+ }
+ }
+ }
+ }
+
+ return false
+}
+
+// hasMinMaxIfReturnPattern checks for patterns like:
+// if a < b { return a }; return b (for min)
+// if a > b { return a }; return b (for max)
+func hasMinMaxIfReturnPattern(ifStmt *ast.IfStmt, retStmt *ast.ReturnStmt, funcName string) bool {
+ // Must have condition with comparison
+ binExpr, ok := ifStmt.Cond.(*ast.BinaryExpr)
+ if !ok {
+ return false
+ }
+
+ // Check comparison operator matches function name
+ expectedOp := token.LSS // for min: if a < b
+ if funcName == "max" {
+ expectedOp = token.GTR // for max: if a > b
+ }
+
+ if binExpr.Op != expectedOp && binExpr.Op != token.LEQ && binExpr.Op != token.GEQ {
+ return false
+ }
+
+ // Check if then branch returns one of the compared values
+ if ifStmt.Body == nil || len(ifStmt.Body.List) != 1 {
+ return false
+ }
+
+ thenRet, ok := ifStmt.Body.List[0].(*ast.ReturnStmt)
+ if !ok || len(thenRet.Results) != 1 {
+ return false
+ }
+
+ // Check final return statement
+ if len(retStmt.Results) != 1 {
+ return false
+ }
+
+ // Check that the returns match the comparison operands
+ // For min: if a < b { return a }; return b
+ // For max: if a > b { return a }; return b
+ return (equalSyntax(thenRet.Results[0], binExpr.X) && equalSyntax(retStmt.Results[0], binExpr.Y)) ||
+ (equalSyntax(thenRet.Results[0], binExpr.Y) && equalSyntax(retStmt.Results[0], binExpr.X))
+}
+
// -- utils --

func is[T any](x any) bool {
diff --git a/go/analysis/passes/modernize/testdata/src/minmax/minmax.go b/go/analysis/passes/modernize/testdata/src/minmax/minmax.go
index 74d84b2..49e670f 100644
--- a/go/analysis/passes/modernize/testdata/src/minmax/minmax.go
+++ b/go/analysis/passes/modernize/testdata/src/minmax/minmax.go
@@ -169,3 +169,77 @@
}
return x
}
+
+func min(a, b int) int { // want "user-defined min function is equivalent to built-in min and can be removed"
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+func max(a, b int) int { // want "user-defined max function is equivalent to built-in max and can be removed"
+ if a > b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined min with float parameters - should NOT be removed due to NaN handling
+func minFloat(a, b float64) float64 {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined max with float parameters - should NOT be removed due to NaN handling
+func maxFloat(a, b float64) float64 {
+ if a > b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined function with different name - should NOT be removed
+func minimum(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined min with different logic - should NOT be removed
+func minDifferent(a, b int) int {
+ return a + b // Completely different logic
+}
+
+// Method on a type - should NOT be removed
+type MyType struct{}
+
+func (m MyType) min(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// Function with wrong signature - should NOT be removed
+func minWrongSig(a int) int {
+ return a
+}
+
+// Function with complex body that doesn't match pattern - should NOT be removed
+func minComplex(a, b int) int {
+ print("choosing min")
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
diff --git a/go/analysis/passes/modernize/testdata/src/minmax/minmax.go.golden b/go/analysis/passes/modernize/testdata/src/minmax/minmax.go.golden
index f8dc94b..dcf169c 100644
--- a/go/analysis/passes/modernize/testdata/src/minmax/minmax.go.golden
+++ b/go/analysis/passes/modernize/testdata/src/minmax/minmax.go.golden
@@ -168,3 +168,61 @@
}
return x
}
+
+// User-defined min with float parameters - should NOT be removed due to NaN handling
+func minFloat(a, b float64) float64 {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined max with float parameters - should NOT be removed due to NaN handling
+func maxFloat(a, b float64) float64 {
+ if a > b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined function with different name - should NOT be removed
+func minimum(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// User-defined min with different logic - should NOT be removed
+func minDifferent(a, b int) int {
+ return a + b // Completely different logic
+}
+
+// Method on a type - should NOT be removed
+type MyType struct{}
+
+func (m MyType) min(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// Function with wrong signature - should NOT be removed
+func minWrongSig(a int) int {
+ return a
+}
+
+// Function with complex body that doesn't match pattern - should NOT be removed
+func minComplex(a, b int) int {
+ print("choosing min")
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}

Change information

Files:
  • M go/analysis/passes/modernize/minmax.go
  • M go/analysis/passes/modernize/testdata/src/minmax/minmax.go
  • M go/analysis/passes/modernize/testdata/src/minmax/minmax.go.golden
Change size: L
Delta: 3 files changed, 416 insertions(+), 2 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 1
Gerrit-Owner: shuang cui <imc...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:00:17 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

shuang cui voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 1
Gerrit-Owner: shuang cui <imc...@gmail.com>
Gerrit-Reviewer: shuang cui <imc...@gmail.com>
Gerrit-Comment-Date: Tue, 30 Sep 2025 03:00:11 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:49:05 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Alan Donovan and shuang cui

shuang cui uploaded new patchset

shuang cui uploaded patch set #2 to this change.
Following approvals got outdated and were removed:
  • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
  • shuang cui
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 2
Gerrit-Owner: shuang cui <imc...@gmail.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: shuang cui <imc...@gmail.com>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Michael Matloob <mat...@golang.org>
Gerrit-CC: Robert Findley <rfin...@google.com>
Gerrit-Attention: Alan Donovan <adon...@google.com>
Gerrit-Attention: shuang cui <imc...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:49:30 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, Go LUCI, golang-co...@googlegroups.com
Attention needed from Alan Donovan

shuang cui voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 2
Gerrit-Owner: shuang cui <imc...@gmail.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: shuang cui <imc...@gmail.com>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Michael Matloob <mat...@golang.org>
Gerrit-CC: Robert Findley <rfin...@google.com>
Gerrit-Attention: Alan Donovan <adon...@google.com>
Gerrit-Comment-Date: Tue, 30 Sep 2025 03:49:23 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:53:21 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Alan Donovan

shuang cui uploaded new patchset

shuang cui uploaded patch set #3 to this change.
Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 3
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 29, 2025, 11:53:48 PM (2 days ago) Sep 29
to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Alan Donovan

shuang cui voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
Gerrit-Change-Number: 707915
Gerrit-PatchSet: 3
Gerrit-Owner: shuang cui <imc...@gmail.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: shuang cui <imc...@gmail.com>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Michael Matloob <mat...@golang.org>
Gerrit-CC: Robert Findley <rfin...@google.com>
Gerrit-Attention: Alan Donovan <adon...@google.com>
Gerrit-Comment-Date: Tue, 30 Sep 2025 03:53:41 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Sep 30, 2025, 12:25:26 AM (2 days ago) Sep 30
to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Alan Donovan

shuang cui added 1 comment

Patchset-level comments
File-level comment, Patchset 3 (Latest):
shuang cui . resolved

Thanks @adon...@google.com for the help and advice.

The functionality is complete, and I have added the corresponding test cases.

However, I'm not sure if there is a simpler way to detect this situation with the current tools.

Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: tools
    Gerrit-Branch: master
    Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
    Gerrit-Change-Number: 707915
    Gerrit-PatchSet: 3
    Gerrit-Owner: shuang cui <imc...@gmail.com>
    Gerrit-Reviewer: Alan Donovan <adon...@google.com>
    Gerrit-Reviewer: shuang cui <imc...@gmail.com>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-CC: Michael Matloob <mat...@golang.org>
    Gerrit-CC: Robert Findley <rfin...@google.com>
    Gerrit-Attention: Alan Donovan <adon...@google.com>
    Gerrit-Comment-Date: Tue, 30 Sep 2025 04:25:19 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Alan Donovan (Gerrit)

    unread,
    Sep 30, 2025, 10:02:10 AM (2 days ago) Sep 30
    to shuang cui, goph...@pubsubhelper.golang.org, Go LUCI, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from shuang cui

    Alan Donovan added 6 comments

    File go/analysis/passes/modernize/minmax.go
    Line 292, Patchset 3 (Latest): inspect.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node) {
    Alan Donovan . unresolved

    Rather than making a pass over all the declarations, since we're only looking for two functions, why not look them up by name? Use `pass.Pkg.Scope().Lookup("min")` check that it's a `*types.Func`. You can use typeindexanalyzer (`Index.Def(obj)`) to find its FuncDecl in constant time.

    Line 314, Patchset 3 (Latest): Pos: funcDecl.Pos(),
    Alan Donovan . unresolved
    This should expand to include any preceding doc comment. You might want to steal this code from gopls' `unusedfunc` analyzer (code that probably one day belongs in a library):
    ```
    // Expand to include leading doc comment.
    pos := node.Pos()
    if doc := docComment(node); doc != nil {
    pos = doc.Pos()
    }

    func docComment(n ast.Node) *ast.CommentGroup {
    switch n := n.(type) {
    case *ast.FuncDecl:
    return n.Doc
    case *ast.GenDecl:
    return n.Doc
    case *ast.ValueSpec:
    return n.Doc
    case *ast.TypeSpec:
    return n.Doc
    }
    return nil // includes File, ImportSpec, Field
    }
    ```
    Line 316, Patchset 3 (Latest): NewText: []byte(""),
    Alan Donovan . unresolved

    You can delete this line.

    Line 328, Patchset 3 (Latest): // Must have a function type
    if funcDecl.Type == nil {
    return false

    }

    // Must have at least 2 parameters
    if funcDecl.Type.Params == nil {
    return false

    }

    // Count total parameter names
    paramCount := 0

    for _, param := range funcDecl.Type.Params.List {
    paramCount += len(param.Names)
    }

    if paramCount < 2 {
    return false

    }

    // Check if any parameter might be floating-point
    for _, param := range funcDecl.Type.Params.List {
    if paramType := pass.TypesInfo.TypeOf(param.Type); paramType != nil && maybeNaN(paramType) {
    return false // Don't suggest removal for float types due to NaN handling
    }
    }

    // Must have exactly one return value of the same type as parameters
    if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 {
    return false
    }
    Alan Donovan . unresolved

    This operation is more conveniently expressed on the types.Signature instead of the FuncDecl syntax.

    Line 376, Patchset 3 (Latest): if len(body.List) == 0 {
    return false
    }
    Alan Donovan . unresolved

    You can delete this; it doesn't save any work.

    Line 495, Patchset 3 (Latest):func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
    Alan Donovan . unresolved

    I don't understand the relationship of this function to the rest of the change. Ditto the hasMinMaxForLoop. Can you explain?

    Might I ask: was this code primarily written by an LLM? If so, I think we might need to establish some clear ground rules for their use in contributions.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • shuang cui
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: tools
      Gerrit-Branch: master
      Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
      Gerrit-Change-Number: 707915
      Gerrit-PatchSet: 3
      Gerrit-Owner: shuang cui <imc...@gmail.com>
      Gerrit-Reviewer: Alan Donovan <adon...@google.com>
      Gerrit-Reviewer: shuang cui <imc...@gmail.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Michael Matloob <mat...@golang.org>
      Gerrit-CC: Robert Findley <rfin...@google.com>
      Gerrit-Attention: shuang cui <imc...@gmail.com>
      Gerrit-Comment-Date: Tue, 30 Sep 2025 14:02:00 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      shuang cui (Gerrit)

      unread,
      Sep 30, 2025, 11:33:49 PM (2 days ago) Sep 30
      to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
      Attention needed from Alan Donovan

      shuang cui added 2 comments

      File go/analysis/passes/modernize/minmax.go
      Line 292, Patchset 3 (Latest): inspect.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node) {
      Alan Donovan . resolved

      Rather than making a pass over all the declarations, since we're only looking for two functions, why not look them up by name? Use `pass.Pkg.Scope().Lookup("min")` check that it's a `*types.Func`. You can use typeindexanalyzer (`Index.Def(obj)`) to find its FuncDecl in constant time.

      shuang cui

      Thanks for your suggestion, I'll make the changes later.

      Line 495, Patchset 3 (Latest):func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
      Alan Donovan . unresolved

      I don't understand the relationship of this function to the rest of the change. Ditto the hasMinMaxForLoop. Can you explain?

      Might I ask: was this code primarily written by an LLM? If so, I think we might need to establish some clear ground rules for their use in contributions.

      shuang cui

      Yes, I used Claude to generate some logic here. But the problem with hasMinMaxLogic and hasMinMaxRangeLoop is actually more my fault: Before making this suggestion, my goal was to eliminate all use of min/max in Go libraries, and I had already "implemented" this locally—but this is unrealistic. Some min/max functions aren't part of the standard library and shouldn't be replaced.

      For example, https://github.com/golang/crypto/blob/master/ssh/channel.go#L134 and https://github.com/golang/go/blob/master/test/235.go#L26 (and there are many more).

      My misunderstanding and unrealistic expectations led to Claude overdoing it. However, the test case is designed for situations like the one in the title, which pass perfectly.

      Sorry for any inconvenience caused. I agree with your point of view. I will review it again and again after submitting it, not just through the test cases.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Alan Donovan
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: tools
      Gerrit-Branch: master
      Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
      Gerrit-Change-Number: 707915
      Gerrit-PatchSet: 3
      Gerrit-Owner: shuang cui <imc...@gmail.com>
      Gerrit-Reviewer: Alan Donovan <adon...@google.com>
      Gerrit-Reviewer: shuang cui <imc...@gmail.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Michael Matloob <mat...@golang.org>
      Gerrit-CC: Robert Findley <rfin...@google.com>
      Gerrit-Attention: Alan Donovan <adon...@google.com>
      Gerrit-Comment-Date: Wed, 01 Oct 2025 03:33:41 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Alan Donovan <adon...@google.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      shuang cui (Gerrit)

      unread,
      Sep 30, 2025, 11:41:48 PM (2 days ago) Sep 30
      to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
      Attention needed from Alan Donovan

      shuang cui added 1 comment

      File go/analysis/passes/modernize/minmax.go
      Line 495, Patchset 3 (Latest):func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
      Alan Donovan . unresolved

      I don't understand the relationship of this function to the rest of the change. Ditto the hasMinMaxForLoop. Can you explain?

      Might I ask: was this code primarily written by an LLM? If so, I think we might need to establish some clear ground rules for their use in contributions.

      shuang cui

      Yes, I used Claude to generate some logic here. But the problem with hasMinMaxLogic and hasMinMaxRangeLoop is actually more my fault: Before making this suggestion, my goal was to eliminate all use of min/max in Go libraries, and I had already "implemented" this locally—but this is unrealistic. Some min/max functions aren't part of the standard library and shouldn't be replaced.

      For example, https://github.com/golang/crypto/blob/master/ssh/channel.go#L134 and https://github.com/golang/go/blob/master/test/235.go#L26 (and there are many more).

      My misunderstanding and unrealistic expectations led to Claude overdoing it. However, the test case is designed for situations like the one in the title, which pass perfectly.

      Sorry for any inconvenience caused. I agree with your point of view. I will review it again and again after submitting it, not just through the test cases.

      shuang cui

      It seems that I may have ignored the case of https://github.com/golang/crypto/blob/master/ssh/channel.go#L134, but did not rule out https://github.com/golang/go/blob/master/test/235.go#L26 and other cases

      Gerrit-Comment-Date: Wed, 01 Oct 2025 03:41:40 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Alan Donovan <adon...@google.com>
      Comment-In-Reply-To: shuang cui <imc...@gmail.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      shuang cui (Gerrit)

      unread,
      Oct 1, 2025, 7:45:09 AM (19 hours ago) Oct 1
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Alan Donovan

      shuang cui uploaded new patchset

      shuang cui uploaded patch set #4 to this change.
      Following approvals got outdated and were removed:
      • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Alan Donovan
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 4
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:08:41 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui uploaded new patchset

        shuang cui uploaded patch set #5 to this change.
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 5
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:09:27 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui voted Commit-Queue+1

        Commit-Queue+1
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 5
        Gerrit-Owner: shuang cui <imc...@gmail.com>
        Gerrit-Reviewer: Alan Donovan <adon...@google.com>
        Gerrit-Reviewer: shuang cui <imc...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Michael Matloob <mat...@golang.org>
        Gerrit-CC: Robert Findley <rfin...@google.com>
        Gerrit-Attention: Alan Donovan <adon...@google.com>
        Gerrit-Comment-Date: Wed, 01 Oct 2025 12:09:17 +0000
        Gerrit-HasComments: No
        Gerrit-Has-Labels: Yes
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:19:15 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Alan Donovan and shuang cui

        shuang cui uploaded new patchset

        shuang cui uploaded patch set #6 to this change.
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        • shuang cui
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 6
        Gerrit-Owner: shuang cui <imc...@gmail.com>
        Gerrit-Reviewer: Alan Donovan <adon...@google.com>
        Gerrit-Reviewer: shuang cui <imc...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Michael Matloob <mat...@golang.org>
        Gerrit-CC: Robert Findley <rfin...@google.com>
        Gerrit-Attention: Alan Donovan <adon...@google.com>
        Gerrit-Attention: shuang cui <imc...@gmail.com>
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:22:27 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Alan Donovan and shuang cui

        shuang cui uploaded new patchset

        shuang cui uploaded patch set #7 to this change.
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        • shuang cui
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 7
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:28:27 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui added 1 comment

        Patchset-level comments
        File-level comment, Patchset 7 (Latest):
        shuang cui . resolved

        In fact, according to the standard library definition of min/max, the number of arguments can be in the range [1, +∞). For example, both min(1) and min(1, 2, 3, 4, -1) are valid. Here we should only consider the most common case, where the number of arguments is 2.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 7
        Gerrit-Owner: shuang cui <imc...@gmail.com>
        Gerrit-Reviewer: Alan Donovan <adon...@google.com>
        Gerrit-Reviewer: shuang cui <imc...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Michael Matloob <mat...@golang.org>
        Gerrit-CC: Robert Findley <rfin...@google.com>
        Gerrit-Attention: Alan Donovan <adon...@google.com>
        Gerrit-Comment-Date: Wed, 01 Oct 2025 12:28:17 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:31:02 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui uploaded new patchset

        shuang cui uploaded patch set #8 to this change.
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: newpatchset
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 8
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 8:46:36 AM (18 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui voted Commit-Queue+1

        Commit-Queue+1
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
        Gerrit-Change-Number: 707915
        Gerrit-PatchSet: 8
        Gerrit-Owner: shuang cui <imc...@gmail.com>
        Gerrit-Reviewer: Alan Donovan <adon...@google.com>
        Gerrit-Reviewer: shuang cui <imc...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Michael Matloob <mat...@golang.org>
        Gerrit-CC: Robert Findley <rfin...@google.com>
        Gerrit-Attention: Alan Donovan <adon...@google.com>
        Gerrit-Comment-Date: Wed, 01 Oct 2025 12:46:30 +0000
        Gerrit-HasComments: No
        Gerrit-Has-Labels: Yes
        unsatisfied_requirement
        open
        diffy

        shuang cui (Gerrit)

        unread,
        Oct 1, 2025, 9:16:28 AM (17 hours ago) Oct 1
        to goph...@pubsubhelper.golang.org, Go LUCI, Alan Donovan, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        shuang cui voted and added 6 comments

        Votes added by shuang cui

        Commit-Queue+1

        6 comments

        File go/analysis/passes/modernize/minmax.go
        Line 292, Patchset 3: inspect.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node) {
        Alan Donovan . resolved

        Rather than making a pass over all the declarations, since we're only looking for two functions, why not look them up by name? Use `pass.Pkg.Scope().Lookup("min")` check that it's a `*types.Func`. You can use typeindexanalyzer (`Index.Def(obj)`) to find its FuncDecl in constant time.

        shuang cui

        Thanks for your suggestion, I'll make the changes later.

        shuang cui

        Using pass.Pkg.Scope().Lookup is indeed a better solution; I didn’t know it could be done this way before.

        Some parts were assisted by Claude, but I reviewed them again myself. I also made slight adjustments to the test cases to cover more scenarios.

        Line 314, Patchset 3: Pos: funcDecl.Pos(),
        Alan Donovan . resolved
        This should expand to include any preceding doc comment. You might want to steal this code from gopls' `unusedfunc` analyzer (code that probably one day belongs in a library):
        ```
        // Expand to include leading doc comment.
        pos := node.Pos()
        if doc := docComment(node); doc != nil {
        pos = doc.Pos()
        }

        func docComment(n ast.Node) *ast.CommentGroup {
        switch n := n.(type) {
        case *ast.FuncDecl:
        return n.Doc
        case *ast.GenDecl:
        return n.Doc
        case *ast.ValueSpec:
        return n.Doc
        case *ast.TypeSpec:
        return n.Doc
        }
        return nil // includes File, ImportSpec, Field
        }
        ```
        shuang cui

        Done

        Line 316, Patchset 3: NewText: []byte(""),
        Alan Donovan . resolved

        You can delete this line.

        shuang cui

        Done

        Line 328, Patchset 3: // Must have a function type

        if funcDecl.Type == nil {
        return false
        }

        // Must have at least 2 parameters
        if funcDecl.Type.Params == nil {
        return false
        }

        // Count total parameter names
        paramCount := 0
        for _, param := range funcDecl.Type.Params.List {
        paramCount += len(param.Names)
        }

        if paramCount < 2 {
        return false
        }

        // Check if any parameter might be floating-point
        for _, param := range funcDecl.Type.Params.List {
        if paramType := pass.TypesInfo.TypeOf(param.Type); paramType != nil && maybeNaN(paramType) {
        return false // Don't suggest removal for float types due to NaN handling
        }
        }

        // Must have exactly one return value of the same type as parameters
        if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 {
        return false
        }
        Alan Donovan . resolved

        This operation is more conveniently expressed on the types.Signature instead of the FuncDecl syntax.

        shuang cui

        Done

        Line 376, Patchset 3: if len(body.List) == 0 {
        return false
        }
        Alan Donovan . resolved

        You can delete this; it doesn't save any work.

        shuang cui

        Done

        Line 495, Patchset 3:func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
        Alan Donovan . resolved

        I don't understand the relationship of this function to the rest of the change. Ditto the hasMinMaxForLoop. Can you explain?

        Might I ask: was this code primarily written by an LLM? If so, I think we might need to establish some clear ground rules for their use in contributions.

        shuang cui

        Yes, I used Claude to generate some logic here. But the problem with hasMinMaxLogic and hasMinMaxRangeLoop is actually more my fault: Before making this suggestion, my goal was to eliminate all use of min/max in Go libraries, and I had already "implemented" this locally—but this is unrealistic. Some min/max functions aren't part of the standard library and shouldn't be replaced.

        For example, https://github.com/golang/crypto/blob/master/ssh/channel.go#L134 and https://github.com/golang/go/blob/master/test/235.go#L26 (and there are many more).

        My misunderstanding and unrealistic expectations led to Claude overdoing it. However, the test case is designed for situations like the one in the title, which pass perfectly.

        Sorry for any inconvenience caused. I agree with your point of view. I will review it again and again after submitting it, not just through the test cases.

        shuang cui

        It seems that I may have ignored the case of https://github.com/golang/crypto/blob/master/ssh/channel.go#L134, but did not rule out https://github.com/golang/go/blob/master/test/235.go#L26 and other cases

        shuang cui

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
          Gerrit-Change-Number: 707915
          Gerrit-PatchSet: 8
          Gerrit-Owner: shuang cui <imc...@gmail.com>
          Gerrit-Reviewer: Alan Donovan <adon...@google.com>
          Gerrit-Reviewer: shuang cui <imc...@gmail.com>
          Gerrit-CC: Gopher Robot <go...@golang.org>
          Gerrit-CC: Michael Matloob <mat...@golang.org>
          Gerrit-CC: Robert Findley <rfin...@google.com>
          Gerrit-Attention: Alan Donovan <adon...@google.com>
          Gerrit-Comment-Date: Wed, 01 Oct 2025 13:16:19 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: Yes
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy

          Alan Donovan (Gerrit)

          unread,
          Oct 1, 2025, 11:04:40 AM (15 hours ago) Oct 1
          to shuang cui, goph...@pubsubhelper.golang.org, Go LUCI, Michael Matloob, Robert Findley, Gopher Robot, golang-co...@googlegroups.com
          Attention needed from shuang cui

          Alan Donovan added 17 comments

          Patchset-level comments
          File-level comment, Patchset 8 (Latest):
          Alan Donovan . resolved

          Thanks, this all looks sound (but for one question about token.LEQ), but I think we can better factor the code to avoid repetition.

          File go/analysis/passes/modernize/minmax.go
          Line 48, Patchset 8 (Latest):// the same signature and logic as built-in min/max, except when they
          Alan Donovan . unresolved

          The logic of built-in min/max is not something we can easily know (it's a compiler intrinsic), and in fact it is more complex due to NaNs. So let's say simply:
          ```
          // User-defined min/max functions are suggested for removal if they may
          // be safely replaced by their built-in namesake.
          ```
          and leave the definition of safety to the logic below.

          Line 292, Patchset 8 (Latest): if obj := pass.Pkg.Scope().Lookup(funcName); obj != nil {
          if fn, ok := obj.(*types.Func); ok {
          Alan Donovan . unresolved

          Combine:

          ```
          if fn, ok := pass.Pkg.Scope().Lookup(funcName).(*types.Func); ok {
          ```

          Line 295, Patchset 8 (Latest): inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

          inspect.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node) {
          decl := n.(*ast.FuncDecl)
          if decl.Name != nil && decl.Name.Name == funcName {
          // Verify this is the same function by checking position
          if pass.TypesInfo.Defs[decl.Name] == fn {
          Alan Donovan . unresolved

          This could be replaced by
          ```
          decl := index.Def(fn).Node().(*ast.FuncDecl)
          ```
          where
          ```
          index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)
          ```

          Line 302, Patchset 8 (Latest): if isEquivalentToBuiltinMinMax(pass, fn, decl, funcName) {
          Alan Donovan . unresolved

          strength-reduce to just decl.Body

          Line 330, Patchset 8 (Latest):// isEquivalentToBuiltinMinMax checks if a user-defined function is equivalent
          Alan Donovan . unresolved

          Function equivalence is an undecidable problem. Let's go with `canUseBuiltinMinMax`, which does not promise the impossible.

          ```
          // canUseBuiltinMinMax reports whether it is safe to replace a call
          // to this min or max function by its built-in namesake.
          ```

          We needn't mention floats here; it's an implementation detail covered by the logic.

          Line 333, Patchset 8 (Latest):func isEquivalentToBuiltinMinMax(pass *analysis.Pass, fn *types.Func, funcDecl *ast.FuncDecl, funcName string) bool {
          Alan Donovan . unresolved

          Redundant; use `fn.Name()`.

          Line 333, Patchset 8 (Latest):func isEquivalentToBuiltinMinMax(pass *analysis.Pass, fn *types.Func, funcDecl *ast.FuncDecl, funcName string) bool {
          Alan Donovan . unresolved

          unused

          Line 342, Patchset 8 (Latest): for i := 0; i < sig.Params().Len(); i++ {
          Alan Donovan . unresolved

          for param := range sig.Params().Variables() {

          Line 385, Patchset 8 (Latest): binExpr, ok := ifStmt.Cond.(*ast.BinaryExpr)
          Alan Donovan . unresolved

          cmp (since it's a comparison)

          Line 396, Patchset 8 (Latest): if binExpr.Op != expectedOp && binExpr.Op != token.LEQ && binExpr.Op != token.GEQ {
          Alan Donovan . unresolved

          What's going on here? Could you add a test case that exercises LEQ?

          Line 396, Patchset 8 (Latest): if binExpr.Op != expectedOp && binExpr.Op != token.LEQ && binExpr.Op != token.GEQ {
          Alan Donovan . unresolved

          `cond(name == "max", token.GTR, token.LSS)`

          Line 401, Patchset 8 (Latest): if ifStmt.Body == nil || len(ifStmt.Body.List) != 1 {
          Alan Donovan . unresolved

          Always true for an IfStmt; delete.

          Line 429, Patchset 8 (Latest):func hasMinMaxIfReturnPattern(ifStmt *ast.IfStmt, retStmt *ast.ReturnStmt, funcName string) bool {
          Alan Donovan . unresolved

          This function is substantially similar to the previous one. Please factor the common parts. The only difference is how to find the "false" result expression.

          Once that's done, I think the merged logic could all go in hasMinMaxLogic, again factoring the handling of IfStmt. Once you have the two expressions, everything else is common to both forms.

          Line 442, Patchset 8 (Latest): if binExpr.Op != expectedOp && binExpr.Op != token.LEQ && binExpr.Op != token.GEQ {
          Alan Donovan . unresolved

          ditto

          Line 468, Patchset 8 (Latest):// docComment returns the doc comment for a node, if any.
          Alan Donovan . unresolved

          Move this below the `-- utils --` fold.

          (We should move many of these helpers into a library at some point.)

          Line 495, Patchset 3:func hasMinMaxRangeLoop(rangeStmt *ast.RangeStmt, funcName string) bool {
          Alan Donovan . resolved

          I don't understand the relationship of this function to the rest of the change. Ditto the hasMinMaxForLoop. Can you explain?

          Might I ask: was this code primarily written by an LLM? If so, I think we might need to establish some clear ground rules for their use in contributions.

          shuang cui

          Yes, I used Claude to generate some logic here. But the problem with hasMinMaxLogic and hasMinMaxRangeLoop is actually more my fault: Before making this suggestion, my goal was to eliminate all use of min/max in Go libraries, and I had already "implemented" this locally—but this is unrealistic. Some min/max functions aren't part of the standard library and shouldn't be replaced.

          For example, https://github.com/golang/crypto/blob/master/ssh/channel.go#L134 and https://github.com/golang/go/blob/master/test/235.go#L26 (and there are many more).

          My misunderstanding and unrealistic expectations led to Claude overdoing it. However, the test case is designed for situations like the one in the title, which pass perfectly.

          Sorry for any inconvenience caused. I agree with your point of view. I will review it again and again after submitting it, not just through the test cases.

          shuang cui

          It seems that I may have ignored the case of https://github.com/golang/crypto/blob/master/ssh/channel.go#L134, but did not rule out https://github.com/golang/go/blob/master/test/235.go#L26 and other cases

          shuang cui

          Done

          Alan Donovan

          I see; thanks for the explanation, that makes sense. I suspect the number of variadic min/max functions is rather smaller than the binary ones, and probably not worth handling as the combinatorics of syntax explode pretty quickly.

          Open in Gerrit

          Related details

          Attention is currently required from:
          • shuang cui
          Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement is not satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: tools
            Gerrit-Branch: master
            Gerrit-Change-Id: I881e463489e963f4eb033188e77ee205675d0738
            Gerrit-Change-Number: 707915
            Gerrit-PatchSet: 8
            Gerrit-Owner: shuang cui <imc...@gmail.com>
            Gerrit-Reviewer: Alan Donovan <adon...@google.com>
            Gerrit-Reviewer: shuang cui <imc...@gmail.com>
            Gerrit-CC: Gopher Robot <go...@golang.org>
            Gerrit-CC: Michael Matloob <mat...@golang.org>
            Gerrit-CC: Robert Findley <rfin...@google.com>
            Gerrit-Attention: shuang cui <imc...@gmail.com>
            Gerrit-Comment-Date: Wed, 01 Oct 2025 15:04:34 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy
            Reply all
            Reply to author
            Forward
            0 new messages