Skip to content

Commit

Permalink
refactor(rule/context-as-argument): replace AST walker by iteration o…
Browse files Browse the repository at this point in the history
…ver declarations (#1160)
  • Loading branch information
chavacava authored Dec 4, 2024
1 parent c3b541f commit 9a5c95f
Showing 1 changed file with 32 additions and 47 deletions.
79 changes: 32 additions & 47 deletions rule/context_as_argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

// ContextAsArgumentRule suggests that `context.Context` should be the first argument of a function.
type ContextAsArgumentRule struct {
allowTypesLUT map[string]struct{}
allowTypes map[string]struct{}

configureOnce sync.Once
}
Expand All @@ -21,64 +21,49 @@ func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []li
r.configureOnce.Do(func() { r.configure(args) })

var failures []lint.Failure
walker := lintContextArguments{
allowTypesLUT: r.allowTypesLUT,
onFailure: func(failure lint.Failure) {
failures = append(failures, failure)
},
}
for _, decl := range file.AST.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok || len(fn.Type.Params.List) <= 1 {
continue // not a function or a function with less than 2 parameters
}

ast.Walk(walker, file.AST)
fnArgs := fn.Type.Params.List

// A context.Context should be the first parameter of a function.
// Flag any that show up after the first.
isCtxStillAllowed := true
for _, arg := range fnArgs {
argIsCtx := isPkgDot(arg.Type, "context", "Context")
if argIsCtx && !isCtxStillAllowed {
failures = append(failures, lint.Failure{
Node: arg,
Category: "arg-order",
Failure: "context.Context should be the first parameter of a function",
Confidence: 0.9,
})

break // only flag one
}

return failures
}
typeName := gofmt(arg.Type)
// a parameter of type context.Context is still allowed if the current arg type is in the allow types LookUpTable
_, isCtxStillAllowed = r.allowTypes[typeName]
}
}

func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) {
r.allowTypesLUT = getAllowTypesFromArguments(arguments)
return failures
}

// Name returns the rule name.
func (*ContextAsArgumentRule) Name() string {
return "context-as-argument"
}

type lintContextArguments struct {
allowTypesLUT map[string]struct{}
onFailure func(lint.Failure)
}

func (w lintContextArguments) Visit(n ast.Node) ast.Visitor {
fn, ok := n.(*ast.FuncDecl)
if !ok || len(fn.Type.Params.List) <= 1 {
return w
}

fnArgs := fn.Type.Params.List

// A context.Context should be the first parameter of a function.
// Flag any that show up after the first.
isCtxStillAllowed := true
for _, arg := range fnArgs {
argIsCtx := isPkgDot(arg.Type, "context", "Context")
if argIsCtx && !isCtxStillAllowed {
w.onFailure(lint.Failure{
Node: arg,
Category: "arg-order",
Failure: "context.Context should be the first parameter of a function",
Confidence: 0.9,
})
break // only flag one
}

typeName := gofmt(arg.Type)
// a parameter of type context.Context is still allowed if the current arg type is in the LUT
_, isCtxStillAllowed = w.allowTypesLUT[typeName]
}

return nil // avoid visiting the function body
func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) {
r.allowTypes = r.getAllowTypesFromArguments(arguments)
}

func getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} {
func (r *ContextAsArgumentRule) getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} {
allowTypesBefore := []string{}
if len(args) >= 1 {
argKV, ok := args[0].(map[string]any)
Expand Down

0 comments on commit 9a5c95f

Please # to comment.