internal/lsp: find references in test packages

Find references to identifiers in both a package and its test package.

Change-Id: I9d9da4aa37c36c448336aed044df79cfd1c903f1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/183990
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 39603a7..117a301 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -474,7 +474,11 @@
 
 		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 @@
 			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 6be976a..407371e 100644
--- a/internal/lsp/references.go
+++ b/internal/lsp/references.go
@@ -38,11 +38,17 @@
 	}
 	// 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 b5178e9..b832e43 100644
--- a/internal/lsp/source/references.go
+++ b/internal/lsp/source/references.go
@@ -22,16 +22,11 @@
 	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 @@
 	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 @@
 			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 1f82eed..641b0ba 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -450,20 +450,26 @@
 			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 f553807..ba1205e 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 f4a3597..82e4070 100644
--- a/internal/lsp/testdata/testy/testy_test.go
+++ b/internal/lsp/testdata/testy/testy_test.go
@@ -3,6 +3,6 @@
 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 f0fdb48..447cfd7 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -33,7 +33,7 @@
 	ExpectedDefinitionsCount       = 38
 	ExpectedTypeDefinitionsCount   = 2
 	ExpectedHighlightsCount        = 2
-	ExpectedReferencesCount        = 2
+	ExpectedReferencesCount        = 4
 	ExpectedRenamesCount           = 8
 	ExpectedSymbolsCount           = 1
 	ExpectedSignaturesCount        = 20