-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Deprecated Function Checker (#85)
# Description Add a deprecated functon checker that identifies the usage and suggests a alternative functions if possible. ## Implementation Details The core of this implementation is the `DeprecatedFuncChecker` struct type, which maintains a map of deprecated functions along with their alternative functions. ### How Deprecated Functions are Recognized 1. **Registration**: Deprecated functions are registered using thr `Register` method, which takes three parameters - Package name - Function name - Alternative function (`packageName.funcName` format) 2. **Function Call Identification**: For each AST node, the checker looks for `*ast.CallExpr` nodes, which represent function calls. 3. **Selector Expression Analysis**: It then checks if the function call is a selector expression (`*ast.SelectorExpr`), which is typical for `package.Function()` calls. 4. **Package and Function Matching**: The checker extracts the package name and function name from the selector expression and compares them against the registered deprecated functions. ## Usage Example ```go checker := NewDeprecatedFuncChecker() checker.Register("fmt", "Println", "fmt.Print") checker.Register("os", "Remove", "os.RemoveAll") deprecated, err := checker.Check(filename, astNode, fileSet) if err != nil { // Handle error } for _, dep := range deprecated { fmt.Printf("Deprecated function %s.%s used. Consider using %s instead.\n", dep.Package, dep.Function, dep.Alternative) } ```
- Loading branch information
Showing
9 changed files
with
517 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package formatter | ||
|
||
import ( | ||
"github.com/gnolang/tlin/internal" | ||
tt "github.com/gnolang/tlin/internal/types" | ||
) | ||
|
||
type DeprecatedFuncFormatter struct{} | ||
|
||
func (f *DeprecatedFuncFormatter) Format(issue tt.Issue, snippet *internal.SourceCode) string { | ||
builder := NewIssueFormatterBuilder(issue, snippet) | ||
return builder. | ||
AddHeader(errorHeader). | ||
AddCodeSnippet(). | ||
AddUnderlineAndMessage(). | ||
AddNote(). | ||
Build() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package checker | ||
|
||
import ( | ||
"go/ast" | ||
"go/token" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// pkgPath -> funcName -> alternative | ||
type deprecatedFuncMap map[string]map[string]string | ||
|
||
// DeprecatedFunc represents a deprecated function. | ||
type DeprecatedFunc struct { | ||
Package string | ||
Function string | ||
Alternative string | ||
Position token.Position | ||
} | ||
|
||
// DeprecatedFuncChecker checks for deprecated functions. | ||
type DeprecatedFuncChecker struct { | ||
deprecatedFuncs deprecatedFuncMap | ||
} | ||
|
||
func NewDeprecatedFuncChecker() *DeprecatedFuncChecker { | ||
return &DeprecatedFuncChecker{ | ||
deprecatedFuncs: make(deprecatedFuncMap), | ||
} | ||
} | ||
|
||
func (d *DeprecatedFuncChecker) Register(pkgName, funcName, alternative string) { | ||
if _, ok := d.deprecatedFuncs[pkgName]; !ok { | ||
d.deprecatedFuncs[pkgName] = make(map[string]string) | ||
} | ||
d.deprecatedFuncs[pkgName][funcName] = alternative | ||
} | ||
|
||
// Check checks a AST node for deprecated functions. | ||
// | ||
// TODO: use this in the linter rule implementation | ||
func (d *DeprecatedFuncChecker) Check( | ||
filename string, | ||
node *ast.File, | ||
fset *token.FileSet, | ||
) ([]DeprecatedFunc, error) { | ||
var found []DeprecatedFunc | ||
|
||
packageAliases := make(map[string]string) | ||
for _, imp := range node.Imports { | ||
path, err := strconv.Unquote(imp.Path.Value) | ||
if err != nil { | ||
continue | ||
} | ||
name := "" | ||
if imp.Name != nil { | ||
name = imp.Name.Name | ||
} else { | ||
parts := strings.Split(path, "/") | ||
name = parts[len(parts)-1] | ||
} | ||
packageAliases[name] = path | ||
} | ||
|
||
ast.Inspect(node, func(n ast.Node) bool { | ||
call, ok := n.(*ast.CallExpr) | ||
if !ok { | ||
return true | ||
} | ||
|
||
switch fun := call.Fun.(type) { | ||
case *ast.SelectorExpr: | ||
ident, ok := fun.X.(*ast.Ident) | ||
if !ok { | ||
return true | ||
} | ||
pkgAlias := ident.Name | ||
funcName := fun.Sel.Name | ||
|
||
pkgPath, ok := packageAliases[pkgAlias] | ||
if !ok { | ||
// Not a package alias, possibly a method call | ||
return true | ||
} | ||
|
||
if funcs, ok := d.deprecatedFuncs[pkgPath]; ok { | ||
if alt, ok := funcs[funcName]; ok { | ||
found = append(found, DeprecatedFunc{ | ||
Package: pkgPath, | ||
Function: funcName, | ||
Alternative: alt, | ||
Position: fset.Position(call.Pos()), | ||
}) | ||
} | ||
} | ||
case *ast.Ident: | ||
// Handle functions imported via dot imports | ||
funcName := fun.Name | ||
// Check dot-imported packages | ||
for alias, pkgPath := range packageAliases { | ||
if alias != "." { | ||
continue | ||
} | ||
if funcs, ok := d.deprecatedFuncs[pkgPath]; ok { | ||
if alt, ok := funcs[funcName]; ok { | ||
found = append(found, DeprecatedFunc{ | ||
Package: pkgPath, | ||
Function: funcName, | ||
Alternative: alt, | ||
Position: fset.Position(call.Pos()), | ||
}) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
return true | ||
}) | ||
|
||
return found, nil | ||
} |
Oops, something went wrong.