lostcancel: do not analyze cancel variable which defined outside current function scope

See golang/go#31856.

Change-Id: I229a7f4a48e7806df62941f801302b6da8a0c12b
GitHub-Last-Rev: 33f85236bb79e325112866ee555950a4479ad7a7
GitHub-Pull-Request: golang/tools#95
Reviewed-on: https://go-review.googlesource.com/c/tools/+/175617
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Shengyu Zhang 2019-05-08 02:06:31 +00:00 committed by Alan Donovan
parent 2d16b83fe9
commit 9529901698
2 changed files with 35 additions and 1 deletions

View File

@ -45,6 +45,8 @@ var contextPackage = "context"
// control-flow path from the call to a return statement and that path
// does not "use" the cancel function. Any reference to the variable
// counts as a use, even within a nested function literal.
// If the variable's scope is larger than the function
// containing the assignment, we assume that other uses exist.
//
// checkLostCancel analyzes a single named or literal function.
func run(pass *analysis.Pass) (interface{}, error) {
@ -66,6 +68,15 @@ func run(pass *analysis.Pass) (interface{}, error) {
}
func runFunc(pass *analysis.Pass, node ast.Node) {
// Find scope of function node
var funcScope *types.Scope
switch v := node.(type) {
case *ast.FuncLit:
funcScope = pass.TypesInfo.Scopes[v.Type]
case *ast.FuncDecl:
funcScope = pass.TypesInfo.Scopes[v.Type]
}
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
cancelvars := make(map[*types.Var]ast.Node)
@ -114,7 +125,11 @@ func runFunc(pass *analysis.Pass, node ast.Node) {
"the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
n.(*ast.SelectorExpr).Sel.Name)
} else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok {
cancelvars[v] = stmt
// If the cancel variable is defined outside function scope,
// do not analyze it.
if funcScope.Contains(v.Pos()) {
cancelvars[v] = stmt
}
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
cancelvars[v] = stmt
}

View File

@ -171,3 +171,22 @@ var _ = func() (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel(bg)
return
}
// Test for Go issue 31856.
func _() {
var cancel func()
func() {
_, cancel = context.WithCancel(bg)
}()
cancel()
}
var cancel1 func()
// Same as above, but for package-level cancel variable.
func _() {
// We assume that other uses of cancel1 exist.
_, cancel1 = context.WithCancel(bg)
}