lang: reject dir-vs-file ambiguous #import

An extensionless import path that names a directory next to a same-named
.sx file ('modules/std' with both modules/std.sx and modules/std/ present)
no longer silently resolves to the directory — it errors and asks for the
explicit .sx spelling. Exemption: a file importing its own companion
directory (X.sx importing X/, the multi-file test layout) stays legal —
the sibling file is the importer itself, so the directory is the only
sensible target.
This commit is contained in:
agra
2026-06-11 08:37:36 +03:00
parent 12bf61a9fc
commit 698f75d79a
7 changed files with 50 additions and 1 deletions

View File

@@ -1074,7 +1074,32 @@ pub fn resolveImports(
try cache.put(resolved_path, result);
break :blk result;
} else |_| {
// File read failed — try as directory import
// File read failed — try as directory import. An extensionless
// path that names a directory next to a same-named `.sx` file
// is ambiguous: require the explicit `.sx` spelling for the
// file rather than silently picking the directory. Exception:
// when the sibling `.sx` is the importing file itself (a test
// importing its own companion directory), the directory is the
// only sensible target.
const sibling_sx = try std.fmt.allocPrint(allocator, "{s}.sx", .{resolved_path});
const sibling_exists = if (std.mem.eql(u8, sibling_sx, file_path))
false
else if (std.Io.Dir.readFileAlloc(.cwd(), io, sibling_sx, allocator, .limited(10 * 1024 * 1024))) |_|
true
else |_|
false;
if (sibling_exists) {
const is_dir = if (std.Io.Dir.openDir(.cwd(), io, resolved_path, .{})) |d| dir_blk: {
d.close(io);
break :dir_blk true;
} else |_| false;
if (is_dir) {
if (diagnostics) |diags| {
diags.addFmt(.err, decl.span, "ambiguous import '{s}': both a file '{s}.sx' and a directory '{s}' exist — write \"{s}.sx\" to import the file", .{ imp.path, imp.path, imp.path, imp.path });
}
return error.ImportError;
}
}
const result = resolveDirectoryImport(allocator, io, resolved_path, chain, cache, source_map, diagnostics, decl.span, stdlib_paths, import_graph, flat_import_graph, comptime_ctx) catch {
if (diagnostics) |diags| {
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});