From 823cbeef4cde2901a8002d181647ded1f7219d7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:25:59 +0000 Subject: [PATCH 1/2] Initial plan From cbc76bae1aee77d93b3f4c10f78f5b7741196dff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:10:08 +0000 Subject: [PATCH 2/2] Add astutil.Root and migrate two linters to Cursor traversal Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/linters/httpstatuscode/httpstatuscode.go | 21 ++++++---------- pkg/linters/internal/astutil/astutil.go | 10 ++++++++ pkg/linters/sortslice/sortslice.go | 26 +++++++++----------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/pkg/linters/httpstatuscode/httpstatuscode.go b/pkg/linters/httpstatuscode/httpstatuscode.go index 702e3a1584b..dbca642295a 100644 --- a/pkg/linters/httpstatuscode/httpstatuscode.go +++ b/pkg/linters/httpstatuscode/httpstatuscode.go @@ -91,34 +91,29 @@ var httpStatusNames = map[int]string{ } func run(pass *analysis.Pass) (any, error) { - insp, err := astutil.Inspector(pass) + root, err := astutil.Root(pass) if err != nil { return nil, err } noLintLinesByFile := nolint.BuildLineIndex(pass, "httpstatuscode") - nodeFilter := []ast.Node{ - (*ast.BinaryExpr)(nil), - (*ast.SwitchStmt)(nil), - } - - insp.Preorder(nodeFilter, func(n ast.Node) { - switch node := n.(type) { + for cur := range root.Preorder((*ast.BinaryExpr)(nil), (*ast.SwitchStmt)(nil)) { + switch node := cur.Node().(type) { case *ast.BinaryExpr: if node.Op != token.EQL && node.Op != token.NEQ { - return + continue } lit, other := extractStatusLiteral(node) if lit == nil { - return + continue } if !isHTTPStatusContext(pass, other) { - return + continue } checkAndReport(pass, lit, noLintLinesByFile) case *ast.SwitchStmt: if node.Tag == nil || !isHTTPStatusContext(pass, node.Tag) { - return + continue } for _, s := range node.Body.List { cc, ok := s.(*ast.CaseClause) @@ -134,7 +129,7 @@ func run(pass *analysis.Pass) (any, error) { } } } - }) + } return nil, nil } diff --git a/pkg/linters/internal/astutil/astutil.go b/pkg/linters/internal/astutil/astutil.go index a5877ccf3b1..d2fe94ce111 100644 --- a/pkg/linters/internal/astutil/astutil.go +++ b/pkg/linters/internal/astutil/astutil.go @@ -103,6 +103,16 @@ func Inspector(pass *analysis.Pass) (*inspector.Inspector, error) { return insp, nil } +// Root extracts the inspector root cursor from pass.ResultOf. +// It returns an error if the inspect result has an unexpected type. +func Root(pass *analysis.Pass) (inspector.Cursor, error) { + insp, err := Inspector(pass) + if err != nil { + return inspector.Cursor{}, err + } + return insp.Root(), nil +} + // NodeText formats node as Go source text using go/printer. func NodeText(fset *token.FileSet, node ast.Node) string { var buf bytes.Buffer diff --git a/pkg/linters/sortslice/sortslice.go b/pkg/linters/sortslice/sortslice.go index 0ca6b90eef5..70c60e48ecf 100644 --- a/pkg/linters/sortslice/sortslice.go +++ b/pkg/linters/sortslice/sortslice.go @@ -25,47 +25,45 @@ var Analyzer = &analysis.Analyzer{ } func run(pass *analysis.Pass) (any, error) { - insp, err := astutil.Inspector(pass) + root, err := astutil.Root(pass) if err != nil { return nil, err } noLintLinesByFile := nolint.BuildLineIndex(pass, "sortslice") - nodeFilter := []ast.Node{(*ast.CallExpr)(nil)} - - insp.Preorder(nodeFilter, func(n ast.Node) { - call, ok := n.(*ast.CallExpr) + for cur := range root.Preorder((*ast.CallExpr)(nil)) { + call, ok := cur.Node().(*ast.CallExpr) if !ok { - return + continue } pos := pass.Fset.PositionFor(call.Pos(), false) if filecheck.IsTestFile(pos.Filename) { - return + continue } if nolint.HasDirective(pos, noLintLinesByFile) { - return + continue } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { - return + continue } pkgIdent, ok := sel.X.(*ast.Ident) if !ok { - return + continue } if pass.TypesInfo == nil { - return + continue } obj := pass.TypesInfo.ObjectOf(pkgIdent) // ObjectOf can be nil when type information is incomplete. if obj == nil { - return + continue } pkgName, ok := obj.(*types.PkgName) if !ok || pkgName.Imported().Path() != "sort" { - return + continue } switch sel.Sel.Name { @@ -75,7 +73,7 @@ func run(pass *analysis.Pass) (any, error) { case "SliceStable": pass.ReportRangef(call, "sort.SliceStable is not type-safe; use slices.SortStableFunc instead") } - }) + } return nil, nil }