blob: 5e021b46ec9ea25fc043336b0f97378fbaa7eade [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 analysis_test
import (
"testing"
"fidl-lsp/analysis"
"fidl-lsp/state"
)
type TestFile struct {
name state.FileID
text string
}
func TestReferencesToSymbol(t *testing.T) {
cases := []struct {
name string
files []TestFile
symbol state.Symbol
expRefs []state.Location
}{
{
name: "Reference to library declaration",
files: []TestFile{
{
"test.fidl",
`library fuchsia.test;`,
},
{
"other.fidl",
`
library other;
using fuchsia.test;`,
},
},
symbol: state.Symbol{
Name: "fuchsia.test",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 6},
End: state.Position{Line: 0, Character: 10},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("other.fidl"),
Range: state.Range{
Start: state.Position{Line: 2, Character: 6},
End: state.Position{Line: 2, Character: 18},
},
},
},
},
{
name: "Reference to library import",
files: []TestFile{
{
"test.fidl",
`library fuchsia.test;`,
},
{
"other.fidl",
`
library other;
using fuchsia.test;`,
},
},
symbol: state.Symbol{
Name: "fuchsia.test",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 2, Character: 6},
End: state.Position{Line: 2, Character: 18},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("other.fidl"),
Range: state.Range{
Start: state.Position{Line: 2, Character: 6},
End: state.Position{Line: 2, Character: 18},
},
},
},
},
{
name: "Reference to symbol in same file",
files: []TestFile{{
"test.fidl",
`
library test;
struct Foo {};
protocol Bar {
Method(Foo foo);
// ~~~
};
`,
}},
symbol: state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 6, Character: 12},
End: state.Position{Line: 6, Character: 15},
},
},
},
},
{
name: "Reference to symbol in other file in same library",
files: []TestFile{
{
"test1.fidl",
`
library test;
struct Foo {};
`,
},
{
"test2.fidl",
`
library test;
protocol Bar {
Method(Foo foo);
// ~~~
};
`,
},
},
symbol: state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test1.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("test2.fidl"),
Range: state.Range{
Start: state.Position{Line: 4, Character: 12},
End: state.Position{Line: 4, Character: 15},
},
},
},
},
{
name: "References to symbol in other library",
files: []TestFile{
{
"imported.fidl",
`
library fuchsia.imported;
struct Foo {};
`,
},
{
"test.fidl",
`
library test;
using fuchsia.imported;
protocol Bar {
Method(fuchsia.imported.Foo foo);
// ~~~
};
`,
},
},
symbol: state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("imported.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 5, Character: 29},
End: state.Position{Line: 5, Character: 32},
},
},
},
},
{
name: "No references to symbol",
files: []TestFile{{
"test.fidl",
`
library test;
struct Foo {};
`,
}},
symbol: state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
},
},
expRefs: []state.Location{},
},
{
name: "References to type alias",
files: []TestFile{
{
"zx.fidl",
`
library zx;
using status = int32;
`,
},
{
"test.fidl",
`
library test;
using zx;
protocol Foo {
Method() -> (zx.status status);
// ~~~~~~
};
`,
},
},
symbol: state.Symbol{
Name: "status",
Location: state.Location{
FileID: state.FileID("zx.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 5, Character: 24},
End: state.Position{Line: 5, Character: 30},
},
},
},
},
{
name: "References in type parameters",
files: []TestFile{{
"test.fidl",
`
library test;
table Foo {};
protocol Bar {
Method(vector<Foo> items);
// ~~~~~
};
`,
}},
symbol: state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 6},
End: state.Position{Line: 3, Character: 9},
},
},
},
expRefs: []state.Location{
{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 6, Character: 20},
End: state.Position{Line: 6, Character: 25},
},
},
},
},
}
for _, ex := range cases {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
for _, file := range ex.files {
fs.NewFile(file.name, file.text)
}
for _, file := range ex.files {
if err := analyzer.Analyze(fs, file.name); err != nil {
t.Fatalf("case `%s`: failed to analyze file `%s`: %s", ex.name, file, err)
}
}
refs, err := analyzer.ReferencesToSymbol(fs, ex.symbol)
if err != nil {
t.Fatalf("case `%s`: error getting references to symbol: %s", ex.name, err)
}
if len(refs) != len(ex.expRefs) {
t.Fatalf("case `%s`: expected %d references, got %d", ex.name, len(ex.expRefs), len(refs))
}
for _, ref := range refs {
found := false
for _, expRef := range ex.expRefs {
if ref == expRef {
found = true
break
}
}
if !found {
t.Errorf(
"case `%s`: got unexpected reference %v to symbol %s",
ex.name,
ref,
ex.symbol.Name,
)
}
}
for _, expRef := range ex.expRefs {
found := false
for _, ref := range refs {
if ref == expRef {
found = true
break
}
}
if !found {
t.Errorf(
"case `%s`: expected but did not find reference %v to symbol %s",
ex.name,
expRef,
ex.symbol.Name,
)
}
}
}
}