diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 39603a7e..117a301c 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -474,7 +474,11 @@ func (r *runner) Reference(t *testing.T, data tests.References) { want := make(map[protocol.Location]bool) for _, pos := range itemList { - loc, err := sm.Location(pos) + m, err := r.mapper(pos.URI()) + if err != nil { + t.Fatal(err) + } + loc, err := m.Location(pos) if err != nil { t.Fatalf("failed for %v: %v", src, err) } @@ -491,12 +495,12 @@ func (r *runner) Reference(t *testing.T, data tests.References) { t.Fatalf("failed for %v: %v", src, err) } - if len(got) != len(itemList) { - t.Errorf("references failed: different lengths got %v want %v", len(got), len(itemList)) + if len(got) != len(want) { + t.Errorf("references failed: different lengths got %v want %v", len(got), len(want)) } for _, loc := range got { if !want[loc] { - t.Errorf("references failed: incorrect references got %v want %v", got, want) + t.Errorf("references failed: incorrect references got %v want %v", loc, want) } } } diff --git a/internal/lsp/references.go b/internal/lsp/references.go index 6be976a7..407371e8 100644 --- a/internal/lsp/references.go +++ b/internal/lsp/references.go @@ -38,11 +38,17 @@ func (s *Server) references(ctx context.Context, params *protocol.ReferenceParam } // Get the location of each reference to return as the result. locations := make([]protocol.Location, 0, len(references)) + seen := make(map[span.Span]bool) for _, ref := range references { refSpan, err := ref.Range.Span() if err != nil { return nil, err } + if seen[refSpan] { + continue // already added this location + } + seen[refSpan] = true + _, refM, err := getSourceFile(ctx, view, refSpan.URI()) if err != nil { return nil, err diff --git a/internal/lsp/source/references.go b/internal/lsp/source/references.go index b5178e94..b832e43e 100644 --- a/internal/lsp/source/references.go +++ b/internal/lsp/source/references.go @@ -22,16 +22,11 @@ type ReferenceInfo struct { isDeclaration bool } -// References returns a list of references for a given identifier within a package. +// References returns a list of references for a given identifier within the packages +// containing i.File. func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, error) { var references []*ReferenceInfo - if i.pkg == nil || i.pkg.IsIllTyped() { - return nil, fmt.Errorf("package for %s is ill typed", i.File.URI()) - } - info := i.pkg.GetTypesInfo() - if info == nil { - return nil, fmt.Errorf("package %s has no types info", i.pkg.PkgPath()) - } + // If the object declaration is nil, assume it is an import spec and do not look for references. if i.decl.obj == nil { return nil, fmt.Errorf("no references for an import spec") @@ -39,7 +34,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro if i.decl.wasImplicit { // The definition is implicit, so we must add it separately. // This occurs when the variable is declared in a type switch statement - // or is an implicit package name. + // or is an implicit package name. Both implicits are local to a file. references = append(references, &ReferenceInfo{ Name: i.decl.obj.Name(), Range: i.decl.rng, @@ -47,28 +42,42 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro isDeclaration: true, }) } - for ident, obj := range info.Defs { - if obj == nil || obj.Pos() != i.decl.obj.Pos() { - continue + + pkgs := i.File.GetPackages(ctx) + for _, pkg := range pkgs { + if pkg == nil || pkg.IsIllTyped() { + return nil, fmt.Errorf("package for %s is ill typed", i.File.URI()) } - references = append(references, &ReferenceInfo{ - Name: ident.Name, - Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), - ident: ident, - obj: obj, - isDeclaration: true, - }) - } - for ident, obj := range info.Uses { - if obj == nil || obj.Pos() != i.decl.obj.Pos() { - continue + info := pkg.GetTypesInfo() + if info == nil { + return nil, fmt.Errorf("package %s has no types info", pkg.PkgPath()) } - references = append(references, &ReferenceInfo{ - Name: ident.Name, - Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), - ident: ident, - obj: obj, - }) + for ident, obj := range info.Defs { + // TODO(suzmue): support the case where an identifier may have two different declarations. + if obj == nil || obj.Pos() != i.decl.obj.Pos() { + continue + } + references = append(references, &ReferenceInfo{ + Name: ident.Name, + Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), + ident: ident, + obj: obj, + isDeclaration: true, + }) + } + for ident, obj := range info.Uses { + if obj == nil || obj.Pos() != i.decl.obj.Pos() { + continue + } + references = append(references, &ReferenceInfo{ + Name: ident.Name, + Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), + ident: ident, + obj: obj, + }) + } + } + return references, nil } diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index 1f82eeda..641b0ba0 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -450,20 +450,26 @@ func (r *runner) Reference(t *testing.T, data tests.References) { want[pos] = true } - got, err := ident.References(ctx) + refs, err := ident.References(ctx) if err != nil { t.Fatalf("failed for %v: %v", src, err) } - if len(got) != len(itemList) { - t.Errorf("references failed: different lengths got %v want %v", len(got), len(itemList)) - } - for _, refInfo := range got { + got := make(map[span.Span]bool) + for _, refInfo := range refs { refSpan, err := refInfo.Range.Span() if err != nil { t.Errorf("failed for %v item %v: %v", src, refInfo.Name, err) } - if !want[refSpan] { + got[refSpan] = true + } + + if len(got) != len(want) { + t.Errorf("references failed: different lengths got %v want %v", len(got), len(want)) + } + + for spn, _ := range got { + if !want[spn] { t.Errorf("references failed: incorrect references got %v want locations %v", got, want) } } diff --git a/internal/lsp/testdata/testy/testy.go b/internal/lsp/testdata/testy/testy.go index f5538076..ba1205e8 100644 --- a/internal/lsp/testdata/testy/testy.go +++ b/internal/lsp/testdata/testy/testy.go @@ -1,5 +1,5 @@ package testy -func a() { //@item(funcA, "a()", "", "func") +func a() { //@mark(identA, "a"),item(funcA, "a()", "", "func"),refs("a", identA, testyA) //@complete("", funcA) } diff --git a/internal/lsp/testdata/testy/testy_test.go b/internal/lsp/testdata/testy/testy_test.go index f4a35970..82e4070a 100644 --- a/internal/lsp/testdata/testy/testy_test.go +++ b/internal/lsp/testdata/testy/testy_test.go @@ -3,6 +3,6 @@ package testy import "testing" func TestSomething(t *testing.T) { //@item(TestSomething, "TestSomething(t *testing.T)", "", "func") - var x int //@diag("x", "LSP", "x declared but not used") - a() + var x int //@mark(testyX, "x"),diag("x", "LSP", "x declared but not used"),refs("x", testyX) + a() //@mark(testyA, "a") } diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index f0fdb485..447cfd70 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -33,7 +33,7 @@ const ( ExpectedDefinitionsCount = 38 ExpectedTypeDefinitionsCount = 2 ExpectedHighlightsCount = 2 - ExpectedReferencesCount = 2 + ExpectedReferencesCount = 4 ExpectedRenamesCount = 8 ExpectedSymbolsCount = 1 ExpectedSignaturesCount = 20