From 5f9351755fc13ce6b9542113c6e61967e89215f6 Mon Sep 17 00:00:00 2001 From: Ian Cottrell Date: Wed, 10 Jul 2019 21:11:23 -0400 Subject: [PATCH] internal/lsp: stop making background contexts everywhere For all uses inside the lsp we use the detatch logic instead For tests we build it in the test harness instead This is in preparation for things on the context becomming important Change-Id: I7e6910e0d3581b82abbeeb09f9c22a99efb73142 Reviewed-on: https://go-review.googlesource.com/c/tools/+/185677 Run-TryBot: Ian Cottrell Reviewed-by: Rebecca Stambler --- internal/lsp/cache/session.go | 5 ++-- internal/lsp/cmd/check_test.go | 3 +-- internal/lsp/cmd/cmd.go | 3 ++- internal/lsp/cmd/cmd_test.go | 3 +++ internal/lsp/cmd/definition_test.go | 5 ++-- internal/lsp/cmd/format_test.go | 3 +-- internal/lsp/lsp_test.go | 42 ++++++++++++++--------------- internal/lsp/protocol/detatch.go | 21 --------------- internal/lsp/protocol/protocol.go | 3 ++- internal/lsp/source/source_test.go | 31 +++++++++++---------- internal/lsp/source/view.go | 2 +- internal/lsp/tests/tests.go | 6 ++++- internal/lsp/workspace.go | 2 +- internal/memoize/detatch.go | 21 --------------- internal/memoize/memoize.go | 4 ++- internal/xcontext/xcontext.go | 23 ++++++++++++++++ 16 files changed, 85 insertions(+), 92 deletions(-) delete mode 100644 internal/lsp/protocol/detatch.go delete mode 100644 internal/memoize/detatch.go create mode 100644 internal/xcontext/xcontext.go diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go index f16814f0..0b568b6e 100644 --- a/internal/lsp/cache/session.go +++ b/internal/lsp/cache/session.go @@ -18,6 +18,7 @@ import ( "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/xlog" "golang.org/x/tools/internal/span" + "golang.org/x/tools/internal/xcontext" ) type session struct { @@ -64,11 +65,11 @@ func (s *session) Cache() source.Cache { return s.cache } -func (s *session) NewView(name string, folder span.URI) source.View { +func (s *session) NewView(ctx context.Context, name string, folder span.URI) source.View { index := atomic.AddInt64(&viewIndex, 1) s.viewMu.Lock() defer s.viewMu.Unlock() - ctx := context.Background() + ctx = xcontext.Detach(ctx) backgroundCtx, cancel := context.WithCancel(ctx) v := &view{ session: s, diff --git a/internal/lsp/cmd/check_test.go b/internal/lsp/cmd/check_test.go index dba72c79..629c073e 100644 --- a/internal/lsp/cmd/check_test.go +++ b/internal/lsp/cmd/check_test.go @@ -5,7 +5,6 @@ package cmd_test import ( - "context" "fmt" "strings" "testing" @@ -23,7 +22,7 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) { fname := uri.Filename() args := []string{"-remote=internal", "check", fname} out := captureStdOut(t, func() { - tool.Main(context.Background(), r.app, args) + tool.Main(r.ctx, r.app, args) }) // parse got into a collection of reports got := map[string]struct{}{} diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go index f0b15409..837019da 100644 --- a/internal/lsp/cmd/cmd.go +++ b/internal/lsp/cmd/cmd.go @@ -26,6 +26,7 @@ import ( "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/span" "golang.org/x/tools/internal/tool" + "golang.org/x/tools/internal/xcontext" ) // Application is the main application as passed to tool.Main @@ -148,7 +149,7 @@ func (app *Application) connect(ctx context.Context) (*connection, error) { return c, nil } connection := newConnection(app) - ctx := context.Background() //TODO:a way of shutting down the internal server + ctx := xcontext.Detach(ctx) //TODO:a way of shutting down the internal server cr, sw, _ := os.Pipe() sr, cw, _ := os.Pipe() var jc *jsonrpc2.Conn diff --git a/internal/lsp/cmd/cmd_test.go b/internal/lsp/cmd/cmd_test.go index a9b173e7..3dbab9ed 100644 --- a/internal/lsp/cmd/cmd_test.go +++ b/internal/lsp/cmd/cmd_test.go @@ -6,6 +6,7 @@ package cmd_test import ( "bytes" + "context" "io/ioutil" "os" "path/filepath" @@ -24,6 +25,7 @@ type runner struct { exporter packagestest.Exporter data *tests.Data app *cmd.Application + ctx context.Context } func TestCommandLine(t *testing.T) { @@ -38,6 +40,7 @@ func testCommandLine(t *testing.T, exporter packagestest.Exporter) { exporter: exporter, data: data, app: cmd.New(data.Config.Dir, data.Exported.Config.Env), + ctx: tests.Context(t), } tests.Run(t, r, data) } diff --git a/internal/lsp/cmd/definition_test.go b/internal/lsp/cmd/definition_test.go index 48904527..bf1531a6 100644 --- a/internal/lsp/cmd/definition_test.go +++ b/internal/lsp/cmd/definition_test.go @@ -5,7 +5,6 @@ package cmd_test import ( - "context" "fmt" "os" "path/filepath" @@ -56,7 +55,7 @@ func TestDefinitionHelpExample(t *testing.T) { fmt.Sprintf("%v:#%v", thisFile, cmd.ExampleOffset)} { args := append(baseArgs, query) got := captureStdOut(t, func() { - tool.Main(context.Background(), cmd.New("", nil), args) + tool.Main(tests.Context(t), cmd.New("", nil), args) }) if !expect.MatchString(got) { t.Errorf("test with %v\nexpected:\n%s\ngot:\n%s", args, expect, got) @@ -84,7 +83,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) { uri := d.Src.URI() args = append(args, fmt.Sprint(d.Src)) got := captureStdOut(t, func() { - tool.Main(context.Background(), r.app, args) + tool.Main(r.ctx, r.app, args) }) got = normalizePaths(r.data, got) if mode&jsonGoDef != 0 && runtime.GOOS == "windows" { diff --git a/internal/lsp/cmd/format_test.go b/internal/lsp/cmd/format_test.go index 1ff8b866..b486fac1 100644 --- a/internal/lsp/cmd/format_test.go +++ b/internal/lsp/cmd/format_test.go @@ -5,7 +5,6 @@ package cmd_test import ( - "context" "os/exec" "regexp" "strings" @@ -40,7 +39,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { } app := cmd.New(r.data.Config.Dir, r.data.Config.Env) got := captureStdOut(t, func() { - tool.Main(context.Background(), app, append([]string{"-remote=internal", "format"}, args...)) + tool.Main(r.ctx, app, append([]string{"-remote=internal", "format"}, args...)) }) got = normalizePaths(r.data, got) // check the first two lines are the expected file header diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 6c358ac0..b056fe95 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -32,18 +32,20 @@ func TestLSP(t *testing.T) { type runner struct { server *Server data *tests.Data + ctx context.Context } const viewName = "lsp_test" func testLSP(t *testing.T, exporter packagestest.Exporter) { + ctx := tests.Context(t) data := tests.Load(t, exporter, "testdata") defer data.Exported.Cleanup() log := xlog.New(xlog.StdSink{}) cache := cache.New() session := cache.NewSession(log) - view := session.NewView(viewName, span.FileURI(data.Config.Dir)) + view := session.NewView(ctx, viewName, span.FileURI(data.Config.Dir)) view.SetEnv(data.Config.Env) for filename, content := range data.Config.Overlay { session.SetOverlay(span.FileURI(filename), content) @@ -59,6 +61,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) { hoverKind: source.SynopsisDocumentation, }, data: data, + ctx: ctx, } tests.Run(t, r, data) } @@ -67,7 +70,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) { func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) { v := r.server.session.View(viewName) for uri, want := range data { - f, err := v.GetFile(context.Background(), uri) + f, err := v.GetFile(r.ctx, uri) if err != nil { t.Fatalf("no file for %s: %v", f, err) } @@ -75,7 +78,7 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) { if !ok { t.Fatalf("%s is not a Go file: %v", uri, err) } - results, err := source.Diagnostics(context.Background(), v, gof, nil) + results, err := source.Diagnostics(r.ctx, v, gof, nil) if err != nil { t.Fatal(err) } @@ -218,7 +221,7 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests func (r *runner) runCompletion(t *testing.T, src span.Span) *protocol.CompletionList { t.Helper() - list, err := r.server.Completion(context.Background(), &protocol.CompletionParams{ + list, err := r.server.Completion(r.ctx, &protocol.CompletionParams{ TextDocumentPositionParams: protocol.TextDocumentPositionParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(src.URI()), @@ -295,7 +298,6 @@ func summarizeCompletionItems(i int, want []source.CompletionItem, got []protoco } func (r *runner) Format(t *testing.T, data tests.Formats) { - ctx := context.Background() for _, spn := range data { uri := spn.URI() filename := uri.Filename() @@ -305,7 +307,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { return out, nil })) - edits, err := r.server.Formatting(context.Background(), &protocol.DocumentFormattingParams{ + edits, err := r.server.Formatting(r.ctx, &protocol.DocumentFormattingParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(uri), }, @@ -316,7 +318,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { } continue } - _, m, err := getSourceFile(ctx, r.server.session.ViewOf(uri), uri) + _, m, err := getSourceFile(r.ctx, r.server.session.ViewOf(uri), uri) if err != nil { t.Error(err) } @@ -333,7 +335,6 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { } func (r *runner) Import(t *testing.T, data tests.Imports) { - ctx := context.Background() for _, spn := range data { uri := spn.URI() filename := uri.Filename() @@ -343,7 +344,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) { return out, nil })) - actions, err := r.server.CodeAction(context.Background(), &protocol.CodeActionParams{ + actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(uri), }, @@ -354,7 +355,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) { } continue } - _, m, err := getSourceFile(ctx, r.server.session.ViewOf(uri), uri) + _, m, err := getSourceFile(r.ctx, r.server.session.ViewOf(uri), uri) if err != nil { t.Error(err) } @@ -393,13 +394,13 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) { var locs []protocol.Location var hover *protocol.Hover if d.IsType { - locs, err = r.server.TypeDefinition(context.Background(), params) + locs, err = r.server.TypeDefinition(r.ctx, params) } else { - locs, err = r.server.Definition(context.Background(), params) + locs, err = r.server.Definition(r.ctx, params) if err != nil { t.Fatalf("failed for %v: %v", d.Src, err) } - hover, err = r.server.Hover(context.Background(), params) + hover, err = r.server.Hover(r.ctx, params) } if err != nil { t.Fatalf("failed for %v: %v", d.Src, err) @@ -446,7 +447,7 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) { TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, Position: loc.Range.Start, } - highlights, err := r.server.DocumentHighlight(context.Background(), params) + highlights, err := r.server.DocumentHighlight(r.ctx, params) if err != nil { t.Fatal(err) } @@ -492,7 +493,7 @@ func (r *runner) Reference(t *testing.T, data tests.References) { Position: loc.Range.Start, }, } - got, err := r.server.References(context.Background(), params) + got, err := r.server.References(r.ctx, params) if err != nil { t.Fatalf("failed for %v: %v", src, err) } @@ -509,7 +510,6 @@ func (r *runner) Reference(t *testing.T, data tests.References) { } func (r *runner) Rename(t *testing.T, data tests.Renames) { - ctx := context.Background() for spn, newText := range data { tag := fmt.Sprintf("%s-rename", newText) @@ -524,7 +524,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) { t.Fatalf("failed for %v: %v", spn, err) } - workspaceEdits, err := r.server.Rename(ctx, &protocol.RenameParams{ + workspaceEdits, err := r.server.Rename(r.ctx, &protocol.RenameParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(uri), }, @@ -544,7 +544,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) { var res []string for uri, edits := range *workspaceEdits.Changes { spnURI := span.URI(uri) - _, m, err := getSourceFile(ctx, r.server.session.ViewOf(span.URI(spnURI)), spnURI) + _, m, err := getSourceFile(r.ctx, r.server.session.ViewOf(span.URI(spnURI)), spnURI) if err != nil { t.Error(err) } @@ -612,7 +612,7 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) { URI: string(uri), }, } - symbols, err := r.server.DocumentSymbol(context.Background(), params) + symbols, err := r.server.DocumentSymbol(r.ctx, params) if err != nil { t.Fatal(err) } @@ -688,7 +688,7 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) { if err != nil { t.Fatalf("failed for %v: %v", loc, err) } - gotSignatures, err := r.server.SignatureHelp(context.Background(), &protocol.TextDocumentPositionParams{ + gotSignatures, err := r.server.SignatureHelp(r.ctx, &protocol.TextDocumentPositionParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(spn.URI()), }, @@ -754,7 +754,7 @@ func (r *runner) Link(t *testing.T, data tests.Links) { if err != nil { t.Fatal(err) } - gotLinks, err := r.server.DocumentLink(context.Background(), &protocol.DocumentLinkParams{ + gotLinks, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{ TextDocument: protocol.TextDocumentIdentifier{ URI: protocol.NewURI(uri), }, diff --git a/internal/lsp/protocol/detatch.go b/internal/lsp/protocol/detatch.go deleted file mode 100644 index 9e34b09a..00000000 --- a/internal/lsp/protocol/detatch.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package protocol - -import ( - "context" - "time" -) - -// detatch returns a context that keeps all the values of its parent context -// but detatches from the cancellation and error handling. -func detatchContext(ctx context.Context) context.Context { return detatchedContext{ctx} } - -type detatchedContext struct{ parent context.Context } - -func (v detatchedContext) Deadline() (time.Time, bool) { return time.Time{}, false } -func (v detatchedContext) Done() <-chan struct{} { return nil } -func (v detatchedContext) Err() error { return nil } -func (v detatchedContext) Value(key interface{}) interface{} { return v.parent.Value(key) } diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go index 2802c3ff..4e1a3cc0 100644 --- a/internal/lsp/protocol/protocol.go +++ b/internal/lsp/protocol/protocol.go @@ -10,13 +10,14 @@ import ( "golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/lsp/telemetry/trace" "golang.org/x/tools/internal/lsp/xlog" + "golang.org/x/tools/internal/xcontext" ) const defaultMessageBufferSize = 20 const defaultRejectIfOverloaded = false func canceller(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID) { - ctx = detatchContext(ctx) + ctx = xcontext.Detach(ctx) ctx, span := trace.StartSpan(ctx, "protocol.canceller") defer span.End() conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: id}) diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index 78d2e90f..c38e38cc 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -30,9 +30,11 @@ func TestSource(t *testing.T) { type runner struct { view source.View data *tests.Data + ctx context.Context } func testSource(t *testing.T, exporter packagestest.Exporter) { + ctx := tests.Context(t) data := tests.Load(t, exporter, "../testdata") defer data.Exported.Cleanup() @@ -40,8 +42,9 @@ func testSource(t *testing.T, exporter packagestest.Exporter) { cache := cache.New() session := cache.NewSession(log) r := &runner{ - view: session.NewView("source_test", span.FileURI(data.Config.Dir)), + view: session.NewView(ctx, "source_test", span.FileURI(data.Config.Dir)), data: data, + ctx: ctx, } r.view.SetEnv(data.Config.Env) for filename, content := range data.Config.Overlay { @@ -52,11 +55,11 @@ func testSource(t *testing.T, exporter packagestest.Exporter) { func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) { for uri, want := range data { - f, err := r.view.GetFile(context.Background(), uri) + f, err := r.view.GetFile(r.ctx, uri) if err != nil { t.Fatal(err) } - results, err := source.Diagnostics(context.Background(), r.view, f.(source.GoFile), nil) + results, err := source.Diagnostics(r.ctx, r.view, f.(source.GoFile), nil) if err != nil { t.Fatal(err) } @@ -132,7 +135,7 @@ func summarizeDiagnostics(i int, want []source.Diagnostic, got []source.Diagnost } func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests.CompletionSnippets, items tests.CompletionItems) { - ctx := context.Background() + ctx := r.ctx for src, itemList := range data { var want []source.CompletionItem for _, pos := range itemList { @@ -289,7 +292,7 @@ func summarizeCompletionItems(i int, want []source.CompletionItem, got []source. } func (r *runner) Format(t *testing.T, data tests.Formats) { - ctx := context.Background() + ctx := r.ctx for _, spn := range data { uri := spn.URI() filename := uri.Filename() @@ -327,7 +330,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { } func (r *runner) Import(t *testing.T, data tests.Imports) { - ctx := context.Background() + ctx := r.ctx for _, spn := range data { uri := spn.URI() filename := uri.Filename() @@ -365,7 +368,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) { } func (r *runner) Definition(t *testing.T, data tests.Definitions) { - ctx := context.Background() + ctx := r.ctx for _, d := range data { f, err := r.view.GetFile(ctx, d.Src.URI()) if err != nil { @@ -407,7 +410,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) { } func (r *runner) Highlight(t *testing.T, data tests.Highlights) { - ctx := context.Background() + ctx := r.ctx for name, locations := range data { src := locations[0] f, err := r.view.GetFile(ctx, src.URI()) @@ -432,7 +435,7 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) { } func (r *runner) Reference(t *testing.T, data tests.References) { - ctx := context.Background() + ctx := r.ctx for src, itemList := range data { f, err := r.view.GetFile(ctx, src.URI()) if err != nil { @@ -478,7 +481,7 @@ func (r *runner) Reference(t *testing.T, data tests.References) { } func (r *runner) Rename(t *testing.T, data tests.Renames) { - ctx := context.Background() + ctx := r.ctx for spn, newText := range data { tag := fmt.Sprintf("%s-rename", newText) @@ -489,11 +492,11 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) { tok := f.GetToken(ctx) pos := tok.Pos(spn.Start().Offset()) - ident, err := source.Identifier(context.Background(), r.view, f.(source.GoFile), pos) + ident, err := source.Identifier(r.ctx, r.view, f.(source.GoFile), pos) if err != nil { t.Error(err) } - changes, err := ident.Rename(context.Background(), newText) + changes, err := ident.Rename(r.ctx, newText) if err != nil { renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { return []byte(err.Error()), nil @@ -568,7 +571,7 @@ func sortSourceTextEdits(d []source.TextEdit) { } func (r *runner) Symbol(t *testing.T, data tests.Symbols) { - ctx := context.Background() + ctx := r.ctx for uri, expectedSymbols := range data { f, err := r.view.GetFile(ctx, uri) if err != nil { @@ -632,7 +635,7 @@ func summarizeSymbols(i int, want []source.Symbol, got []source.Symbol, reason s } func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) { - ctx := context.Background() + ctx := r.ctx for spn, expectedSignature := range data { f, err := r.view.GetFile(ctx, spn.URI()) if err != nil { diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go index 62a281a1..2e99f49d 100644 --- a/internal/lsp/source/view.go +++ b/internal/lsp/source/view.go @@ -129,7 +129,7 @@ type Cache interface { // A session may have many active views at any given time. type Session interface { // NewView creates a new View and returns it. - NewView(name string, folder span.URI) View + NewView(ctx context.Context, name string, folder span.URI) View // Cache returns the cache that created this session. Cache() Cache diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index a1589d82..bfa1bc23 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -127,6 +127,10 @@ type Golden struct { Modified bool } +func Context(t testing.TB) context.Context { + return context.Background() +} + func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data { t.Helper() @@ -193,7 +197,7 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data { // Merge the exported.Config with the view.Config. data.Config = *data.Exported.Config data.Config.Fset = token.NewFileSet() - data.Config.Context = context.Background() + data.Config.Context = Context(nil) data.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { panic("ParseFile should not be called") } diff --git a/internal/lsp/workspace.go b/internal/lsp/workspace.go index 4c761b48..ddb597ec 100644 --- a/internal/lsp/workspace.go +++ b/internal/lsp/workspace.go @@ -31,6 +31,6 @@ func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFold } func (s *Server) addView(ctx context.Context, name string, uri span.URI) error { - s.session.NewView(name, uri) + s.session.NewView(ctx, name, uri) return nil } diff --git a/internal/memoize/detatch.go b/internal/memoize/detatch.go deleted file mode 100644 index 6112de6e..00000000 --- a/internal/memoize/detatch.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package memoize - -import ( - "context" - "time" -) - -// detatch returns a context that keeps all the values of its parent context -// but detatches from the cancellation and error handling. -func detatchContext(ctx context.Context) context.Context { return detatchedContext{ctx} } - -type detatchedContext struct{ parent context.Context } - -func (v detatchedContext) Deadline() (time.Time, bool) { return time.Time{}, false } -func (v detatchedContext) Done() <-chan struct{} { return nil } -func (v detatchedContext) Err() error { return nil } -func (v detatchedContext) Value(key interface{}) interface{} { return v.parent.Value(key) } diff --git a/internal/memoize/memoize.go b/internal/memoize/memoize.go index 9e0cb4c2..279185ce 100644 --- a/internal/memoize/memoize.go +++ b/internal/memoize/memoize.go @@ -19,6 +19,8 @@ import ( "runtime" "sync" "unsafe" + + "golang.org/x/tools/internal/xcontext" ) // Store binds keys to functions, returning handles that can be used to access @@ -180,7 +182,7 @@ func (e *entry) get(ctx context.Context, f Function) (interface{}, bool) { // Use the background context to avoid canceling the function. // The function cannot be canceled even if the context is canceled // because multiple goroutines may depend on it. - value = f(detatchContext(ctx)) + value = f(xcontext.Detach(ctx)) // The function has completed. Update the value in the entry. e.mu.Lock() diff --git a/internal/xcontext/xcontext.go b/internal/xcontext/xcontext.go new file mode 100644 index 00000000..ff8ed4eb --- /dev/null +++ b/internal/xcontext/xcontext.go @@ -0,0 +1,23 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package xcontext is a package to offer the extra functionality we need +// from contexts that is not available from the standard context package. +package xcontext + +import ( + "context" + "time" +) + +// Detach returns a context that keeps all the values of its parent context +// but detaches from the cancellation and error handling. +func Detach(ctx context.Context) context.Context { return detachedContext{ctx} } + +type detachedContext struct{ parent context.Context } + +func (v detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false } +func (v detachedContext) Done() <-chan struct{} { return nil } +func (v detachedContext) Err() error { return nil } +func (v detachedContext) Value(key interface{}) interface{} { return v.parent.Value(key) }