internal/memoize: delete Bind(cleanup) hook

Now that the workspace directory uses Snapshot.Destroy
to clean up (see https://go-review.googlesource.com/c/tools/+/414499)
there is no need for this feature.

Change-Id: Id5782273ce5030b4fb8f3b66a8d16a45a831ed91
Reviewed-on: https://go-review.googlesource.com/c/tools/+/414500
Reviewed-by: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
diff --git a/internal/lsp/cache/analysis.go b/internal/lsp/cache/analysis.go
index 9f7a19c..847ac2d 100644
--- a/internal/lsp/cache/analysis.go
+++ b/internal/lsp/cache/analysis.go
@@ -147,7 +147,7 @@
 			}
 		}
 		return runAnalysis(ctx, snapshot, a, pkg, results)
-	}, nil)
+	})
 	act.handle = h
 
 	act = s.addActionHandle(act)
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index c076f42..843919d 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -88,7 +88,7 @@
 			},
 			err: parseErr,
 		}
-	}, nil)
+	})
 
 	pmh := &parseModHandle{handle: h}
 	s.mu.Lock()
@@ -162,7 +162,7 @@
 			},
 			err: parseErr,
 		}
-	}, nil)
+	})
 
 	pwh := &parseWorkHandle{handle: h}
 	s.mu.Lock()
@@ -288,7 +288,7 @@
 			why[req.Mod.Path] = whyList[i]
 		}
 		return &modWhyData{why: why}
-	}, nil)
+	})
 
 	mwh := &modWhyHandle{handle: h}
 	s.mu.Lock()
diff --git a/internal/lsp/cache/mod_tidy.go b/internal/lsp/cache/mod_tidy.go
index bd2ff0c..0f36249 100644
--- a/internal/lsp/cache/mod_tidy.go
+++ b/internal/lsp/cache/mod_tidy.go
@@ -138,7 +138,7 @@
 				TidiedContent: tempContents,
 			},
 		}
-	}, nil)
+	})
 
 	mth := &modTidyHandle{handle: h}
 	s.mu.Lock()
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index f7b4f9c..c3eae2f 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -130,7 +130,7 @@
 	}
 	astHandle := s.generation.Bind(astCacheKey{pkgHandle.key, pgf.URI}, func(ctx context.Context, arg memoize.Arg) interface{} {
 		return buildASTCache(pgf)
-	}, nil)
+	})
 
 	d, err := astHandle.Get(ctx, s.generation, s)
 	if err != nil {
diff --git a/internal/lsp/cache/symbols.go b/internal/lsp/cache/symbols.go
index d56a036..f27178e 100644
--- a/internal/lsp/cache/symbols.go
+++ b/internal/lsp/cache/symbols.go
@@ -41,7 +41,7 @@
 		snapshot := arg.(*snapshot)
 		symbols, err := symbolize(snapshot, fh)
 		return &symbolData{symbols, err}
-	}, nil)
+	})
 
 	sh := &symbolHandle{
 		handle: handle,
diff --git a/internal/memoize/memoize.go b/internal/memoize/memoize.go
index 7fa5340..4b84410 100644
--- a/internal/memoize/memoize.go
+++ b/internal/memoize/memoize.go
@@ -34,6 +34,7 @@
 	handlesMu sync.Mutex // lock ordering: Store.handlesMu before Handle.mu
 	handles   map[interface{}]*Handle
 	// handles which are bound to generations for GC purposes.
+	// (It is the subset of values of 'handles' with trackGenerations enabled.)
 	boundHandles map[*Handle]struct{}
 }
 
@@ -78,7 +79,11 @@
 		if _, ok := h.generations[g]; ok {
 			delete(h.generations, g) // delete even if it's dead, in case of dangling references to the entry.
 			if len(h.generations) == 0 {
-				h.destroy(g.store)
+				h.state = stateDestroyed
+				delete(g.store.handles, h.key)
+				if h.trackGenerations {
+					delete(g.store.boundHandles, h)
+				}
 			}
 		}
 		h.mu.Unlock()
