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:
parent
2d16b83fe9
commit
9529901698
|
@ -45,6 +45,8 @@ var contextPackage = "context"
|
||||||
// control-flow path from the call to a return statement and that path
|
// control-flow path from the call to a return statement and that path
|
||||||
// does not "use" the cancel function. Any reference to the variable
|
// does not "use" the cancel function. Any reference to the variable
|
||||||
// counts as a use, even within a nested function literal.
|
// 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.
|
// checkLostCancel analyzes a single named or literal function.
|
||||||
func run(pass *analysis.Pass) (interface{}, error) {
|
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) {
|
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.
|
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
|
||||||
cancelvars := make(map[*types.Var]ast.Node)
|
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",
|
"the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
|
||||||
n.(*ast.SelectorExpr).Sel.Name)
|
n.(*ast.SelectorExpr).Sel.Name)
|
||||||
} else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok {
|
} else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok {
|
||||||
|
// If the cancel variable is defined outside function scope,
|
||||||
|
// do not analyze it.
|
||||||
|
if funcScope.Contains(v.Pos()) {
|
||||||
cancelvars[v] = stmt
|
cancelvars[v] = stmt
|
||||||
|
}
|
||||||
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
|
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
|
||||||
cancelvars[v] = stmt
|
cancelvars[v] = stmt
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,3 +171,22 @@ var _ = func() (ctx context.Context, cancel func()) {
|
||||||
ctx, cancel = context.WithCancel(bg)
|
ctx, cancel = context.WithCancel(bg)
|
||||||
return
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue