diff --git a/src/analysis.zig b/src/analysis.zig
index a30340f93..49f79eda1 100644
--- a/src/analysis.zig
+++ b/src/analysis.zig
@@ -5600,6 +5600,13 @@ fn addReferencedTypes(
},
.ip_index, .compile_error => {},
- .either => {}, // TODO
+ .either => |either| {
+ for (either) |entry| {
+ try analyser.addReferencedTypes(
+ .{ .data = entry.type_data, .is_type_val = ty.is_type_val },
+ .{ .referenced_types = referenced_types },
+ );
+ }
+ },
}
}
diff --git a/src/features/goto.zig b/src/features/goto.zig
index f8543c3cc..4bdec5a2d 100644
--- a/src/features/goto.zig
+++ b/src/features/goto.zig
@@ -25,97 +25,125 @@ pub const GotoKind = enum {
fn gotoDefinitionSymbol(
analyser: *Analyser,
+ arena: std.mem.Allocator,
name_range: types.Range,
decl_handle: Analyser.DeclWithHandle,
kind: GotoKind,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
+ var locs: std.ArrayListUnmanaged(types.DefinitionLink) = .empty;
+
const token_handle = switch (kind) {
.declaration => try decl_handle.definitionToken(analyser, false),
.definition => try decl_handle.definitionToken(analyser, true),
- .type_definition => blk: {
- if (try decl_handle.resolveType(analyser)) |ty| {
- if (try ty.typeDefinitionToken()) |token_handle| break :blk token_handle;
+ .type_definition => {
+ var set: Analyser.ReferencedType.Set = .empty;
+ var collector: Analyser.ReferencedType.Collector = .{ .referenced_types = &set };
+ if (try decl_handle.resolveType(analyser)) |resolved_type| {
+ try analyser.referencedTypes(
+ resolved_type,
+ &collector,
+ );
}
- const type_declaration = try decl_handle.typeDeclarationNode() orelse return null;
- const target_range = offsets.nodeToRange(type_declaration.handle.tree, type_declaration.node, offset_encoding);
- return .{
- .originSelectionRange = name_range,
- .targetUri = type_declaration.handle.uri,
- .targetRange = target_range,
- .targetSelectionRange = target_range,
- };
+ if (set.count() == 0) {
+ const type_declaration = try decl_handle.typeDeclarationNode() orelse return null;
+ const target_range = offsets.nodeToRange(type_declaration.handle.tree, type_declaration.node, offset_encoding);
+
+ try locs.append(arena, .{
+ .originSelectionRange = name_range,
+ .targetUri = type_declaration.handle.uri,
+ .targetRange = target_range,
+ .targetSelectionRange = target_range,
+ });
+ return try locs.toOwnedSlice(arena);
+ }
+
+ for (set.keys()) |ref| {
+ const target_range = offsets.tokenToRange(ref.handle.tree, ref.token, offset_encoding);
+ try locs.append(arena, .{
+ .originSelectionRange = name_range,
+ .targetUri = ref.handle.uri,
+ .targetRange = target_range,
+ .targetSelectionRange = target_range,
+ });
+ }
+ return try locs.toOwnedSlice(arena);
},
};
const target_range = offsets.tokenToRange(token_handle.handle.tree, token_handle.token, offset_encoding);
- return .{
+ try locs.append(arena, .{
.originSelectionRange = name_range,
.targetUri = token_handle.handle.uri,
.targetRange = target_range,
.targetSelectionRange = target_range,
- };
+ });
+ return try locs.toOwnedSlice(arena);
}
fn gotoDefinitionLabel(
analyser: *Analyser,
+ arena: std.mem.Allocator,
handle: *DocumentStore.Handle,
pos_index: usize,
loc: offsets.Loc,
kind: GotoKind,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
const name_loc = Analyser.identifierLocFromIndex(handle.tree, pos_index) orelse return null;
const name = offsets.locToSlice(handle.tree.source, name_loc);
const decl = (try Analyser.lookupLabel(handle, name, pos_index)) orelse return null;
- return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, loc, offset_encoding), decl, kind, offset_encoding);
+ return try gotoDefinitionSymbol(analyser, arena, offsets.locToRange(handle.tree.source, loc, offset_encoding), decl, kind, offset_encoding);
}
fn gotoDefinitionGlobal(
analyser: *Analyser,
+ arena: std.mem.Allocator,
handle: *DocumentStore.Handle,
pos_index: usize,
kind: GotoKind,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
const name_loc = Analyser.identifierLocFromIndex(handle.tree, pos_index) orelse return null;
const name = offsets.locToSlice(handle.tree.source, name_loc);
const decl = (try analyser.lookupSymbolGlobal(handle, name, pos_index)) orelse return null;
- return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding);
+ return try gotoDefinitionSymbol(analyser, arena, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding);
}
fn gotoDefinitionEnumLiteral(
analyser: *Analyser,
+ arena: std.mem.Allocator,
handle: *DocumentStore.Handle,
source_index: usize,
kind: GotoKind,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
const name_loc = Analyser.identifierLocFromIndex(handle.tree, source_index) orelse return null;
const name = offsets.locToSlice(handle.tree.source, name_loc);
const decl = (try analyser.getSymbolEnumLiteral(handle, source_index, name)) orelse return null;
- return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding);
+ return try gotoDefinitionSymbol(analyser, arena, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding);
}
fn gotoDefinitionBuiltin(
document_store: *DocumentStore,
+ arena: std.mem.Allocator,
handle: *DocumentStore.Handle,
loc: offsets.Loc,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
@@ -136,12 +164,12 @@ fn gotoDefinitionBuiltin(
};
switch (result) {
.failure => return null,
- .success => |uri| return .{
+ .success => |uri| return try arena.dupe(types.DefinitionLink, &.{.{
.originSelectionRange = offsets.locToRange(handle.tree.source, name_loc, offset_encoding),
.targetUri = uri,
.targetRange = target_range,
.targetSelectionRange = target_range,
- },
+ }}),
}
}
@@ -167,8 +195,8 @@ fn gotoDefinitionFieldAccess(
var locs: std.ArrayListUnmanaged(types.DefinitionLink) = .empty;
for (accesses) |access| {
- if (try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), access, kind, offset_encoding)) |l|
- try locs.append(arena, l);
+ if (try gotoDefinitionSymbol(analyser, arena, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), access, kind, offset_encoding)) |l|
+ try locs.appendSlice(arena, l);
}
if (locs.items.len == 0)
@@ -183,7 +211,7 @@ fn gotoDefinitionString(
pos_context: Analyser.PositionContext,
handle: *DocumentStore.Handle,
offset_encoding: offsets.Encoding,
-) error{OutOfMemory}!?types.DefinitionLink {
+) error{OutOfMemory}!?[]const types.DefinitionLink {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
@@ -219,12 +247,12 @@ fn gotoDefinitionString(
.start = .{ .line = 0, .character = 0 },
.end = .{ .line = 0, .character = 0 },
};
- return .{
+ return try arena.dupe(types.DefinitionLink, &.{.{
.originSelectionRange = offsets.locToRange(handle.tree.source, loc, offset_encoding),
.targetUri = uri orelse return null,
.targetRange = target_range,
.targetSelectionRange = target_range,
- };
+ }});
}
pub fn gotoHandler(
@@ -245,38 +273,31 @@ pub fn gotoHandler(
const pos_context = try Analyser.getPositionContext(arena, handle.tree, source_index, true);
const response = switch (pos_context) {
- .builtin => |loc| try gotoDefinitionBuiltin(&server.document_store, handle, loc, server.offset_encoding),
- .var_access => try gotoDefinitionGlobal(&analyser, handle, source_index, kind, server.offset_encoding),
- .field_access => |loc| blk: {
- const links = try gotoDefinitionFieldAccess(&analyser, arena, handle, source_index, loc, kind, server.offset_encoding) orelse return null;
- if (server.client_capabilities.supports_textDocument_definition_linkSupport) {
- return .{ .array_of_DefinitionLink = links };
- }
- switch (links.len) {
- 0 => unreachable,
- 1 => break :blk links[0],
- else => return null,
- }
- },
+ .builtin => |loc| try gotoDefinitionBuiltin(&server.document_store, arena, handle, loc, server.offset_encoding),
+ .var_access => try gotoDefinitionGlobal(&analyser, arena, handle, source_index, kind, server.offset_encoding),
+ .field_access => |loc| try gotoDefinitionFieldAccess(&analyser, arena, handle, source_index, loc, kind, server.offset_encoding),
.import_string_literal,
.cinclude_string_literal,
.embedfile_string_literal,
=> try gotoDefinitionString(&server.document_store, arena, pos_context, handle, server.offset_encoding),
- .label_access, .label_decl => |loc| try gotoDefinitionLabel(&analyser, handle, source_index, loc, kind, server.offset_encoding),
- .enum_literal => try gotoDefinitionEnumLiteral(&analyser, handle, source_index, kind, server.offset_encoding),
+ .label_access, .label_decl => |loc| try gotoDefinitionLabel(&analyser, arena, handle, source_index, loc, kind, server.offset_encoding),
+ .enum_literal => try gotoDefinitionEnumLiteral(&analyser, arena, handle, source_index, kind, server.offset_encoding),
else => null,
} orelse return null;
if (server.client_capabilities.supports_textDocument_definition_linkSupport) {
- return .{
- .array_of_DefinitionLink = try arena.dupe(types.DefinitionLink, &.{response}),
- };
+ return .{ .array_of_DefinitionLink = response };
}
+ const single = switch (response.len) {
+ 1 => response[0],
+ else => return null,
+ };
+
return .{
.Definition = .{ .Location = .{
- .uri = response.targetUri,
- .range = response.targetSelectionRange,
+ .uri = single.targetUri,
+ .range = single.targetSelectionRange,
} },
};
}
diff --git a/tests/lsp_features/definition.zig b/tests/lsp_features/definition.zig
index f9ad92bed..d2d94e7ff 100644
--- a/tests/lsp_features/definition.zig
+++ b/tests/lsp_features/definition.zig
@@ -31,12 +31,12 @@ test "global variable" {
);
try testDefinition(
- \\const S = struct { alpha: u32 };
+ \\const S = struct { alpha: u32 };
\\const <>s: S = S{ .alpha = 5 };
);
try testDefinition(
- \\const S = struct { alpha: u32 };
+ \\const S = struct { alpha: u32 };
\\const <>s = S{ .alpha = 5 };
);
}
@@ -107,8 +107,8 @@ test "struct init" {
test "decl literal on generic type" {
try testDefinition(
- \\fn Box(comptime T: type) type {
- \\ return struct {
+ \\fn Box(comptime T: type) type {
+ \\ return struct {
\\ item: T,
\\ const init: @This() = undefined;
\\ };
@@ -122,7 +122,7 @@ test "decl literal on generic type" {
test "capture" {
try testDefinition(
\\test {
- \\ const S = struct {};
+ \\ const S = struct {};
\\ var maybe: ?S = 5;
\\ if (maybe) |<>some| {}
\\}
@@ -242,6 +242,14 @@ test "non labeled break" {
);
}
+test "function references" {
+ try testDefinition(
+ \\const A = struct {};
+ \\const B = struct {};
+ \\fn fo<>o(_: A) B {}
+ );
+}
+
/// - use `<>` to indicate the cursor position
/// - use `content` to set the expected range of the declaration
/// - use `content` to set the expected range of the definition