Skip to content

Commit a48ccd0

Browse files
committed
actually @import("root") to the root module
1 parent 92ed3c6 commit a48ccd0

File tree

3 files changed

+236
-63
lines changed

3 files changed

+236
-63
lines changed

src/DocumentStore.zig

Lines changed: 145 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ pub const Handle = struct {
223223
self.* = undefined;
224224
}
225225
} = .init,
226+
227+
associated_compilation_units: GetAssociatedCompilationUnitsResult = .unresolved,
226228
},
227229

228230
const ZirOrZoir = union(Ast.Mode) {
@@ -302,6 +304,7 @@ pub const Handle = struct {
302304
self.cimports.deinit(allocator);
303305

304306
self.impl.associated_build_file.deinit(allocator);
307+
self.impl.associated_compilation_units.deinit(allocator);
305308

306309
self.* = undefined;
307310
}
@@ -508,6 +511,93 @@ pub const Handle = struct {
508511
return .none;
509512
}
510513

514+
pub const GetAssociatedCompilationUnitsResult = union(enum) {
515+
/// The Handle has no associated compilation unit.
516+
none,
517+
/// The associated compilation unit has not been resolved yet.
518+
unresolved,
519+
/// The associated compilation unit has been successfully resolved to a list of root module.
520+
resolved: []const []const u8,
521+
522+
fn deinit(result: *GetAssociatedCompilationUnitsResult, allocator: std.mem.Allocator) void {
523+
switch (result.*) {
524+
.none, .unresolved => {},
525+
.resolved => |root_source_files| {
526+
allocator.free(root_source_files);
527+
},
528+
}
529+
result.* = undefined;
530+
}
531+
};
532+
533+
/// Returns the root source file of the root module of the given handle. Same as `@import("root")`.
534+
pub fn getAssociatedCompilationUnits(self: *Handle, document_store: *DocumentStore) error{OutOfMemory}!GetAssociatedCompilationUnitsResult {
535+
const build_file, const target_root_source_file = switch (self.impl.associated_compilation_units) {
536+
else => return self.impl.associated_compilation_units,
537+
.unresolved => switch (try self.getAssociatedBuildFile(document_store)) {
538+
.none => return .none,
539+
.unresolved => return .unresolved,
540+
.resolved => |resolved| .{ resolved.build_file, resolved.root_source_file },
541+
},
542+
};
543+
544+
const build_config = build_file.tryLockConfig() orelse return .none;
545+
defer build_file.unlockConfig();
546+
547+
const allocator = document_store.allocator;
548+
const modules = &build_config.modules.map;
549+
550+
var visted: std.DynamicBitSetUnmanaged = try .initEmpty(allocator, modules.count());
551+
defer visted.deinit(allocator);
552+
553+
var queue: std.ArrayList(usize) = try .initCapacity(allocator, 1);
554+
defer queue.deinit(allocator);
555+
556+
const target_index = modules.getIndex(target_root_source_file).?;
557+
558+
// We only care about the root source file of each root module so we convert them to a set.
559+
var root_modules: std.StringArrayHashMapUnmanaged(void) = .empty;
560+
defer root_modules.deinit(allocator);
561+
562+
try root_modules.ensureTotalCapacity(allocator, build_config.compilations.len);
563+
for (build_config.compilations) |compile| {
564+
root_modules.putAssumeCapacity(compile.root_module, {});
565+
}
566+
567+
var results: std.ArrayList([]const u8) = .empty;
568+
defer results.deinit(allocator);
569+
570+
// Do a graph search from root modules until we reach `root_source_file`
571+
for (root_modules.keys()) |root_module| {
572+
visted.unsetAll();
573+
queue.clearRetainingCapacity();
574+
queue.appendAssumeCapacity(modules.getIndex(root_module).?);
575+
576+
while (queue.pop()) |index| {
577+
if (index == target_index) {
578+
try results.append(allocator, root_module);
579+
break;
580+
}
581+
582+
if (visted.isSet(index)) continue;
583+
visted.set(index);
584+
585+
const imported_modules = modules.values()[index].import_table.map.values();
586+
try queue.ensureUnusedCapacity(allocator, imported_modules.len);
587+
for (imported_modules) |root_source_file| {
588+
queue.appendAssumeCapacity(modules.getIndex(root_source_file) orelse continue);
589+
}
590+
}
591+
}
592+
593+
if (results.items.len == 0) {
594+
self.impl.associated_compilation_units = .none;
595+
} else {
596+
self.impl.associated_compilation_units = .{ .resolved = try results.toOwnedSlice(allocator) };
597+
}
598+
return self.impl.associated_compilation_units;
599+
}
600+
511601
fn getLazy(
512602
self: *Handle,
513603
comptime T: type,
@@ -1346,10 +1436,14 @@ fn createAndStoreDocument(
13461436

13471437
if (gop.found_existing) {
13481438
std.debug.assert(new_handle.impl.associated_build_file == .init);
1439+
std.debug.assert(new_handle.impl.associated_compilation_units == .unresolved);
13491440
if (lsp_synced) {
13501441
new_handle.impl.associated_build_file = gop.value_ptr.*.impl.associated_build_file;
13511442
gop.value_ptr.*.impl.associated_build_file = .init;
13521443

1444+
new_handle.impl.associated_compilation_units = gop.value_ptr.*.impl.associated_compilation_units;
1445+
gop.value_ptr.*.impl.associated_compilation_units = .unresolved;
1446+
13531447
new_handle.uri = gop.key_ptr.*;
13541448
gop.value_ptr.*.deinit();
13551449
gop.value_ptr.*.* = new_handle;
@@ -1724,30 +1818,53 @@ fn publishCimportDiagnostics(self: *DocumentStore, handle: *Handle) !void {
17241818
try self.diagnostics_collection.publishDiagnostics();
17251819
}
17261820

1821+
pub const UriFromImportStringResult = union(enum) {
1822+
none,
1823+
one: Uri,
1824+
many: []const Uri,
1825+
1826+
pub fn deinit(result: *UriFromImportStringResult, allocator: std.mem.Allocator) void {
1827+
switch (result.*) {
1828+
.none => {},
1829+
.one => |uri| uri.deinit(allocator),
1830+
.many => |uris| {
1831+
for (uris) |uri| uri.deinit(allocator);
1832+
allocator.free(uris);
1833+
},
1834+
}
1835+
}
1836+
};
1837+
17271838
/// takes the string inside a @import() node (without the quotation marks)
17281839
/// and returns it's uri
17291840
/// caller owns the returned memory
17301841
/// **Thread safe** takes a shared lock
1731-
pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, handle: *Handle, import_str: []const u8) error{OutOfMemory}!?Uri {
1842+
pub fn uriFromImportStr(
1843+
self: *DocumentStore,
1844+
allocator: std.mem.Allocator,
1845+
handle: *Handle,
1846+
import_str: []const u8,
1847+
) error{OutOfMemory}!UriFromImportStringResult {
17321848
const tracy_zone = tracy.trace(@src());
17331849
defer tracy_zone.end();
17341850

17351851
if (std.mem.endsWith(u8, import_str, ".zig") or std.mem.endsWith(u8, import_str, ".zon")) {
17361852
const base_path = handle.uri.toFsPath(allocator) catch |err| switch (err) {
17371853
error.OutOfMemory => return error.OutOfMemory,
1738-
error.UnsupportedScheme => return null,
1854+
error.UnsupportedScheme => return .none,
17391855
};
17401856
defer allocator.free(base_path);
1741-
return try resolveFileImportString(allocator, base_path, import_str);
1857+
const uri = try resolveFileImportString(allocator, base_path, import_str) orelse return .none;
1858+
return .{ .one = uri };
17421859
}
17431860

17441861
if (std.mem.eql(u8, import_str, "std")) {
1745-
const zig_lib_dir = self.config.zig_lib_dir orelse return null;
1862+
const zig_lib_dir = self.config.zig_lib_dir orelse return .none;
17461863

17471864
const std_path = try zig_lib_dir.join(allocator, &.{ "std", "std.zig" });
17481865
defer allocator.free(std_path);
17491866

1750-
return try .fromPath(allocator, std_path);
1867+
return .{ .one = try .fromPath(allocator, std_path) };
17511868
}
17521869

17531870
if (std.mem.eql(u8, import_str, "builtin")) {
@@ -1756,24 +1873,33 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand
17561873
.none, .unresolved => {},
17571874
.resolved => |resolved| {
17581875
if (resolved.build_file.builtin_uri) |builtin_uri| {
1759-
return try builtin_uri.dupe(allocator);
1876+
return .{ .one = try builtin_uri.dupe(allocator) };
17601877
}
17611878
},
17621879
}
17631880
}
17641881
if (self.config.builtin_path) |builtin_path| {
1765-
return try .fromPath(allocator, builtin_path);
1882+
return .{ .one = try .fromPath(allocator, builtin_path) };
17661883
}
1767-
return null;
1884+
return .none;
17681885
}
17691886

1770-
if (!supports_build_system) return null;
1887+
if (!supports_build_system) return .none;
17711888

17721889
if (std.mem.eql(u8, import_str, "root")) {
1773-
switch (try handle.getAssociatedBuildFile(self)) {
1774-
.none, .unresolved => return null,
1775-
.resolved => |result| return try .fromPath(allocator, result.root_source_file),
1890+
const root_source_files = switch (try handle.getAssociatedCompilationUnits(self)) {
1891+
.none, .unresolved => return .none,
1892+
.resolved => |root_source_files| root_source_files,
1893+
};
1894+
var uris: std.ArrayList(Uri) = try .initCapacity(allocator, root_source_files.len);
1895+
defer {
1896+
for (uris.items) |uri| uri.deinit(allocator);
1897+
uris.deinit(allocator);
17761898
}
1899+
for (root_source_files) |root_source_file| {
1900+
uris.appendAssumeCapacity(try .fromPath(allocator, root_source_file));
1901+
}
1902+
return .{ .many = try uris.toOwnedSlice(allocator) };
17771903
}
17781904

17791905
if (isBuildFile(handle.uri)) blk: {
@@ -1782,20 +1908,20 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand
17821908
defer build_file.unlockConfig();
17831909

17841910
if (build_config.dependencies.map.get(import_str)) |path| {
1785-
return try .fromPath(allocator, path);
1911+
return .{ .one = try .fromPath(allocator, path) };
17861912
}
1787-
return null;
1913+
return .none;
17881914
}
17891915

17901916
switch (try handle.getAssociatedBuildFile(self)) {
1791-
.none, .unresolved => return null,
1917+
.none, .unresolved => return .none,
17921918
.resolved => |resolved| {
1793-
const build_config = resolved.build_file.tryLockConfig() orelse return null;
1919+
const build_config = resolved.build_file.tryLockConfig() orelse return .none;
17941920
defer resolved.build_file.unlockConfig();
17951921

1796-
const module = build_config.modules.map.get(resolved.root_source_file) orelse return null;
1797-
const imported_root_source_file = module.import_table.map.get(import_str) orelse return null;
1798-
return try .fromPath(allocator, imported_root_source_file);
1922+
const module = build_config.modules.map.get(resolved.root_source_file) orelse return .none;
1923+
const imported_root_source_file = module.import_table.map.get(import_str) orelse return .none;
1924+
return .{ .one = try .fromPath(allocator, imported_root_source_file) };
17991925
},
18001926
}
18011927
}

src/analysis.zig

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,22 +2291,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) error
22912291
return null;
22922292
}
22932293

2294-
const import_uri = (try analyser.store.uriFromImportStr(
2295-
analyser.arena,
2296-
handle,
2297-
import_string,
2298-
)) orelse (try analyser.store.uriFromImportStr(
2299-
analyser.arena,
2300-
analyser.root_handle orelse return null,
2301-
import_string,
2302-
)) orelse return null;
2303-
2304-
const new_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null;
2305-
2306-
return .{
2307-
.data = .{ .container = .root(new_handle) },
2308-
.is_type_val = true,
2309-
};
2294+
if (try analyser.resolveImportString(handle, import_string)) |ty| return ty;
2295+
if (try analyser.resolveImportString(analyser.root_handle orelse return null, import_string)) |ty| return ty;
2296+
return null;
23102297
},
23112298
.c_import => {
23122299
if (!DocumentStore.supports_build_system) return null;
@@ -4431,6 +4418,34 @@ pub const ScopeWithHandle = struct {
44314418
}
44324419
};
44334420

4421+
pub fn resolveImportString(analyser: *Analyser, handle: *DocumentStore.Handle, import_string: []const u8) error{OutOfMemory}!?Type {
4422+
const result = try analyser.store.uriFromImportStr(analyser.arena, handle, import_string);
4423+
switch (result) {
4424+
.none => return null,
4425+
.one => |uri| {
4426+
const node_handle = analyser.store.getOrLoadHandle(uri) orelse return null;
4427+
return .{
4428+
.data = .{ .container = .root(node_handle) },
4429+
.is_type_val = true,
4430+
};
4431+
},
4432+
.many => |uris| {
4433+
var entries: std.ArrayList(Type.Data.EitherEntry) = try .initCapacity(analyser.arena, uris.len);
4434+
for (uris) |uri| {
4435+
const node_handle = analyser.store.getOrLoadHandle(uri) orelse continue;
4436+
entries.appendAssumeCapacity(.{
4437+
.type_data = .{ .container = .root(node_handle) },
4438+
.descriptor = "",
4439+
});
4440+
}
4441+
return .{
4442+
.data = .{ .either = entries.items },
4443+
.is_type_val = true,
4444+
};
4445+
},
4446+
}
4447+
}
4448+
44344449
/// Look up `type_name` in 'zig_lib_dir/std/builtin.zig' and return it as an instance
44354450
/// Useful for functionality related to builtin fns
44364451
pub fn instanceStdBuiltinType(analyser: *Analyser, type_name: []const u8) error{OutOfMemory}!?Type {
@@ -4705,12 +4720,7 @@ pub fn getFieldAccessType(
47054720
.start = import_str_tok.loc.start + 1,
47064721
.end = import_str_tok.loc.end - 1,
47074722
});
4708-
const import_uri = try analyser.store.uriFromImportStr(analyser.arena, handle, import_str) orelse return null;
4709-
const node_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null;
4710-
current_type = .{
4711-
.data = .{ .container = .root(node_handle) },
4712-
.is_type_val = true,
4713-
};
4723+
current_type = try analyser.resolveImportString(handle, import_str) orelse return null;
47144724
_ = tokenizer.next(); // eat the .r_paren
47154725
continue; // Outermost `while`
47164726
}

0 commit comments

Comments
 (0)