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:
11
examples/1158-diagnostics-import-dir-file-ambiguous.sx
Normal file
11
examples/1158-diagnostics-import-dir-file-ambiguous.sx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// An extensionless #import that matches BOTH a `.sx` file and a sibling
|
||||||
|
// directory of the same name is ambiguous — the compiler refuses to pick
|
||||||
|
// silently and asks for the explicit `.sx` spelling. `modules/std` is the
|
||||||
|
// canonical collision: `modules/std.sx` (the prelude) sits next to
|
||||||
|
// `modules/std/` (mem/fs/process/...).
|
||||||
|
|
||||||
|
#import "modules/std";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: ambiguous import 'modules/std': both a file 'modules/std.sx' and a directory 'modules/std' exist — write "modules/std.sx" to import the file
|
||||||
|
--> examples/1158-diagnostics-import-dir-file-ambiguous.sx:7:1
|
||||||
|
|
|
||||||
|
7 | #import "modules/std";
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -398,6 +398,10 @@ Direct C header import:
|
|||||||
math :: #import "modules/math"; // namespaced import (directory: all .sx files merged)
|
math :: #import "modules/math"; // namespaced import (directory: all .sx files merged)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A path that matches both a file and a same-named sibling directory
|
||||||
|
(`modules/std.sx` next to `modules/std/`) is rejected as ambiguous — write the
|
||||||
|
`.sx` path to import the file.
|
||||||
|
|
||||||
When two flat-imported modules each define a function of the same name, every
|
When two flat-imported modules each define a function of the same name, every
|
||||||
module's own code binds its OWN author — a bare call resolves to the same-name
|
module's own code binds its OWN author — a bare call resolves to the same-name
|
||||||
function in the caller's module (or in its single flat import that provides it).
|
function in the caller's module (or in its single flat import that provides it).
|
||||||
|
|||||||
2
specs.md
2
specs.md
@@ -2495,6 +2495,8 @@ pkg :: #import "modules/math"; // namespaced — all .sx files merged under pk
|
|||||||
|
|
||||||
Directory imports scan only the top level of the specified directory (non-recursive). Files are processed in alphabetical order for deterministic builds. Files within the directory may `#import` each other or external files.
|
Directory imports scan only the top level of the specified directory (non-recursive). Files are processed in alphabetical order for deterministic builds. Files within the directory may `#import` each other or external files.
|
||||||
|
|
||||||
|
If an extensionless path matches both a file and a sibling directory of the same name (`modules/std.sx` next to `modules/std/`), the import is an error — write the `.sx` path to import the file. Exception: a file importing its own companion directory (`X.sx` importing `X/`) is not ambiguous; the directory is the only sensible target.
|
||||||
|
|
||||||
Namespaced declarations are accessed with dot notation:
|
Namespaced declarations are accessed with dot notation:
|
||||||
```sx
|
```sx
|
||||||
std.print("hello");
|
std.print("hello");
|
||||||
|
|||||||
@@ -1074,7 +1074,32 @@ pub fn resolveImports(
|
|||||||
try cache.put(resolved_path, result);
|
try cache.put(resolved_path, result);
|
||||||
break :blk result;
|
break :blk result;
|
||||||
} else |_| {
|
} 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 {
|
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| {
|
if (diagnostics) |diags| {
|
||||||
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});
|
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});
|
||||||
|
|||||||
Reference in New Issue
Block a user