blob: b42a6521f9bdae758d6183948e67b9aa1fe30b41 [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 (
"os"
"testing"
"fidl-lsp/analysis"
"fidl-lsp/state"
)
var (
fuchsiaDir = os.Getenv("FUCHSIA_DIR")
toolsPath = fuchsiaDir + "/out/default.zircon/tools/"
fidlcPath = toolsPath + "fidlc"
fidlLintPath = toolsPath + "fidl-lint"
fidlFormatPath = toolsPath + "fidl-format"
)
func defaultConfig() analysis.Config {
return analysis.Config{
BuildRootDir: fuchsiaDir,
FidlcPath: fidlcPath,
FidlLintPath: fidlLintPath,
FidlFormatPath: fidlFormatPath,
}
}
func TestDefinitionOfLibrary(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"file.fidl",
`library test;`,
)
if err := analyzer.Analyze(fs, "file.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "test",
Location: state.Location{
FileID: state.FileID("file.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 8},
End: state.Position{Line: 0, Character: 12},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("file.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 8},
End: state.Position{Line: 0, Character: 12},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestDefinitionOfLibraryMultipleFiles(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"file1.fidl",
`library test;`,
)
fs.NewFile(
"file2.fidl",
`library test;`,
)
if err := analyzer.Analyze(fs, "file1.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if err := analyzer.Analyze(fs, "file2.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "test",
Location: state.Location{
FileID: state.FileID("file1.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 8},
End: state.Position{Line: 0, Character: 12},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLocs := []state.Location{
{
FileID: state.FileID("file1.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 8},
End: state.Position{Line: 0, Character: 12},
},
},
{
FileID: state.FileID("file2.fidl"),
Range: state.Range{
Start: state.Position{Line: 0, Character: 8},
End: state.Position{Line: 0, Character: 12},
},
},
}
if len(locs) != len(expLocs) {
t.Fatalf("expected %d definition locations", len(expLocs))
}
for _, expLoc := range expLocs {
found := false
for _, loc := range locs {
if expLoc == loc {
found = true
break
}
}
if !found {
t.Errorf("expected but did not find definition of symbol: %v", expLoc)
}
}
}
func TestDefinitionOfLibraryImport(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"import.fidl",
`
library fuchsia.import;
const uint8 FOO = 0;
`,
)
fs.NewFile(
"test.fidl",
`
library test;
using fuchsia.import;
const uint8 BAR = fuchsia.import.FOO;
`,
)
if err := analyzer.Analyze(fs, "import.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if err := analyzer.Analyze(fs, "test.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "fuchsia.import",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 6},
End: state.Position{Line: 3, Character: 20},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("import.fidl"),
Range: state.Range{
Start: state.Position{Line: 1, Character: 8},
End: state.Position{Line: 1, Character: 22},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestDefinitionOfFullyQualifiedSymbol(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"import.fidl",
`
library fuchsia.import;
struct Foo {};
`,
)
fs.NewFile(
"test.fidl",
`
library test;
using fuchsia.import;
protocol Baz {
Method(fuchsia.import.Foo f);
};
`,
)
if err := analyzer.Analyze(fs, "import.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if err := analyzer.Analyze(fs, "test.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "fuchsia.import.Foo",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 5, Character: 11},
End: state.Position{Line: 5, Character: 29},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("import.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestDefinitionOfLocalSymbol(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"test.fidl",
`
library test;
struct Foo {};
protocol Baz {
Method(Foo f);
};
`,
)
if err := analyzer.Analyze(fs, "test.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 6, Character: 11},
End: state.Position{Line: 6, Character: 14},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestDefinitionOfSymbolInSameLibraryDifferentFile(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"test1.fidl",
`
library test;
struct Foo {};
`,
)
fs.NewFile(
"test2.fidl",
`
library test;
protocol Baz {
Method(Foo f);
};
`,
)
if err := analyzer.Analyze(fs, "test1.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if err := analyzer.Analyze(fs, "test2.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test2.fidl"),
Range: state.Range{
Start: state.Position{Line: 5, Character: 11},
End: state.Position{Line: 5, Character: 14},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("test1.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestTwoLibrariesWithSameName(t *testing.T) {
analyzer := analysis.NewAnalyzer(
defaultConfig(),
// Two distinct libraries with the same name
analysis.CompiledLibraries{
{
Name: "fuchsia.test",
Files: []string{"test1.fidl"},
},
{
Name: "fuchsia.test",
Files: []string{"test2.fidl"},
},
},
)
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"test1.fidl",
`
library fuchsia.test;
struct Foo {};
union Bar {
1: Foo foo;
};
`,
)
fs.NewFile(
"test2.fidl",
`
library fuchsia.test;
struct Foo {};
union Bar {
1: Foo foo;
};
`,
)
if err := analyzer.Analyze(fs, "test1.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if err := analyzer.Analyze(fs, "test2.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
locs, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "Foo",
Location: state.Location{
FileID: state.FileID("test1.fidl"),
Range: state.Range{
Start: state.Position{Line: 5, Character: 7},
End: state.Position{Line: 5, Character: 10},
},
},
})
if err != nil {
t.Fatalf("error getting definition of symbol: %s", err)
}
expLoc := state.Location{
FileID: state.FileID("test1.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 7},
End: state.Position{Line: 3, Character: 10},
},
}
if len(locs) != 1 {
t.Fatalf("expected 1 definition location")
}
if locs[0] != expLoc {
t.Errorf("unexpected definition of symbol: expected %v, got %v", expLoc, locs[0])
}
}
func TestDefinitionOfReservedKeyword(t *testing.T) {
analyzer := analysis.NewAnalyzer(defaultConfig(), analysis.CompiledLibraries{})
defer analyzer.Cleanup()
fs := state.NewFileSystem()
fs.NewFile(
"test.fidl",
`
library test;
struct Foo {};
`,
)
if err := analyzer.Analyze(fs, "test.fidl"); err != nil {
t.Fatalf("failed to analyze file: %s", err)
}
if _, err := analyzer.DefinitionOfSymbol(fs, state.Symbol{
Name: "struct",
Location: state.Location{
FileID: state.FileID("test.fidl"),
Range: state.Range{
Start: state.Position{Line: 3, Character: 0},
End: state.Position{Line: 3, Character: 6},
},
},
}); err == nil {
t.Fatalf("expect error on definition of reserved keyword")
}
}