@@ -155,14 +160,6 @@
 	function Function
 	// value is set in completed state.
 	value interface{}
-	// cleanup, if non-nil, is used to perform any necessary clean-up on values
-	// produced by function.
-	//
-	// cleanup is never set for reference counted handles.
-	//
-	// TODO(rfindley): remove this field once workspace folders no longer need to
-	// be tracked.
-	cleanup func(interface{})
 
 	// If trackGenerations is set, this handle tracks generations in which it
 	// is valid, via the generations field. Otherwise, it is explicitly reference
@@ -171,7 +168,7 @@
 	refCounter       int32
 }
 
-// Bind returns a handle for the given key and function.
+// Bind returns a "generational" handle for the given key and function.
 //
 // Each call to bind will return the same handle if it is already bound. Bind
 // will always return a valid handle, creating one if needed. Each key can
@@ -179,22 +176,18 @@
 // until the associated generation is destroyed. Bind does not cause the value
 // to be generated.
 //
-// If cleanup is non-nil, it will be called on any non-nil values produced by
-// function when they are no longer referenced.
-//
 // It is responsibility of the caller to call Inherit on the handler whenever
 // it should still be accessible by a next generation.
-func (g *Generation) Bind(key interface{}, function Function, cleanup func(interface{})) *Handle {
-	return g.getHandle(key, function, cleanup, true)
+func (g *Generation) Bind(key interface{}, function Function) *Handle {
+	return g.getHandle(key, function, true)
 }
 
-// GetHandle returns a handle for the given key and function with similar
-// properties and behavior as Bind.
-//
-// As in opposite to Bind it returns a release callback which has to be called
-// once this reference to handle is not needed anymore.
+// GetHandle returns a "reference-counted" handle for the given key
+// and function with similar properties and behavior as Bind. Unlike
+// Bind, it returns a release callback which must be called once the
+// handle is no longer needed.
 func (g *Generation) GetHandle(key interface{}, function Function) (*Handle, func()) {
-	h := g.getHandle(key, function, nil, false)
+	h := g.getHandle(key, function, false)
 	store := g.store
 	release := func() {
 		// Acquire store.handlesMu before mutating refCounter
@@ -206,7 +199,7 @@
 
 		h.refCounter--
 		if h.refCounter == 0 {
-			// Don't call h.destroy: for reference counted handles we can't know when
+			// Don't mark destroyed: for reference counted handles we can't know when
 			// they are no longer reachable from runnable goroutines. For example,
 			// gopls could have a current operation that is using a packageHandle.
 			// Destroying the handle here would cause that operation to hang.
@@ -216,7 +209,7 @@
 	return h, release
 }
 
-func (g *Generation) getHandle(key interface{}, function Function, cleanup func(interface{}), trackGenerations bool) *Handle {
+func (g *Generation) getHandle(key interface{}, function Function, trackGenerations bool) *Handle {
 	// panic early if the function is nil
 	// it would panic later anyway, but in a way that was much harder to debug
 	if function == nil {
@@ -232,7 +225,6 @@
 		h = &Handle{
 			key:              key,
 			function:         function,
-			cleanup:          cleanup,
 			trackGenerations: trackGenerations,
 		}
 		if trackGenerations {
@@ -298,18 +290,6 @@
 	h.incrementRef(g)
 }
 
-// destroy marks h as destroyed. h.mu and store.handlesMu must be held.
-func (h *Handle) destroy(store *Store) {
-	h.state = stateDestroyed
-	if h.cleanup != nil && h.value != nil {
-		h.cleanup(h.value)
-	}
-	delete(store.handles, h.key)
-	if h.trackGenerations {
-		delete(store.boundHandles, h)
-	}
-}
-
 func (h *Handle) incrementRef(g *Generation) {
 	h.mu.Lock()
 	defer h.mu.Unlock()
@@ -412,11 +392,6 @@
 			}
 			v := function(childCtx, arg)
 			if childCtx.Err() != nil {
-				// It's possible that v was computed despite the context cancellation. In
-				// this case we should ensure that it is cleaned up.
-				if h.cleanup != nil && v != nil {
-					h.cleanup(v)
-				}
 				return
 			}
 
@@ -427,19 +402,9 @@
 			// checked childCtx above. Even so, that should be harmless, since each
 			// run should produce the same results.
 			if h.state != stateRunning {
-				// v will never be used, so ensure that it is cleaned up.
-				if h.cleanup != nil && v != nil {
-					h.cleanup(v)
-				}
 				return
 			}
 
-			if h.cleanup != nil && h.value != nil {
-				// Clean up before overwriting an existing value.
-				h.cleanup(h.value)
-			}
-
-			// At this point v will be cleaned up whenever h is destroyed.
 			h.value = v
 			h.function = nil
 			h.state = stateCompleted
diff --git a/internal/memoize/memoize_test.go b/internal/memoize/memoize_test.go
index ae387b8..48bb181 100644
--- a/internal/memoize/memoize_test.go
+++ b/internal/memoize/memoize_test.go
@@ -23,7 +23,7 @@
 	h := g.Bind("key", func(context.Context, memoize.Arg) interface{} {
 		evaled++
 		return "res"
-	}, nil)
+	})
 	expectGet(t, h, g, "res")
 	expectGet(t, h, g, "res")
 	if evaled != 1 {
@@ -50,7 +50,7 @@
 	s := &memoize.Store{}
 	// Evaluate key in g1.
 	g1 := s.Generation("g1")
-	h1 := g1.Bind("key", func(context.Context, memoize.Arg) interface{} { return "res" }, nil)
+	h1 := g1.Bind("key", func(context.Context, memoize.Arg) interface{} { return "res" })
 	expectGet(t, h1, g1, "res")
 
 	// Get key in g2. It should inherit the value from g1.
@@ -58,7 +58,7 @@
 	h2 := g2.Bind("key", func(context.Context, memoize.Arg) interface{} {
 		t.Fatal("h2 should not need evaluation")
 		return "error"
-	}, nil)
+	})
 	expectGet(t, h2, g2, "res")
 
 	// With g1 destroyed, g2 should still work.
@@ -68,47 +68,10 @@
 	// With all generations destroyed, key should be re-evaluated.
 	g2.Destroy("TestGenerations")
 	g3 := s.Generation("g3")
-	h3 := g3.Bind("key", func(context.Context, memoize.Arg) interface{} { return "new res" }, nil)
+	h3 := g3.Bind("key", func(context.Context, memoize.Arg) interface{} { return "new res" })
 	expectGet(t, h3, g3, "new res")
 }
 
-func TestCleanup(t *testing.T) {
-	s := &memoize.Store{}
-	g1 := s.Generation("g1")
-	v1 := false
-	v2 := false
-	cleanup := func(v interface{}) {
-		*(v.(*bool)) = true
-	}
-	h1 := g1.Bind("key1", func(context.Context, memoize.Arg) interface{} {
-		return &v1
-	}, nil)
-	h2 := g1.Bind("key2", func(context.Context, memoize.Arg) interface{} {
-		return &v2
-	}, cleanup)
-	expectGet(t, h1, g1, &v1)
-	expectGet(t, h2, g1, &v2)
-	g2 := s.Generation("g2")
-	g2.Inherit(h1)
-	g2.Inherit(h2)
-
-	g1.Destroy("TestCleanup")
-	expectGet(t, h1, g2, &v1)
-	expectGet(t, h2, g2, &v2)
-	for k, v := range map[string]*bool{"key1": &v1, "key2": &v2} {
-		if got, want := *v, false; got != want {
-			t.Errorf("after destroying g1, bound value %q is cleaned up", k)
-		}
-	}
-	g2.Destroy("TestCleanup")
-	if got, want := v1, false; got != want {
-		t.Error("after destroying g2, v1 is cleaned up")
-	}
-	if got, want := v2, true; got != want {
-		t.Error("after destroying g2, v2 is not cleaned up")
-	}
-}
-
 func TestHandleRefCounting(t *testing.T) {
 	s := &memoize.Store{}
 	g1 := s.Generation("g1")