Skip to content

Commit

Permalink
feat: suggested fix for 'clear'
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Dec 27, 2024
1 parent 3a7b590 commit e8bd05a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
57 changes: 49 additions & 8 deletions exptostd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
package exptostd

import (
"bytes"
"fmt"
"go/ast"
"go/build"
"go/printer"
"go/token"
"go/types"
"os"
"slices"
Expand All @@ -23,9 +26,9 @@ const (
)

type stdReplacement struct {
MinGo int
Text string
KeepImport bool
MinGo int
Text string
Suggested func(callExpr *ast.CallExpr) (analysis.SuggestedFix, error)
}

type analyzer struct {
Expand All @@ -43,7 +46,7 @@ func NewAnalyzer() *analysis.Analyzer {
_, skip := os.LookupEnv("EXPTOSTD_SKIP_GO_VERSION_CHECK")

l := &analyzer{
importsCleaner: strings.NewReplacer("\"", "", "`", ""),
importsCleaner: strings.NewReplacer(`"`, "", "`", ""),
skipGoVersionDetection: skip,
mapsPkgReplacements: map[string]stdReplacement{
"Keys": {MinGo: go123, Text: "slices.Collect(maps.Keys())"},
Expand All @@ -53,7 +56,7 @@ func NewAnalyzer() *analysis.Analyzer {
"Clone": {MinGo: go121, Text: "maps.Clone()"},
"Copy": {MinGo: go121, Text: "maps.Copy()"},
"DeleteFunc": {MinGo: go121, Text: "maps.DeleteFunc()"},
"Clear": {MinGo: go121, Text: "clear()", KeepImport: true},
"Clear": {MinGo: go121, Text: "clear()", Suggested: suggestedFixForClear},
},
slicesPkgReplacements: map[string]stdReplacement{
"Equal": {MinGo: go121, Text: "slices.Equal()"},
Expand Down Expand Up @@ -97,7 +100,7 @@ func NewAnalyzer() *analysis.Analyzer {
}
}

func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) {
func (a *analyzer) run(pass *analysis.Pass) (any, error) {
insp, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
if !ok {
return nil, nil
Expand Down Expand Up @@ -180,8 +183,23 @@ func (a *analyzer) detectPackageUsage(pass *analysis.Pass,
}

if pkg.Imported().Path() == importPath {
pass.Reportf(callExpr.Pos(), "%s.%s() can be replaced by %s", importPath, selExpr.Sel.Name, rp.Text)
return !rp.KeepImport
diagnostic := analysis.Diagnostic{
Pos: callExpr.Pos(),
Message: fmt.Sprintf("%s.%s() can be replaced by %s", importPath, selExpr.Sel.Name, rp.Text),
}

if rp.Suggested != nil {
fix, err := rp.Suggested(callExpr)
if err != nil {
diagnostic.Message = fmt.Sprintf("Suggested fix error: %v", err)
} else {
diagnostic.SuggestedFixes = append(diagnostic.SuggestedFixes, fix)
}
}

pass.Report(diagnostic)

return true
}

return false
Expand Down Expand Up @@ -222,6 +240,29 @@ func (a *analyzer) suggestRemoveImport(pass *analysis.Pass, imports map[string]*
})
}

func suggestedFixForClear(callExpr *ast.CallExpr) (analysis.SuggestedFix, error) {
s := &ast.CallExpr{
Fun: ast.NewIdent("clear"),
Args: callExpr.Args,
Ellipsis: callExpr.Ellipsis,
}

buf := bytes.NewBuffer(nil)

err := printer.Fprint(buf, token.NewFileSet(), s)
if err != nil {
return analysis.SuggestedFix{}, fmt.Errorf("print suggested fix: %w", err)
}

return analysis.SuggestedFix{
TextEdits: []analysis.TextEdit{{
Pos: callExpr.Pos(),
End: callExpr.End(),
NewText: buf.Bytes(),
}},
}, nil
}

func getGoVersion(pass *analysis.Pass) int {
// Prior to go1.22, versions.FileVersion returns only the toolchain version,
// which is of no use to us,
Expand Down
2 changes: 1 addition & 1 deletion testdata/src/expmaps/maps.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package expmaps

import (
`golang.org/x/exp/maps`
`golang.org/x/exp/maps` // want "Import statement 'golang.org/x/exp/maps' can be replaced by 'maps'"
)

func _(m, a map[string]string) {
Expand Down
4 changes: 2 additions & 2 deletions testdata/src/expmaps/maps.go.golden
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package expmaps

import (
`maps`
`maps` // want "Import statement 'golang.org/x/exp/maps' can be replaced by 'maps'"
)

func _(m, a map[string]string) {
Expand All @@ -23,6 +23,6 @@ func _(m, a map[string]string) {
return true
})

maps.Clear(m) // want `golang.org/x/exp/maps.Clear\(\) can be replaced by clear\(\)`
clear(m) // want `golang.org/x/exp/maps.Clear\(\) can be replaced by clear\(\)`
}

0 comments on commit e8bd05a

Please sign in to comment.