forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds a new pass which checks for `...Unlock()` expressions without `defer` which could result in a lock being held indefinitely. Resolves cockroachdb#105366 Release note: None
- Loading branch information
1 parent
3b2c035
commit fe62ff0
Showing
9 changed files
with
209 additions
and
0 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
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
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,30 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") | ||
|
||
go_library( | ||
name = "deferunlockcheck", | ||
srcs = ["deferunlockcheck.go"], | ||
importpath = "github.com/cockroachdb/cockroach/pkg/testutils/lint/passes/deferunlockcheck", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//pkg/testutils/lint/passes/passesutil", | ||
"@org_golang_x_tools//go/analysis", | ||
"@org_golang_x_tools//go/analysis/passes/inspect", | ||
"@org_golang_x_tools//go/ast/inspector", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "deferunlockcheck_test", | ||
srcs = ["deferunlockcheck_test.go"], | ||
args = ["-test.timeout=295s"], | ||
data = glob(["testdata/**"]) + [ | ||
"@go_sdk//:files", | ||
], | ||
deps = [ | ||
":deferunlockcheck", | ||
"//pkg/build/bazel", | ||
"//pkg/testutils/datapathutils", | ||
"//pkg/testutils/skip", | ||
"@org_golang_x_tools//go/analysis/analysistest", | ||
], | ||
) |
59 changes: 59 additions & 0 deletions
59
pkg/testutils/lint/passes/deferunlockcheck/deferunlockcheck.go
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,59 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package deferunlockcheck | ||
|
||
import ( | ||
"go/ast" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/testutils/lint/passes/passesutil" | ||
"golang.org/x/tools/go/analysis" | ||
"golang.org/x/tools/go/analysis/passes/inspect" | ||
"golang.org/x/tools/go/ast/inspector" | ||
) | ||
|
||
// Doc documents this pass. | ||
const Doc = "checks that usages of mutex Unlock() are deferred." | ||
|
||
const noLintName = "deferunlock" | ||
|
||
// Analyzer is an analysis pass that checks for mutex unlocks which | ||
// aren't deferred. | ||
var Analyzer = &analysis.Analyzer{ | ||
Name: "deferunlockcall", | ||
Doc: Doc, | ||
Requires: []*analysis.Analyzer{inspect.Analyzer}, | ||
Run: run, | ||
} | ||
|
||
func run(pass *analysis.Pass) (interface{}, error) { | ||
astInspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) | ||
// Note: defer ...Unlock() expressions are captured as ast.DeferStmt nodes so we don't have to worry about those results returning | ||
// for ast.ExprStmt nodes. | ||
filter := []ast.Node{ | ||
(*ast.ExprStmt)(nil), | ||
} | ||
astInspector.Preorder(filter, func(n ast.Node) { | ||
stmt := n.(*ast.ExprStmt) | ||
expr, ok := stmt.X.(*ast.CallExpr) | ||
if !ok { | ||
return | ||
} | ||
sel, ok := expr.Fun.(*ast.SelectorExpr) | ||
if !ok { | ||
return | ||
} | ||
if sel.Sel != nil && (sel.Sel.Name == "Unlock" || sel.Sel.Name == "RUnlock") && !passesutil.HasNolintComment(pass, n, noLintName) { | ||
pass.Reportf(sel.Pos(), "Mutex %s not deferred", sel.Sel.Name) | ||
} | ||
}) | ||
|
||
return nil, nil | ||
} |
34 changes: 34 additions & 0 deletions
34
pkg/testutils/lint/passes/deferunlockcheck/deferunlockcheck_test.go
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,34 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package deferunlockcheck_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/build/bazel" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/datapathutils" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/lint/passes/deferunlockcheck" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/skip" | ||
"golang.org/x/tools/go/analysis/analysistest" | ||
) | ||
|
||
func init() { | ||
if bazel.BuiltWithBazel() { | ||
bazel.SetGoEnv() | ||
} | ||
} | ||
|
||
func Test(t *testing.T) { | ||
skip.UnderStress(t) | ||
testdata := datapathutils.TestDataPath(t) | ||
analysistest.TestData = func() string { return testdata } | ||
analysistest.Run(t, testdata, deferunlockcheck.Analyzer, "a") | ||
} |
58 changes: 58 additions & 0 deletions
58
pkg/testutils/lint/passes/deferunlockcheck/testdata/src/a/a.go
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,58 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package a | ||
|
||
import ( | ||
"github.com/cockroachdb/cockroach/pkg/util/syncutil" | ||
) | ||
|
||
type TestUnlockLint struct { | ||
mu struct { | ||
syncutil.Mutex | ||
} | ||
} | ||
|
||
func init() { | ||
var mut syncutil.Mutex | ||
var rwmut syncutil.RWMutex | ||
testUnlock := &TestUnlockLint{} | ||
|
||
// Test the main use case. | ||
// Should only capture Unlock() | ||
testUnlock.mu.Lock() | ||
testUnlock.mu.Unlock() // want `Mutex Unlock not deferred` | ||
// This should pass. | ||
defer testUnlock.mu.Unlock() | ||
|
||
// Test within a function. | ||
okFn := func() { | ||
testUnlock.mu.Lock() | ||
defer testUnlock.mu.Unlock() | ||
} | ||
failFn := func() { | ||
testUnlock.mu.Lock() | ||
testUnlock.mu.Unlock() // want `Mutex Unlock not deferred` | ||
} | ||
okFn() | ||
failFn() | ||
|
||
// Test mut variation. | ||
defer mut.Unlock() | ||
mut.Unlock() // want `Mutex Unlock not deferred` | ||
|
||
// Test RUnlock | ||
defer rwmut.RUnlock() | ||
rwmut.RUnlock() // want `Mutex RUnlock not deferred` | ||
|
||
// Test the no lint rule. | ||
// nolint:deferunlock | ||
testUnlock.mu.Unlock() | ||
} |
21 changes: 21 additions & 0 deletions
21
...unlockcheck/testdata/src/github.com/cockroachdb/cockroach/pkg/util/syncutil/mutex_sync.go
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,21 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package syncutil | ||
|
||
import "sync" | ||
|
||
type Mutex struct { | ||
sync.Mutex | ||
} | ||
|
||
type RWMutex struct { | ||
sync.RWMutex | ||
} |