Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions pkg/linters/contextcancelnotdeferred/contextcancelnotdeferred.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/github/gh-aw/pkg/linters/internal/astutil"
"github.com/github/gh-aw/pkg/linters/internal/filecheck"
"github.com/github/gh-aw/pkg/linters/internal/nolint"
)

// Analyzer is the context-cancel-not-deferred analysis pass.
Expand All @@ -28,19 +29,20 @@ func run(pass *analysis.Pass) (any, error) {
if err != nil {
return nil, err
}
noLintLinesByFile := nolint.BuildLineIndex(pass, "contextcancelnotdeferred")

nodeFilter := []ast.Node{
(*ast.FuncDecl)(nil),
}

insp.Preorder(nodeFilter, func(n ast.Node) {
inspectCancelFuncDecl(pass, n)
inspectCancelFuncDecl(pass, n, noLintLinesByFile)
})

return nil, nil
}

func inspectCancelFuncDecl(pass *analysis.Pass, n ast.Node) {
func inspectCancelFuncDecl(pass *analysis.Pass, n ast.Node, noLintLinesByFile map[string]map[int]struct{}) {
fn, ok := n.(*ast.FuncDecl)
if !ok || fn.Body == nil {
return
Expand All @@ -54,11 +56,11 @@ func inspectCancelFuncDecl(pass *analysis.Pass, n ast.Node) {
cancelVars := make(map[types.Object]*cancelVarState)

ast.Inspect(fn.Body, func(node ast.Node) bool {
return inspectCancelNode(pass, cancelVars, node)
return inspectCancelNode(pass, cancelVars, node, noLintLinesByFile)
})

for _, state := range cancelVars {
if state.hasDirectCancel && !state.hasDeferCancel {
if state.hasDirectCancel && !state.hasDeferCancel && !nolint.HasDirective(pass.Fset.PositionFor(state.createPos, false), noLintLinesByFile) {
pass.Report(analysis.Diagnostic{
Pos: state.createPos,
Message: "context cancel function should be deferred immediately after context.WithCancel/WithTimeout/WithDeadline",
Expand All @@ -67,7 +69,7 @@ func inspectCancelFuncDecl(pass *analysis.Pass, n ast.Node) {
}
}

func inspectCancelNode(pass *analysis.Pass, cancelVars map[types.Object]*cancelVarState, node ast.Node) bool {
func inspectCancelNode(pass *analysis.Pass, cancelVars map[types.Object]*cancelVarState, node ast.Node, noLintLinesByFile map[string]map[int]struct{}) bool {
if node == nil {
return false
}
Expand All @@ -91,7 +93,7 @@ func inspectCancelNode(pass *analysis.Pass, cancelVars map[types.Object]*cancelV
if obj == nil {
continue
}
if prev, exists := cancelVars[obj]; exists && prev.hasDirectCancel && !prev.hasDeferCancel {
if prev, exists := cancelVars[obj]; exists && prev.hasDirectCancel && !prev.hasDeferCancel && !nolint.HasDirective(pass.Fset.PositionFor(prev.createPos, false), noLintLinesByFile) {
pass.Report(analysis.Diagnostic{
Pos: prev.createPos,
Message: "context cancel function should be deferred immediately after context.WithCancel/WithTimeout/WithDeadline",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,10 @@ func BadReassignThenGood(parent context.Context) error {
func BlankIdentifier(parent context.Context) {
_, _ = context.WithTimeout(parent, time.Second)
}

func GoodNoLint(parent context.Context) error {
ctx, cancel := context.WithTimeout(parent, time.Second) //nolint:contextcancelnotdeferred
_ = ctx
cancel()
return nil
}
5 changes: 5 additions & 0 deletions pkg/linters/ctxbackground/ctxbackground.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/github/gh-aw/pkg/linters/internal/astutil"
"github.com/github/gh-aw/pkg/linters/internal/filecheck"
"github.com/github/gh-aw/pkg/linters/internal/nolint"
)

// Analyzer is the ctx-background analysis pass.
Expand All @@ -28,6 +29,7 @@ func run(pass *analysis.Pass) (any, error) {
if err != nil {
return nil, err
}
noLintLinesByFile := nolint.BuildLineIndex(pass, "ctxbackground")

for cur := range insp.Root().Preorder((*ast.CallExpr)(nil)) {
call, ok := cur.Node().(*ast.CallExpr)
Expand All @@ -39,6 +41,9 @@ func run(pass *analysis.Pass) (any, error) {
if filecheck.IsTestFile(pos.Filename) {
continue
}
if nolint.HasDirective(pos, noLintLinesByFile) {
continue
}

for encl := range cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) {
var ftype *ast.FuncType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,10 @@ func DoWorkWithDetachedClosure(ctx context.Context) {
_ = ctx
}

// not flagged: inline nolint suppresses intentional detachment.
func DoWorkNoLint(ctx context.Context) {
_ = context.Background() //nolint:ctxbackground
_ = ctx
}

func handle(f func(context.Context)) { f(nil) }
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,10 @@ func DoWorkWithDetachedClosure(ctx context.Context) {
_ = ctx
}

// not flagged: inline nolint suppresses intentional detachment.
func DoWorkNoLint(ctx context.Context) {
_ = context.Background() //nolint:ctxbackground
_ = ctx
}

func handle(f func(context.Context)) { f(nil) }
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/github/gh-aw/pkg/linters/internal/astutil"
"github.com/github/gh-aw/pkg/linters/internal/filecheck"
"github.com/github/gh-aw/pkg/linters/internal/nolint"
)

// Analyzer is the exec-command-without-context analysis pass.
Expand All @@ -30,6 +31,7 @@ func run(pass *analysis.Pass) (any, error) {
if err != nil {
return nil, err
}
noLintLinesByFile := nolint.BuildLineIndex(pass, "execcommandwithoutcontext")

for cur := range insp.Root().Preorder((*ast.CallExpr)(nil)) {
call, ok := cur.Node().(*ast.CallExpr)
Expand All @@ -45,6 +47,9 @@ func run(pass *analysis.Pass) (any, error) {
if filecheck.IsTestFile(pos.Filename) {
continue
}
if nolint.HasDirective(pos, noLintLinesByFile) {
continue
}

for encl := range cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) {
funcType := enclosingFuncType(encl.Node())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ func OuterCtxInnerClosure(ctx context.Context) {
_ = exec.Command("ls") // want `use exec\.CommandContext\(ctx, \.\.\.\) instead of exec\.Command to propagate context cancellation`
}()
}

// Good: inline nolint suppresses an intentional detached command.
func GoodNoLint(ctx context.Context, name string) {
_ = ctx
_ = exec.Command(name) //nolint:execcommandwithoutcontext
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ func OuterCtxInnerClosure(ctx context.Context) {
_ = exec.CommandContext(ctx, "ls") // want `use exec\.CommandContext\(ctx, \.\.\.\) instead of exec\.Command to propagate context cancellation`
}()
}

// Good: inline nolint suppresses an intentional detached command.
func GoodNoLint(ctx context.Context, name string) {
_ = ctx
_ = exec.Command(name) //nolint:execcommandwithoutcontext
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ func GoodFuncLitWithOwnCtx() {
func doWork(fn func(context.Context, time.Duration)) {
fn(context.Background(), time.Second)
}

// Good: inline nolint suppresses intentional sleep.
func GoodNoLint(ctx context.Context, d time.Duration) {
_ = ctx
time.Sleep(d) //nolint:timesleepnocontext
}
5 changes: 5 additions & 0 deletions pkg/linters/timesleepnocontext/timesleepnocontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/github/gh-aw/pkg/linters/internal/astutil"
"github.com/github/gh-aw/pkg/linters/internal/filecheck"
"github.com/github/gh-aw/pkg/linters/internal/nolint"
)

// Analyzer is the time-sleep-no-context analysis pass.
Expand All @@ -30,6 +31,7 @@ func run(pass *analysis.Pass) (any, error) {
if err != nil {
return nil, err
}
noLintLinesByFile := nolint.BuildLineIndex(pass, "timesleepnocontext")

for cur := range insp.Root().Preorder((*ast.CallExpr)(nil)) {
call, ok := cur.Node().(*ast.CallExpr)
Expand All @@ -44,6 +46,9 @@ func run(pass *analysis.Pass) (any, error) {
if filecheck.IsTestFile(pos.Filename) {
continue
}
if nolint.HasDirective(pos, noLintLinesByFile) {
continue
}

for encl := range cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) {
funcType := enclosingFuncType(encl.Node())
Expand Down