Skip to content

Commit bd24e21

Browse files
authored
Merge pull request #2348 from xdBronch/references
speed up reference finding
2 parents f0350ca + 4f3cf63 commit bd24e21

File tree

4 files changed

+116
-119
lines changed

4 files changed

+116
-119
lines changed

src/DocumentScope.zig

Lines changed: 28 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -77,70 +77,13 @@ pub const DeclarationLookupContext = struct {
7777
}
7878
};
7979

80-
/// Assumes that the `node` is not a container_field of a struct tuple field.
81-
/// Returns a `.identifier` or `.builtin` token.
82-
fn getDeclNameToken(tree: Ast, node: Ast.Node.Index) ?Ast.TokenIndex {
83-
var buffer: [1]Ast.Node.Index = undefined;
84-
const token_index = switch (tree.nodeTag(node)) {
85-
.local_var_decl,
86-
.global_var_decl,
87-
.simple_var_decl,
88-
.aligned_var_decl,
89-
=> tree.nodeMainToken(node) + 1,
90-
.fn_proto,
91-
.fn_proto_multi,
92-
.fn_proto_one,
93-
.fn_proto_simple,
94-
.fn_decl,
95-
=> tree.fullFnProto(&buffer, node).?.name_token orelse return null,
96-
97-
.identifier => tree.nodeMainToken(node),
98-
.test_decl => tree.nodeData(node).opt_token_and_node[0].unwrap() orelse return null,
99-
100-
.container_field,
101-
.container_field_init,
102-
.container_field_align,
103-
=> tree.nodeMainToken(node),
104-
105-
.root,
106-
.container_decl,
107-
.container_decl_trailing,
108-
.container_decl_arg,
109-
.container_decl_arg_trailing,
110-
.container_decl_two,
111-
.container_decl_two_trailing,
112-
.tagged_union,
113-
.tagged_union_trailing,
114-
.tagged_union_two,
115-
.tagged_union_two_trailing,
116-
.tagged_union_enum_tag,
117-
.tagged_union_enum_tag_trailing,
118-
.block,
119-
.block_semicolon,
120-
.block_two,
121-
.block_two_semicolon,
122-
=> return null,
123-
124-
else => unreachable,
125-
};
126-
127-
return switch (tree.tokenTag(token_index)) {
128-
.identifier, .builtin => token_index,
129-
else => null,
130-
};
131-
}
132-
13380
pub const Declaration = union(enum) {
13481
/// Index of the ast node.
13582
/// Can have one of the following tags:
136-
/// - `.root`
137-
/// - `.container_decl`
138-
/// - `.tagged_union`
13983
/// - `.container_field`
14084
/// - `.fn_proto`
14185
/// - `.fn_decl`
14286
/// - `.var_decl`
143-
/// - `.block`
14487
ast_node: Ast.Node.Index,
14588
/// Function parameter
14689
function_parameter: Param,
@@ -250,7 +193,34 @@ pub const Declaration = union(enum) {
250193
/// Returns a `.identifier` or `.builtin` token.
251194
pub fn nameToken(decl: Declaration, tree: Ast) Ast.TokenIndex {
252195
return switch (decl) {
253-
.ast_node => |n| getDeclNameToken(tree, n).?,
196+
.ast_node => |node| {
197+
var buffer: [1]Ast.Node.Index = undefined;
198+
const token_index = switch (tree.nodeTag(node)) {
199+
.local_var_decl,
200+
.global_var_decl,
201+
.simple_var_decl,
202+
.aligned_var_decl,
203+
=> tree.nodeMainToken(node) + 1,
204+
.fn_proto,
205+
.fn_proto_multi,
206+
.fn_proto_one,
207+
.fn_proto_simple,
208+
.fn_decl,
209+
=> tree.fullFnProto(&buffer, node).?.name_token.?,
210+
211+
.container_field,
212+
.container_field_init,
213+
.container_field_align,
214+
=> tree.nodeMainToken(node),
215+
216+
else => unreachable,
217+
};
218+
219+
switch (tree.tokenTag(token_index)) {
220+
.identifier, .builtin => return token_index,
221+
else => unreachable,
222+
}
223+
},
254224
.function_parameter => |payload| payload.get(tree).?.name_token.?,
255225
.optional_payload => |payload| payload.identifier,
256226
.error_union_payload => |payload| payload.identifier,

src/analysis.zig

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3867,7 +3867,7 @@ pub const Type = struct {
38673867
return;
38683868
}
38693869
if (token >= 1 and tree.tokenTag(token - 1) == .keyword_return) blk: {
3870-
const function_scope = innermostFunctionScopeAtIndex(doc_scope, tree.tokenStart(token - 1)).unwrap() orelse break :blk;
3870+
const function_scope = innermostScopeAtIndexWithTag(doc_scope, tree.tokenStart(token - 1), .initOne(.function)).unwrap() orelse break :blk;
38713871
const function_node = doc_scope.getScopeAstNode(function_scope).?;
38723872
var buf: [1]Ast.Node.Index = undefined;
38733873
const func = tree.fullFnProto(&buf, function_node).?;
@@ -5293,36 +5293,28 @@ pub fn iterateLabels(handle: *DocumentStore.Handle, source_index: usize, comptim
52935293
}
52945294
}
52955295

5296-
pub fn innermostScopeAtIndex(document_scope: DocumentScope, source_index: usize) Scope.Index {
5297-
var scope_iterator = iterateEnclosingScopes(&document_scope, source_index);
5298-
var scope_index: Scope.Index = scope_iterator.next().unwrap().?; // the DocumentScope's root scope must exist
5299-
while (scope_iterator.next().unwrap()) |inner_scope| {
5300-
scope_index = inner_scope;
5301-
}
5302-
return scope_index;
5296+
pub fn innermostScopeAtIndex(
5297+
document_scope: DocumentScope,
5298+
source_index: usize,
5299+
) Scope.Index {
5300+
return innermostScopeAtIndexWithTag(document_scope, source_index, .initFull()).unwrap().?;
53035301
}
53045302

5305-
pub fn innermostFunctionScopeAtIndex(document_scope: DocumentScope, source_index: usize) Scope.OptionalIndex {
5303+
pub fn innermostScopeAtIndexWithTag(
5304+
document_scope: DocumentScope,
5305+
source_index: usize,
5306+
tag_filter: std.EnumSet(Scope.Tag),
5307+
) Scope.OptionalIndex {
53065308
var scope_iterator = iterateEnclosingScopes(&document_scope, source_index);
53075309
var scope_index: Scope.OptionalIndex = .none;
53085310
while (scope_iterator.next().unwrap()) |inner_scope| {
5309-
if (document_scope.getScopeTag(inner_scope) != .function) continue;
5311+
const scope_tag = document_scope.getScopeTag(inner_scope);
5312+
if (!tag_filter.contains(scope_tag)) continue;
53105313
scope_index = inner_scope.toOptional();
53115314
}
53125315
return scope_index;
53135316
}
53145317

5315-
pub fn innermostBlockScope(document_scope: DocumentScope, source_index: usize) Ast.Node.Index {
5316-
var scope_iterator = iterateEnclosingScopes(&document_scope, source_index);
5317-
var ast_node: ?Ast.Node.Index = null;
5318-
while (scope_iterator.next().unwrap()) |inner_scope| {
5319-
if (document_scope.getScopeAstNode(inner_scope)) |node| {
5320-
ast_node = node;
5321-
}
5322-
}
5323-
return ast_node.?; // the DocumentScope's root scope is guaranteed to have an Ast Node
5324-
}
5325-
53265318
pub fn innermostContainer(analyser: *Analyser, handle: *DocumentStore.Handle, source_index: usize) error{OutOfMemory}!Type {
53275319
const tree = handle.tree;
53285320
const document_scope = try handle.getDocumentScope();

src/features/references.zig

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const Builder = struct {
6565
locations: std.ArrayListUnmanaged(types.Location) = .empty,
6666
/// this is the declaration we are searching for
6767
decl_handle: Analyser.DeclWithHandle,
68+
/// the decl is local to a function, block, etc
69+
local_only_decl: bool,
6870
/// Whether the `decl_handle` has been added
6971
did_add_decl_handle: bool = false,
7072
analyser: *Analyser,
@@ -104,6 +106,10 @@ const Builder = struct {
104106
fn referenceNode(self: *const Context, tree: Ast, node: Ast.Node.Index) error{OutOfMemory}!void {
105107
const builder = self.builder;
106108
const handle = self.handle;
109+
const decl_name = offsets.identifierTokenToNameSlice(
110+
builder.decl_handle.handle.tree,
111+
builder.decl_handle.nameToken(),
112+
);
107113

108114
switch (tree.nodeTag(node)) {
109115
.identifier,
@@ -119,6 +125,7 @@ const Builder = struct {
119125
else => unreachable,
120126
};
121127
const name = offsets.identifierTokenToNameSlice(tree, name_token);
128+
if (!std.mem.eql(u8, name, decl_name)) return;
122129

123130
const child = try builder.analyser.lookupSymbolGlobal(
124131
handle,
@@ -131,15 +138,18 @@ const Builder = struct {
131138
}
132139
},
133140
.field_access => {
134-
const lhs_node, const field_name = tree.nodeData(node).node_and_token;
141+
if (builder.local_only_decl) return;
142+
const lhs_node, const field_token = tree.nodeData(node).node_and_token;
143+
const name = offsets.identifierTokenToNameSlice(tree, field_token);
144+
if (!std.mem.eql(u8, name, decl_name)) return;
145+
135146
const lhs = try builder.analyser.resolveTypeOfNode(.of(lhs_node, handle)) orelse return;
136147
const deref_lhs = try builder.analyser.resolveDerefType(lhs) orelse lhs;
137148

138-
const symbol = offsets.identifierTokenToNameSlice(tree, field_name);
139-
const child = try deref_lhs.lookupSymbol(builder.analyser, symbol) orelse return;
149+
const child = try deref_lhs.lookupSymbol(builder.analyser, name) orelse return;
140150

141151
if (builder.decl_handle.eql(child)) {
142-
try builder.add(handle, field_name);
152+
try builder.add(handle, field_token);
143153
}
144154
},
145155
.struct_init_one,
@@ -151,23 +161,28 @@ const Builder = struct {
151161
.struct_init_dot_two,
152162
.struct_init_dot_two_comma,
153163
=> {
164+
if (builder.local_only_decl) return;
154165
var buffer: [2]Ast.Node.Index = undefined;
155166
const struct_init = tree.fullStructInit(&buffer, node).?;
156167
for (struct_init.ast.fields) |value_node| { // the node of `value` in `.name = value`
157168
const name_token = tree.firstToken(value_node) - 2; // math our way two token indexes back to get the `name`
158169
const name_loc = offsets.tokenToLoc(tree, name_token);
159170
const name = offsets.locToSlice(tree.source, name_loc);
171+
if (!std.mem.eql(u8, name, decl_name)) continue;
160172

161173
const lookup = try builder.analyser.lookupSymbolFieldInit(handle, name, node, &.{}) orelse continue;
162174

163175
if (builder.decl_handle.eql(lookup)) {
164176
try builder.add(handle, name_token);
177+
return;
165178
}
166179
}
167180
},
168181
.enum_literal => {
182+
if (builder.local_only_decl) return;
169183
const name_token = tree.nodeMainToken(node);
170184
const name = offsets.identifierTokenToNameSlice(handle.tree, name_token);
185+
if (!std.mem.eql(u8, name, decl_name)) return;
171186
const lookup = try builder.analyser.getSymbolEnumLiteral(handle, tree.tokenStart(name_token), name) orelse return;
172187

173188
if (builder.decl_handle.eql(lookup)) {
@@ -242,48 +257,61 @@ fn symbolReferences(
242257

243258
std.debug.assert(decl_handle.decl != .label); // use `labelReferences` instead
244259

245-
var builder = Builder{
246-
.allocator = allocator,
247-
.analyser = analyser,
248-
.decl_handle = decl_handle,
249-
.encoding = encoding,
250-
};
251-
errdefer builder.deinit();
252-
253-
const curr_handle = decl_handle.handle;
254-
if (include_decl) try builder.add(curr_handle, decl_handle.nameToken());
255-
256-
switch (decl_handle.decl) {
257-
.ast_node => {
258-
try builder.collectReferences(curr_handle, .root);
259-
260-
const source_index = decl_handle.handle.tree.tokenStart(decl_handle.nameToken());
261-
// highlight requests only pertain to the current document, otherwise we can try to narrow things down
262-
const workspace = if (request == .highlight) false else blk: {
263-
const doc_scope = try curr_handle.getDocumentScope();
264-
const scope_index = Analyser.innermostScopeAtIndex(doc_scope, source_index);
265-
break :blk switch (doc_scope.getScopeTag(scope_index)) {
266-
.function, .block => false,
267-
.container, .container_usingnamespace => decl_handle.isPublic(),
268-
.other => true,
269-
};
270-
};
271-
if (workspace) {
272-
try gatherReferences(allocator, analyser, curr_handle, skip_std_references, include_decl, &builder, .get);
273-
}
260+
const doc_scope = try decl_handle.handle.getDocumentScope();
261+
const source_index = decl_handle.handle.tree.tokenStart(decl_handle.nameToken());
262+
const scope_index = Analyser.innermostScopeAtIndexWithTag(doc_scope, source_index, .init(.{
263+
.block = true,
264+
.container = true,
265+
.container_usingnamespace = true,
266+
.function = false,
267+
.other = false,
268+
})).unwrap().?;
269+
const scope_node = doc_scope.getScopeAstNode(scope_index).?;
270+
271+
// If `local_node != null`, references to the declaration can only be
272+
// found inside of the given ast node.
273+
const local_node: ?Ast.Node.Index = switch (decl_handle.decl) {
274+
.ast_node => switch (doc_scope.getScopeTag(scope_index)) {
275+
.block => scope_node,
276+
.container, .container_usingnamespace => null,
277+
.function, .other => unreachable,
274278
},
275279
.optional_payload,
276280
.error_union_payload,
277281
.error_union_error,
278282
.for_loop_payload,
279283
.assign_destructure,
280-
.switch_payload,
281-
=> {
282-
try builder.collectReferences(curr_handle, .root);
283-
},
284-
.function_parameter => |payload| try builder.collectReferences(curr_handle, payload.func),
284+
=> scope_node,
285+
.switch_payload => |payload| payload.node,
286+
.function_parameter => |payload| payload.func,
285287
.label => unreachable, // handled separately by labelReferences
286-
.error_token => {},
288+
.error_token => return .empty,
289+
};
290+
291+
var builder: Builder = .{
292+
.allocator = allocator,
293+
.analyser = analyser,
294+
.decl_handle = decl_handle,
295+
.local_only_decl = local_node != null,
296+
.encoding = encoding,
297+
};
298+
errdefer builder.deinit();
299+
300+
if (include_decl) try builder.add(decl_handle.handle, decl_handle.nameToken());
301+
302+
try builder.collectReferences(decl_handle.handle, local_node orelse .root);
303+
304+
const workspace = local_node == null and request != .highlight and decl_handle.isPublic();
305+
if (workspace) {
306+
try gatherReferences(
307+
allocator,
308+
analyser,
309+
decl_handle.handle,
310+
skip_std_references,
311+
include_decl,
312+
&builder,
313+
.get,
314+
);
287315
}
288316

289317
return builder.locations;

src/features/signature_help.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ pub fn getSignatureInfo(
7878
markup_kind: types.MarkupKind,
7979
) !?types.SignatureInformation {
8080
const document_scope = try handle.getDocumentScope();
81-
const innermost_block = Analyser.innermostBlockScope(document_scope, absolute_index);
81+
const innermost_block_scope = Analyser.innermostScopeAtIndexWithTag(document_scope, absolute_index, .init(.{
82+
.block = true,
83+
.container = true,
84+
.container_usingnamespace = true,
85+
.function = true,
86+
.other = false,
87+
})).unwrap().?;
88+
const innermost_block = document_scope.getScopeAstNode(innermost_block_scope).?;
8289
const tree = handle.tree;
8390

8491
// Use the innermost scope to determine the earliest token we would need

0 commit comments

Comments
 (0)