mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge remote-tracking branch 'origin/main' into builtin-str2
This commit is contained in:
commit
3fa79da27c
16 changed files with 693 additions and 620 deletions
|
|
@ -139,6 +139,21 @@ const TypeVarProblem = struct {
|
|||
ast_anno: AST.TypeAnno.Idx,
|
||||
};
|
||||
|
||||
const ModuleFoundStatus = enum {
|
||||
module_was_found,
|
||||
module_not_found,
|
||||
};
|
||||
|
||||
const TypeBindingLocation = struct {
|
||||
scope_index: usize,
|
||||
binding: *Scope.TypeBinding,
|
||||
};
|
||||
|
||||
const TypeBindingLocationConst = struct {
|
||||
scope_index: usize,
|
||||
binding: *const Scope.TypeBinding,
|
||||
};
|
||||
|
||||
/// Deinitialize canonicalizer resources
|
||||
pub fn deinit(
|
||||
self: *Self,
|
||||
|
|
@ -237,17 +252,8 @@ pub fn init(
|
|||
// Use the same alias as the module name for auto-imports
|
||||
const alias = module_name_ident;
|
||||
|
||||
// Process the import using the shared helper function
|
||||
// This will:
|
||||
// - Get or create Import.Idx
|
||||
// - Introduce module alias to scope
|
||||
// - Process type imports
|
||||
// - Introduce exposed items (including auto-expose for type modules)
|
||||
// - Store import_indices mapping
|
||||
// - Create CIR import statement
|
||||
// - Add imported module to scope
|
||||
// - Check module exists
|
||||
_ = try result.processModuleImport(module_name_ident, alias, empty_exposed_span, auto_import_region);
|
||||
// Process the import using the aliased import path (auto-imports always have an alias)
|
||||
_ = try result.importWithAlias(module_name_ident, alias, empty_exposed_span, auto_import_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1389,7 +1395,7 @@ fn createExposedScope(
|
|||
|
||||
// Use a dummy statement index - we just need to track that it's exposed
|
||||
const dummy_idx = @as(Statement.Idx, @enumFromInt(0));
|
||||
try self.exposed_scope.put(gpa, .type_decl, ident_idx, dummy_idx);
|
||||
try self.exposed_scope.type_bindings.put(gpa, ident_idx, Scope.TypeBinding{ .local_nominal = dummy_idx });
|
||||
}
|
||||
|
||||
// Store by text in a temporary hash map, since indices may change
|
||||
|
|
@ -1422,7 +1428,7 @@ fn createExposedScope(
|
|||
|
||||
// Use a dummy statement index - we just need to track that it's exposed
|
||||
const dummy_idx = @as(Statement.Idx, @enumFromInt(0));
|
||||
try self.exposed_scope.put(gpa, .type_decl, ident_idx, dummy_idx);
|
||||
try self.exposed_scope.type_bindings.put(gpa, ident_idx, Scope.TypeBinding{ .local_nominal = dummy_idx });
|
||||
}
|
||||
|
||||
// Store by text in a temporary hash map, since indices may change
|
||||
|
|
@ -1597,7 +1603,75 @@ fn bringIngestedFileIntoScope(
|
|||
|
||||
/// Process a module import with common logic shared by explicit imports and auto-imports.
|
||||
/// This handles everything after module name and alias resolution.
|
||||
fn processModuleImport(
|
||||
/// Process import with an alias (normal import like `import json.Json` or `import json.Json as J`)
|
||||
fn importAliased(
|
||||
self: *Self,
|
||||
module_name: Ident.Idx,
|
||||
alias_tok: ?Token.Idx,
|
||||
exposed_items_span: CIR.ExposedItem.Span,
|
||||
import_region: Region,
|
||||
) std.mem.Allocator.Error!?Statement.Idx {
|
||||
const module_name_text = self.env.getIdent(module_name);
|
||||
|
||||
// 1. Get or create Import.Idx for this module
|
||||
const module_import_idx = try self.env.imports.getOrPut(
|
||||
self.env.gpa,
|
||||
self.env.common.getStringStore(),
|
||||
module_name_text,
|
||||
);
|
||||
|
||||
// 2. Resolve the alias
|
||||
const alias = try self.resolveModuleAlias(alias_tok, module_name) orelse return null;
|
||||
|
||||
// 3. Add to scope: alias -> module_name mapping
|
||||
try self.scopeIntroduceModuleAlias(alias, module_name);
|
||||
|
||||
// 4. Process type imports from this module
|
||||
try self.processTypeImports(module_name, alias);
|
||||
|
||||
// 5. Introduce exposed items into scope (includes auto-expose for type modules)
|
||||
try self.introduceItemsAliased(exposed_items_span, module_name, alias, import_region, module_import_idx);
|
||||
|
||||
// 6. Store the mapping from module name to Import.Idx
|
||||
try self.import_indices.put(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 7. Create CIR import statement
|
||||
const cir_import = Statement{
|
||||
.s_import = .{
|
||||
.module_name_tok = module_name,
|
||||
.qualifier_tok = null,
|
||||
.alias_tok = null,
|
||||
.exposes = exposed_items_span,
|
||||
},
|
||||
};
|
||||
|
||||
const import_idx = try self.env.addStatement(cir_import, import_region);
|
||||
try self.env.store.addScratchStatement(import_idx);
|
||||
|
||||
// 8. Add the module to the current scope so it can be used in qualified lookups
|
||||
const current_scope = self.currentScope();
|
||||
_ = try current_scope.introduceImportedModule(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 9. Check that this module actually exists, and if not report an error
|
||||
if (self.module_envs) |envs_map| {
|
||||
if (!envs_map.contains(module_name)) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
|
||||
return import_idx;
|
||||
}
|
||||
|
||||
/// Process import with an alias provided directly as an Ident.Idx (used for auto-imports)
|
||||
fn importWithAlias(
|
||||
self: *Self,
|
||||
module_name: Ident.Idx,
|
||||
alias: Ident.Idx,
|
||||
|
|
@ -1620,7 +1694,7 @@ fn processModuleImport(
|
|||
try self.processTypeImports(module_name, alias);
|
||||
|
||||
// 4. Introduce exposed items into scope (includes auto-expose for type modules)
|
||||
try self.introduceExposedItemsIntoScope(exposed_items_span, module_name, alias, import_region);
|
||||
try self.introduceItemsAliased(exposed_items_span, module_name, alias, import_region, module_import_idx);
|
||||
|
||||
// 5. Store the mapping from module name to Import.Idx
|
||||
try self.import_indices.put(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
|
@ -1644,9 +1718,64 @@ fn processModuleImport(
|
|||
|
||||
// 8. Check that this module actually exists, and if not report an error
|
||||
if (self.module_envs) |envs_map| {
|
||||
// Check if the module exists
|
||||
if (!envs_map.contains(module_name)) {
|
||||
// Module not found - create diagnostic
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
|
||||
return import_idx;
|
||||
}
|
||||
|
||||
/// Process auto-expose import without alias (like `import json.Parser.Config`)
|
||||
fn importUnaliased(
|
||||
self: *Self,
|
||||
module_name: Ident.Idx,
|
||||
exposed_items_span: CIR.ExposedItem.Span,
|
||||
import_region: Region,
|
||||
) std.mem.Allocator.Error!Statement.Idx {
|
||||
const module_name_text = self.env.getIdent(module_name);
|
||||
|
||||
// 1. Get or create Import.Idx for this module
|
||||
const module_import_idx = try self.env.imports.getOrPut(
|
||||
self.env.gpa,
|
||||
self.env.common.getStringStore(),
|
||||
module_name_text,
|
||||
);
|
||||
|
||||
// 2. Introduce exposed items into scope (no alias, no auto-expose of main type)
|
||||
try self.introduceItemsUnaliased(exposed_items_span, module_name, import_region, module_import_idx);
|
||||
|
||||
// 3. Store the mapping from module name to Import.Idx
|
||||
try self.import_indices.put(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 4. Create CIR import statement
|
||||
const cir_import = Statement{
|
||||
.s_import = .{
|
||||
.module_name_tok = module_name,
|
||||
.qualifier_tok = null,
|
||||
.alias_tok = null,
|
||||
.exposes = exposed_items_span,
|
||||
},
|
||||
};
|
||||
|
||||
const import_idx = try self.env.addStatement(cir_import, import_region);
|
||||
try self.env.store.addScratchStatement(import_idx);
|
||||
|
||||
// 5. Add the module to the current scope so it can be used in qualified lookups
|
||||
const current_scope = self.currentScope();
|
||||
_ = try current_scope.introduceImportedModule(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 6. Check that this module actually exists, and if not report an error
|
||||
if (self.module_envs) |envs_map| {
|
||||
if (!envs_map.contains(module_name)) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
|
|
@ -1725,17 +1854,17 @@ fn canonicalizeImportStatement(
|
|||
}
|
||||
};
|
||||
|
||||
// 2. Determine the alias (either explicit or default to last part)
|
||||
const alias = try self.resolveModuleAlias(import_stmt.alias_tok, module_name) orelse return null;
|
||||
|
||||
// 3. Convert exposed items to CIR
|
||||
// 2. Convert exposed items to CIR
|
||||
const scratch_start = self.env.store.scratchExposedItemTop();
|
||||
try self.convertASTExposesToCIR(import_stmt.exposes);
|
||||
const cir_exposes = try self.env.store.exposedItemSpanFrom(scratch_start);
|
||||
const import_region = self.parse_ir.tokenizedRegionToRegion(import_stmt.region);
|
||||
|
||||
// 4. Process the import using shared logic
|
||||
return try self.processModuleImport(module_name, alias, cir_exposes, import_region);
|
||||
// 3. Dispatch to the appropriate handler based on whether this is a nested import
|
||||
return if (import_stmt.nested_import)
|
||||
try self.importUnaliased(module_name, cir_exposes, import_region)
|
||||
else
|
||||
try self.importAliased(module_name, import_stmt.alias_tok, cir_exposes, import_region);
|
||||
}
|
||||
|
||||
/// Resolve the module alias name from either explicit alias or module name
|
||||
|
|
@ -1849,29 +1978,26 @@ fn convertASTExposesToCIR(
|
|||
}
|
||||
}
|
||||
|
||||
/// Introduce converted exposed items into scope for identifier resolution
|
||||
fn introduceExposedItemsIntoScope(
|
||||
/// Introduce converted exposed items into scope for aliased imports
|
||||
/// For imports like `import json.Parser exposing [Config]`, this will:
|
||||
/// 1. Auto-expose the module's main type if it's a type module
|
||||
/// 2. Process explicitly exposed items
|
||||
fn introduceItemsAliased(
|
||||
self: *Self,
|
||||
exposed_items_span: CIR.ExposedItem.Span,
|
||||
module_name: Ident.Idx,
|
||||
module_alias: Ident.Idx,
|
||||
import_region: Region,
|
||||
module_import_idx: CIR.Import.Idx,
|
||||
) std.mem.Allocator.Error!void {
|
||||
const exposed_items_slice = self.env.store.sliceExposedItems(exposed_items_span);
|
||||
const current_scope = self.currentScope();
|
||||
|
||||
// If we have module_envs, validate the imports
|
||||
if (self.module_envs) |envs_map| {
|
||||
// Check if the module exists
|
||||
if (!envs_map.contains(module_name)) {
|
||||
// Module not found - Module existence check is already done in canonicalizeImportStatement,
|
||||
// so there is no need to create another diagnostic here for module_not_found
|
||||
return;
|
||||
}
|
||||
const module_entry = envs_map.get(module_name) orelse return;
|
||||
const module_env = module_entry.env;
|
||||
|
||||
// Get the module's exposed_items
|
||||
const module_env = envs_map.get(module_name).?.env;
|
||||
|
||||
// For type modules, auto-introduce the main type with the alias name
|
||||
// Auto-expose the module's main type for type modules
|
||||
switch (module_env.module_kind) {
|
||||
.type_module => |main_type_ident| {
|
||||
if (module_env.containsExposedById(main_type_ident)) {
|
||||
|
|
@ -1880,6 +2006,18 @@ fn introduceExposedItemsIntoScope(
|
|||
.original_name = main_type_ident,
|
||||
};
|
||||
try self.scopeIntroduceExposedItem(module_alias, item_info);
|
||||
|
||||
const target_node_idx = module_env.getExposedNodeIndexById(main_type_ident);
|
||||
try self.setExternalTypeBinding(
|
||||
current_scope,
|
||||
module_alias,
|
||||
module_name,
|
||||
main_type_ident,
|
||||
target_node_idx,
|
||||
module_import_idx,
|
||||
import_region,
|
||||
.module_was_found,
|
||||
);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
|
|
@ -1942,6 +2080,129 @@ fn introduceExposedItemsIntoScope(
|
|||
}
|
||||
}
|
||||
|
||||
/// Introduce converted exposed items into scope for auto-expose imports
|
||||
/// For imports like `import json.Parser.Config`, this will:
|
||||
/// 1. Skip auto-exposing the module's main type (no alias exists)
|
||||
/// 2. Process only explicitly exposed items
|
||||
fn introduceItemsUnaliased(
|
||||
self: *Self,
|
||||
exposed_items_span: CIR.ExposedItem.Span,
|
||||
module_name: Ident.Idx,
|
||||
import_region: Region,
|
||||
module_import_idx: CIR.Import.Idx,
|
||||
) std.mem.Allocator.Error!void {
|
||||
const exposed_items_slice = self.env.store.sliceExposedItems(exposed_items_span);
|
||||
const current_scope = self.currentScope();
|
||||
|
||||
if (self.module_envs) |envs_map| {
|
||||
const module_entry = envs_map.get(module_name) orelse return;
|
||||
const module_env = module_entry.env;
|
||||
|
||||
// No auto-expose of main type - only process explicitly exposed items
|
||||
for (exposed_items_slice) |exposed_item_idx| {
|
||||
const exposed_item = self.env.store.getExposedItem(exposed_item_idx);
|
||||
const local_ident = exposed_item.alias orelse exposed_item.name;
|
||||
const local_name_text = self.env.getIdent(local_ident);
|
||||
|
||||
const target_ident = module_env.common.findIdent(self.env.getIdent(exposed_item.name));
|
||||
const is_type_name = local_name_text.len > 0 and local_name_text[0] >= 'A' and local_name_text[0] <= 'Z';
|
||||
|
||||
if (target_ident) |ident_in_module| {
|
||||
if (!module_env.containsExposedById(ident_in_module)) {
|
||||
if (is_type_name) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .type_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.type_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .value_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.value_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const target_node_idx = module_env.getExposedNodeIndexById(ident_in_module) orelse {
|
||||
if (is_type_name) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .type_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.type_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .value_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.value_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
continue;
|
||||
};
|
||||
|
||||
const item_info = Scope.ExposedItemInfo{
|
||||
.module_name = module_name,
|
||||
.original_name = exposed_item.name,
|
||||
};
|
||||
try self.scopeIntroduceExposedItem(local_ident, item_info);
|
||||
|
||||
if (is_type_name) {
|
||||
try self.setExternalTypeBinding(
|
||||
current_scope,
|
||||
local_ident,
|
||||
module_name,
|
||||
exposed_item.name,
|
||||
target_node_idx,
|
||||
module_import_idx,
|
||||
import_region,
|
||||
.module_was_found,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (local_name_text.len > 0 and local_name_text[0] >= 'A' and local_name_text[0] <= 'Z') {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .type_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.type_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .value_not_exposed = .{
|
||||
.module_name = module_name,
|
||||
.value_name = exposed_item.name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (exposed_items_slice) |exposed_item_idx| {
|
||||
const exposed_item = self.env.store.getExposedItem(exposed_item_idx);
|
||||
const local_ident = exposed_item.alias orelse exposed_item.name;
|
||||
const local_name_text = self.env.getIdent(local_ident);
|
||||
const item_info = Scope.ExposedItemInfo{
|
||||
.module_name = module_name,
|
||||
.original_name = exposed_item.name,
|
||||
};
|
||||
try self.scopeIntroduceExposedItem(local_ident, item_info);
|
||||
|
||||
if (local_name_text.len > 0 and local_name_text[0] >= 'A' and local_name_text[0] <= 'Z') {
|
||||
try self.setExternalTypeBinding(
|
||||
current_scope,
|
||||
local_ident,
|
||||
module_name,
|
||||
exposed_item.name,
|
||||
null,
|
||||
module_import_idx,
|
||||
import_region,
|
||||
.module_not_found,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonicalize a decl with an annotation
|
||||
fn canonicalizeDeclWithAnnotation(
|
||||
self: *Self,
|
||||
|
|
@ -5398,12 +5659,56 @@ fn canonicalizeTypeAnnoBasicType(
|
|||
.base = .{ .builtin = builtin_type },
|
||||
} }, region);
|
||||
} else {
|
||||
// If it's not a builtin, look up in scope
|
||||
if (self.scopeLookupTypeDecl(type_name_ident)) |type_decl_idx| {
|
||||
return try self.env.addTypeAnno(CIR.TypeAnno{ .lookup = .{
|
||||
.name = type_name_ident,
|
||||
.base = .{ .local = .{ .decl_idx = type_decl_idx } },
|
||||
} }, region);
|
||||
// If it's not a builtin, look up in scope using unified type bindings
|
||||
if (self.scopeLookupTypeBinding(type_name_ident)) |binding_location| {
|
||||
const binding = binding_location.binding.*;
|
||||
return switch (binding) {
|
||||
.local_nominal => |stmt| try self.env.addTypeAnno(CIR.TypeAnno{ .lookup = .{
|
||||
.name = type_name_ident,
|
||||
.base = .{ .local = .{ .decl_idx = stmt } },
|
||||
} }, region),
|
||||
.local_alias => |stmt| try self.env.addTypeAnno(CIR.TypeAnno{ .lookup = .{
|
||||
.name = type_name_ident,
|
||||
.base = .{ .local = .{ .decl_idx = stmt } },
|
||||
} }, region),
|
||||
.associated_nominal => |stmt| try self.env.addTypeAnno(CIR.TypeAnno{ .lookup = .{
|
||||
.name = type_name_ident,
|
||||
.base = .{ .local = .{ .decl_idx = stmt } },
|
||||
} }, region),
|
||||
.external_nominal => |external| blk: {
|
||||
const import_idx = external.import_idx orelse {
|
||||
break :blk try self.env.pushMalformed(TypeAnno.Idx, Diagnostic{ .module_not_imported = .{
|
||||
.module_name = external.module_ident,
|
||||
.region = type_name_region,
|
||||
} });
|
||||
};
|
||||
|
||||
const target_node_idx = external.target_node_idx orelse {
|
||||
// Check if the module was not found
|
||||
if (external.module_not_found) {
|
||||
break :blk try self.env.pushMalformed(TypeAnno.Idx, Diagnostic{ .type_from_missing_module = .{
|
||||
.module_name = external.module_ident,
|
||||
.type_name = type_name_ident,
|
||||
.region = type_name_region,
|
||||
} });
|
||||
} else {
|
||||
break :blk try self.env.pushMalformed(TypeAnno.Idx, Diagnostic{ .type_not_exposed = .{
|
||||
.module_name = external.module_ident,
|
||||
.type_name = type_name_ident,
|
||||
.region = type_name_region,
|
||||
} });
|
||||
}
|
||||
};
|
||||
|
||||
break :blk try self.env.addTypeAnno(CIR.TypeAnno{ .lookup = .{
|
||||
.name = type_name_ident,
|
||||
.base = .{ .external = .{
|
||||
.module_idx = import_idx,
|
||||
.target_node_idx = target_node_idx,
|
||||
} },
|
||||
} }, region);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Check if this is an auto-imported type from module_envs
|
||||
|
|
@ -7163,12 +7468,14 @@ fn scopeIntroduceTypeDecl(
|
|||
while (i > 0) {
|
||||
i -= 1;
|
||||
const scope = &self.scopes.items[i];
|
||||
switch (scope.lookupTypeDecl(name_ident)) {
|
||||
.found => |type_decl_idx| {
|
||||
shadowed_in_parent = type_decl_idx;
|
||||
break;
|
||||
},
|
||||
.not_found => continue,
|
||||
if (scope.type_bindings.get(name_ident)) |binding| {
|
||||
shadowed_in_parent = switch (binding) {
|
||||
.local_nominal => |stmt| stmt,
|
||||
.local_alias => |stmt| stmt,
|
||||
.associated_nominal => |stmt| stmt,
|
||||
.external_nominal => null,
|
||||
};
|
||||
if (shadowed_in_parent) |_| break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7274,15 +7581,40 @@ fn scopeLookupTypeDecl(self: *Self, ident_idx: Ident.Idx) ?Statement.Idx {
|
|||
i -= 1;
|
||||
const scope = &self.scopes.items[i];
|
||||
|
||||
// Check for type aliases (unqualified names in associated blocks)
|
||||
if (scope.lookupTypeAlias(ident_idx)) |aliased_decl| {
|
||||
return aliased_decl;
|
||||
// Check unified type bindings
|
||||
if (scope.type_bindings.get(ident_idx)) |binding| {
|
||||
return switch (binding) {
|
||||
.local_nominal => |stmt| stmt,
|
||||
.local_alias => |stmt| stmt,
|
||||
.associated_nominal => |stmt| stmt,
|
||||
.external_nominal => null, // External types don't have local Statement.Idx
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check regular type declarations
|
||||
switch (scope.lookupTypeDecl(ident_idx)) {
|
||||
.found => |type_decl_idx| return type_decl_idx,
|
||||
.not_found => continue,
|
||||
return null;
|
||||
}
|
||||
|
||||
fn scopeLookupTypeBinding(self: *Self, ident_idx: Ident.Idx) ?TypeBindingLocation {
|
||||
var i = self.scopes.items.len;
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
const scope = &self.scopes.items[i];
|
||||
if (scope.type_bindings.getPtr(ident_idx)) |binding_ptr| {
|
||||
return TypeBindingLocation{ .scope_index = i, .binding = binding_ptr };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn scopeLookupTypeBindingConst(self: *const Self, ident_idx: Ident.Idx) ?TypeBindingLocationConst {
|
||||
var i = self.scopes.items.len;
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
const scope = &self.scopes.items[i];
|
||||
if (scope.type_bindings.getPtr(ident_idx)) |binding_ptr| {
|
||||
return TypeBindingLocationConst{ .scope_index = i, .binding = binding_ptr };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7428,6 +7760,30 @@ pub fn scopeIntroduceExposedItem(self: *Self, item_name: Ident.Idx, item_info: S
|
|||
}
|
||||
}
|
||||
|
||||
/// Set an external type binding for an imported nominal type
|
||||
fn setExternalTypeBinding(
|
||||
self: *Self,
|
||||
scope: *Scope,
|
||||
local_ident: Ident.Idx,
|
||||
module_ident: Ident.Idx,
|
||||
original_ident: Ident.Idx,
|
||||
target_node_idx: ?u16,
|
||||
module_import_idx: CIR.Import.Idx,
|
||||
origin_region: Region,
|
||||
module_found_status: ModuleFoundStatus,
|
||||
) !void {
|
||||
try scope.type_bindings.put(self.env.gpa, local_ident, Scope.TypeBinding{
|
||||
.external_nominal = .{
|
||||
.module_ident = module_ident,
|
||||
.original_ident = original_ident,
|
||||
.target_node_idx = target_node_idx,
|
||||
.import_idx = module_import_idx,
|
||||
.origin_region = origin_region,
|
||||
.module_not_found = module_found_status == .module_not_found,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Look up an exposed item in parent scopes (for shadowing detection)
|
||||
fn scopeLookupExposedItemInParentScopes(self: *const Self, item_name: Ident.Idx) ?Scope.ExposedItemInfo {
|
||||
// Search from second-innermost to outermost scope (excluding current scope)
|
||||
|
|
|
|||
|
|
@ -118,6 +118,11 @@ pub const Diagnostic = union(enum) {
|
|||
type_name: Ident.Idx,
|
||||
region: Region,
|
||||
},
|
||||
type_from_missing_module: struct {
|
||||
module_name: Ident.Idx,
|
||||
type_name: Ident.Idx,
|
||||
region: Region,
|
||||
},
|
||||
module_not_imported: struct {
|
||||
module_name: Ident.Idx,
|
||||
region: Region,
|
||||
|
|
@ -269,6 +274,7 @@ pub const Diagnostic = union(enum) {
|
|||
.module_not_found => |d| d.region,
|
||||
.value_not_exposed => |d| d.region,
|
||||
.type_not_exposed => |d| d.region,
|
||||
.type_from_missing_module => |d| d.region,
|
||||
.module_not_imported => |d| d.region,
|
||||
.too_many_exports => |d| d.region,
|
||||
.undeclared_type => |d| d.region,
|
||||
|
|
@ -889,9 +895,19 @@ pub const Diagnostic = union(enum) {
|
|||
) !Report {
|
||||
var report = Report.init(allocator, "UNDECLARED TYPE", .runtime_error);
|
||||
const owned_type_name = try report.addOwnedString(type_name);
|
||||
try report.document.addReflowingText("The type ");
|
||||
try report.document.addType(owned_type_name);
|
||||
try report.document.addReflowingText(" is not declared in this scope.");
|
||||
|
||||
// Check if this looks like a qualified type (contains dots)
|
||||
const has_dots = std.mem.indexOfScalar(u8, type_name, '.') != null;
|
||||
|
||||
if (has_dots) {
|
||||
try report.document.addReflowingText("Cannot resolve qualified type ");
|
||||
try report.document.addType(owned_type_name);
|
||||
try report.document.addReflowingText(".");
|
||||
} else {
|
||||
try report.document.addReflowingText("The type ");
|
||||
try report.document.addType(owned_type_name);
|
||||
try report.document.addReflowingText(" is not declared in this scope.");
|
||||
}
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addLineBreak();
|
||||
|
||||
|
|
@ -1411,13 +1427,32 @@ pub const Diagnostic = union(enum) {
|
|||
|
||||
const owned_module = try report.addOwnedString(module_name);
|
||||
const owned_type = try report.addOwnedString(type_name);
|
||||
try report.document.addReflowingText("The ");
|
||||
try report.document.addModuleName(owned_module);
|
||||
try report.document.addReflowingText(" module does not expose anything named ");
|
||||
try report.document.addType(owned_type);
|
||||
try report.document.addReflowingText(".");
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addReflowingText("Make sure the module exports this type, or use a type that is exposed.");
|
||||
|
||||
// Check if trying to access a type with the same name as the module (e.g., Result.Result)
|
||||
const is_same_name = std.mem.eql(u8, module_name, type_name);
|
||||
|
||||
if (is_same_name) {
|
||||
// Special message for Result.Result, Color.Color, etc.
|
||||
const qualified_name = try std.fmt.allocPrint(allocator, "{s}.{s}", .{ module_name, type_name });
|
||||
defer allocator.free(qualified_name);
|
||||
const owned_qualified = try report.addOwnedString(qualified_name);
|
||||
|
||||
try report.document.addReflowingText("There is no ");
|
||||
try report.document.addType(owned_qualified);
|
||||
try report.document.addReflowingText(" type.");
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addLineBreak();
|
||||
} else {
|
||||
// Standard message for other cases (e.g., Color.RGB where Color is a nominal type)
|
||||
const qualified_name = try std.fmt.allocPrint(allocator, "{s}.{s}", .{ module_name, type_name });
|
||||
defer allocator.free(qualified_name);
|
||||
const owned_qualified = try report.addOwnedString(qualified_name);
|
||||
|
||||
try report.document.addType(owned_qualified);
|
||||
try report.document.addReflowingText(" does not exist.");
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addLineBreak();
|
||||
}
|
||||
|
||||
const owned_filename = try report.addOwnedString(filename);
|
||||
try report.document.addSourceRegion(
|
||||
|
|
@ -1428,6 +1463,21 @@ pub const Diagnostic = union(enum) {
|
|||
line_starts,
|
||||
);
|
||||
|
||||
// Add tip at the end
|
||||
try report.document.addLineBreak();
|
||||
if (is_same_name) {
|
||||
try report.document.addReflowingText("There is a ");
|
||||
try report.document.addModuleName(owned_module);
|
||||
try report.document.addReflowingText(" module, but it does not have a ");
|
||||
try report.document.addType(owned_type);
|
||||
try report.document.addReflowingText(" type nested inside it.");
|
||||
} else {
|
||||
try report.document.addType(owned_module);
|
||||
try report.document.addReflowingText(" is a valid type, but it does not have an associated ");
|
||||
try report.document.addType(owned_type);
|
||||
try report.document.addReflowingText(".");
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ pub const Tag = enum {
|
|||
diag_module_not_found,
|
||||
diag_value_not_exposed,
|
||||
diag_type_not_exposed,
|
||||
diag_type_from_missing_module,
|
||||
diag_module_not_imported,
|
||||
diag_too_many_exports,
|
||||
diag_nominal_type_redeclared,
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ pub fn deinit(store: *NodeStore) void {
|
|||
/// when adding/removing variants from ModuleEnv unions. Update these when modifying the unions.
|
||||
///
|
||||
/// Count of the diagnostic nodes in the ModuleEnv
|
||||
pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 56;
|
||||
pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 57;
|
||||
/// Count of the expression nodes in the ModuleEnv
|
||||
pub const MODULEENV_EXPR_NODE_COUNT = 33;
|
||||
/// Count of the statement nodes in the ModuleEnv
|
||||
|
|
@ -2831,6 +2831,12 @@ pub fn addDiagnostic(store: *NodeStore, reason: CIR.Diagnostic) Allocator.Error!
|
|||
node.data_1 = @as(u32, @bitCast(r.module_name));
|
||||
node.data_2 = @as(u32, @bitCast(r.type_name));
|
||||
},
|
||||
.type_from_missing_module => |r| {
|
||||
node.tag = .diag_type_from_missing_module;
|
||||
region = r.region;
|
||||
node.data_1 = @as(u32, @bitCast(r.module_name));
|
||||
node.data_2 = @as(u32, @bitCast(r.type_name));
|
||||
},
|
||||
.module_not_imported => |r| {
|
||||
node.tag = .diag_module_not_imported;
|
||||
region = r.region;
|
||||
|
|
@ -3083,6 +3089,11 @@ pub fn getDiagnostic(store: *const NodeStore, diagnostic: CIR.Diagnostic.Idx) CI
|
|||
.type_name = @as(base.Ident.Idx, @bitCast(node.data_2)),
|
||||
.region = store.getRegionAt(node_idx),
|
||||
} },
|
||||
.diag_type_from_missing_module => return CIR.Diagnostic{ .type_from_missing_module = .{
|
||||
.module_name = @as(base.Ident.Idx, @bitCast(node.data_1)),
|
||||
.type_name = @as(base.Ident.Idx, @bitCast(node.data_2)),
|
||||
.region = store.getRegionAt(node_idx),
|
||||
} },
|
||||
.diag_module_not_imported => return CIR.Diagnostic{ .module_not_imported = .{
|
||||
.module_name = @as(base.Ident.Idx, @bitCast(node.data_1)),
|
||||
.region = store.getRegionAt(node_idx),
|
||||
|
|
|
|||
|
|
@ -7,17 +7,37 @@ const collections = @import("collections");
|
|||
const CIR = @import("CIR.zig");
|
||||
|
||||
const Ident = base.Ident;
|
||||
const Region = base.Region;
|
||||
|
||||
const Scope = @This();
|
||||
|
||||
/// Represents a type binding for a type imported from an external module.
|
||||
/// Contains all necessary information to resolve the type from the imported module.
|
||||
pub const ExternalTypeBinding = struct {
|
||||
module_ident: Ident.Idx,
|
||||
original_ident: Ident.Idx,
|
||||
target_node_idx: ?u16,
|
||||
import_idx: ?CIR.Import.Idx,
|
||||
origin_region: Region,
|
||||
/// True if the module was attempted to be imported but was not found.
|
||||
/// This allows us to emit a more specific diagnostic when the type is used.
|
||||
module_not_found: bool,
|
||||
};
|
||||
|
||||
/// A unified type binding that can represent either a locally declared type or an externally imported type.
|
||||
/// This is the single source of truth for all type resolution in a scope.
|
||||
pub const TypeBinding = union(enum) {
|
||||
local_nominal: CIR.Statement.Idx,
|
||||
local_alias: CIR.Statement.Idx,
|
||||
associated_nominal: CIR.Statement.Idx,
|
||||
external_nominal: ExternalTypeBinding,
|
||||
};
|
||||
|
||||
/// Maps an Ident to a Pattern in the Can IR
|
||||
idents: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx),
|
||||
aliases: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx),
|
||||
/// Maps type names to their type declaration statements
|
||||
type_decls: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx),
|
||||
/// Maps unqualified type names to their fully qualified Statement.Idx (for associated types)
|
||||
/// Example: within Foo's associated block, "Bar" -> statement for "Foo.Bar"
|
||||
type_aliases: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx),
|
||||
/// Canonical bindings for type names (local, auto-imported, and imported types)
|
||||
type_bindings: std.AutoHashMapUnmanaged(Ident.Idx, TypeBinding),
|
||||
/// Maps type variables to their type annotation indices
|
||||
type_vars: std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx),
|
||||
/// Maps module alias names to their full module names
|
||||
|
|
@ -33,8 +53,7 @@ pub fn init(is_function_boundary: bool) Scope {
|
|||
return Scope{
|
||||
.idents = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx){},
|
||||
.aliases = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx){},
|
||||
.type_decls = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx){},
|
||||
.type_aliases = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx){},
|
||||
.type_bindings = std.AutoHashMapUnmanaged(Ident.Idx, TypeBinding){},
|
||||
.type_vars = std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx){},
|
||||
.module_aliases = std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx){},
|
||||
.exposed_items = std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo){},
|
||||
|
|
@ -47,8 +66,7 @@ pub fn init(is_function_boundary: bool) Scope {
|
|||
pub fn deinit(self: *Scope, gpa: std.mem.Allocator) void {
|
||||
self.idents.deinit(gpa);
|
||||
self.aliases.deinit(gpa);
|
||||
self.type_decls.deinit(gpa);
|
||||
self.type_aliases.deinit(gpa);
|
||||
self.type_bindings.deinit(gpa);
|
||||
self.type_vars.deinit(gpa);
|
||||
self.module_aliases.deinit(gpa);
|
||||
self.exposed_items.deinit(gpa);
|
||||
|
|
@ -157,12 +175,11 @@ pub const ImportedModuleIntroduceResult = union(enum) {
|
|||
};
|
||||
|
||||
/// Item kinds in a scope
|
||||
pub const ItemKind = enum { ident, alias, type_decl, type_var, module_alias, exposed_item };
|
||||
pub const ItemKind = enum { ident, alias, type_var, module_alias, exposed_item };
|
||||
|
||||
/// Get the appropriate map for the given item kind
|
||||
pub fn items(scope: *Scope, comptime item_kind: ItemKind) switch (item_kind) {
|
||||
.ident, .alias => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx),
|
||||
.type_decl => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx),
|
||||
.type_var => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx),
|
||||
.module_alias => *std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx),
|
||||
.exposed_item => *std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo),
|
||||
|
|
@ -170,7 +187,6 @@ pub fn items(scope: *Scope, comptime item_kind: ItemKind) switch (item_kind) {
|
|||
return switch (item_kind) {
|
||||
.ident => &scope.idents,
|
||||
.alias => &scope.aliases,
|
||||
.type_decl => &scope.type_decls,
|
||||
.type_var => &scope.type_vars,
|
||||
.module_alias => &scope.module_aliases,
|
||||
.exposed_item => &scope.exposed_items,
|
||||
|
|
@ -180,7 +196,6 @@ pub fn items(scope: *Scope, comptime item_kind: ItemKind) switch (item_kind) {
|
|||
/// Get the appropriate map for the given item kind (const version)
|
||||
pub fn itemsConst(scope: *const Scope, comptime item_kind: ItemKind) switch (item_kind) {
|
||||
.ident, .alias => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx),
|
||||
.type_decl => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx),
|
||||
.type_var => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx),
|
||||
.module_alias => *const std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx),
|
||||
.exposed_item => *const std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo),
|
||||
|
|
@ -188,7 +203,6 @@ pub fn itemsConst(scope: *const Scope, comptime item_kind: ItemKind) switch (ite
|
|||
return switch (item_kind) {
|
||||
.ident => &scope.idents,
|
||||
.alias => &scope.aliases,
|
||||
.type_decl => &scope.type_decls,
|
||||
.type_var => &scope.type_vars,
|
||||
.module_alias => &scope.module_aliases,
|
||||
.exposed_item => &scope.exposed_items,
|
||||
|
|
@ -198,7 +212,6 @@ pub fn itemsConst(scope: *const Scope, comptime item_kind: ItemKind) switch (ite
|
|||
/// Put an item in the scope, panics on OOM
|
||||
pub fn put(scope: *Scope, gpa: std.mem.Allocator, comptime item_kind: ItemKind, name: Ident.Idx, value: switch (item_kind) {
|
||||
.ident, .alias => CIR.Pattern.Idx,
|
||||
.type_decl => CIR.Statement.Idx,
|
||||
.type_var => CIR.TypeAnno.Idx,
|
||||
.module_alias => Ident.Idx,
|
||||
.exposed_item => ExposedItemInfo,
|
||||
|
|
@ -214,13 +227,14 @@ pub fn introduceTypeDecl(
|
|||
type_decl: CIR.Statement.Idx,
|
||||
parent_lookup_fn: ?fn (Ident.Idx) ?CIR.Statement.Idx,
|
||||
) std.mem.Allocator.Error!TypeIntroduceResult {
|
||||
// Check if already exists in current scope by comparing text content
|
||||
var iter = scope.type_decls.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (name.idx == entry.key_ptr.idx) {
|
||||
// Type redeclaration is an error, not just a warning
|
||||
return TypeIntroduceResult{ .redeclared_error = entry.value_ptr.* };
|
||||
}
|
||||
// Check if type already exists in this scope
|
||||
if (scope.type_bindings.getPtr(name)) |existing| {
|
||||
return switch (existing.*) {
|
||||
.local_nominal => |stmt| TypeIntroduceResult{ .redeclared_error = stmt },
|
||||
.local_alias => |stmt| TypeIntroduceResult{ .type_alias_redeclared = stmt },
|
||||
.associated_nominal => |stmt| TypeIntroduceResult{ .nominal_type_redeclared = stmt },
|
||||
.external_nominal => TypeIntroduceResult{ .nominal_type_redeclared = type_decl },
|
||||
};
|
||||
}
|
||||
|
||||
// Check for shadowing in parent scopes and issue warnings
|
||||
|
|
@ -229,7 +243,8 @@ pub fn introduceTypeDecl(
|
|||
shadowed_stmt = lookup_fn(name);
|
||||
}
|
||||
|
||||
try scope.put(gpa, .type_decl, name, type_decl);
|
||||
// Add type binding (single source of truth)
|
||||
try scope.type_bindings.put(gpa, name, TypeBinding{ .local_nominal = type_decl });
|
||||
|
||||
if (shadowed_stmt) |stmt| {
|
||||
return TypeIntroduceResult{ .shadowing_warning = stmt };
|
||||
|
|
@ -238,26 +253,6 @@ pub fn introduceTypeDecl(
|
|||
return TypeIntroduceResult{ .success = {} };
|
||||
}
|
||||
|
||||
/// Lookup a type declaration in the scope hierarchy
|
||||
/// TODO: Optimize lookup performance - currently O(n) due to text comparison
|
||||
/// TODO: Consider caching or using a more efficient data structure for type lookup
|
||||
/// TODO: Support for nominal vs structural type distinction (future := operator)
|
||||
pub fn lookupTypeDecl(scope: *const Scope, name: Ident.Idx) TypeLookupResult {
|
||||
// Search by comparing text content, not identifier index
|
||||
var iter = scope.type_decls.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (name.idx == entry.key_ptr.idx) {
|
||||
return TypeLookupResult{ .found = entry.value_ptr.* };
|
||||
}
|
||||
}
|
||||
return TypeLookupResult{ .not_found = {} };
|
||||
}
|
||||
|
||||
/// Look up an unqualified type alias (for associated types)
|
||||
pub fn lookupTypeAlias(scope: *const Scope, name: Ident.Idx) ?CIR.Statement.Idx {
|
||||
return scope.type_aliases.get(name);
|
||||
}
|
||||
|
||||
/// Introduce an unqualified type alias (for associated types)
|
||||
/// Maps an unqualified name to a fully qualified type declaration
|
||||
pub fn introduceTypeAlias(
|
||||
|
|
@ -266,7 +261,9 @@ pub fn introduceTypeAlias(
|
|||
unqualified_name: Ident.Idx,
|
||||
qualified_type_decl: CIR.Statement.Idx,
|
||||
) !void {
|
||||
try scope.type_aliases.put(gpa, unqualified_name, qualified_type_decl);
|
||||
try scope.type_bindings.put(gpa, unqualified_name, TypeBinding{
|
||||
.associated_nominal = qualified_type_decl,
|
||||
});
|
||||
}
|
||||
|
||||
/// Update an existing type declaration in the scope
|
||||
|
|
@ -278,17 +275,17 @@ pub fn updateTypeDecl(
|
|||
name: Ident.Idx,
|
||||
new_type_decl: CIR.Statement.Idx,
|
||||
) std.mem.Allocator.Error!void {
|
||||
// Find the existing entry by comparing text content
|
||||
var iter = scope.type_decls.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (name.idx == entry.key_ptr.idx) {
|
||||
// Update the existing entry with the new statement index
|
||||
entry.value_ptr.* = new_type_decl;
|
||||
return;
|
||||
}
|
||||
if (scope.type_bindings.getPtr(name)) |binding_ptr| {
|
||||
const current = binding_ptr.*;
|
||||
binding_ptr.* = switch (current) {
|
||||
.local_nominal => TypeBinding{ .local_nominal = new_type_decl },
|
||||
.local_alias => TypeBinding{ .local_alias = new_type_decl },
|
||||
.associated_nominal => TypeBinding{ .associated_nominal = new_type_decl },
|
||||
.external_nominal => current,
|
||||
};
|
||||
} else {
|
||||
try scope.type_bindings.put(gpa, name, TypeBinding{ .local_nominal = new_type_decl });
|
||||
}
|
||||
// If not found, add it as a new entry
|
||||
try scope.put(gpa, .type_decl, name, new_type_decl);
|
||||
}
|
||||
|
||||
/// Introduce a type variable into the scope
|
||||
|
|
|
|||
|
|
@ -790,6 +790,14 @@ test "NodeStore round trip - Diagnostics" {
|
|||
},
|
||||
});
|
||||
|
||||
try diagnostics.append(gpa, CIR.Diagnostic{
|
||||
.type_from_missing_module = .{
|
||||
.module_name = rand_ident_idx(),
|
||||
.type_name = rand_ident_idx(),
|
||||
.region = rand_region(),
|
||||
},
|
||||
});
|
||||
|
||||
// Test the round-trip for all diagnostics
|
||||
for (diagnostics.items) |diagnostic| {
|
||||
const idx = try store.addDiagnostic(diagnostic);
|
||||
|
|
|
|||
|
|
@ -822,6 +822,9 @@ pub const Statement = union(enum) {
|
|||
qualifier_tok: ?Token.Idx,
|
||||
alias_tok: ?Token.Idx,
|
||||
exposes: ExposedItem.Span,
|
||||
/// True when importing like `import json.Parser.Config` where Config is auto-exposed
|
||||
/// but Parser should not become an alias (unlike `import json.Parser exposing [Config]`)
|
||||
nested_import: bool,
|
||||
region: TokenizedRegion,
|
||||
},
|
||||
type_decl: struct {
|
||||
|
|
|
|||
|
|
@ -1172,6 +1172,7 @@ pub fn getStatement(store: *const NodeStore, statement_idx: AST.Statement.Idx) A
|
|||
.start = exposes_start,
|
||||
.len = exposes_len,
|
||||
} },
|
||||
.nested_import = false,
|
||||
.region = node.region,
|
||||
} };
|
||||
},
|
||||
|
|
|
|||
|
|
@ -977,48 +977,83 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) Error!AST.Statem
|
|||
}
|
||||
if ((qualifier == null and self.peek() == .UpperIdent) or (qualifier != null and (self.peek() == .NoSpaceDotUpperIdent or self.peek() == .DotUpperIdent))) {
|
||||
var exposes = AST.ExposedItem.Span{ .span = base.DataSpan.empty() };
|
||||
const module_name_tok = self.pos;
|
||||
// Handle 'as' clause if present
|
||||
if (self.peekNext() == .KwAs) {
|
||||
self.advance(); // Advance past UpperIdent
|
||||
self.advance(); // Advance past KwAs
|
||||
alias_tok = self.pos;
|
||||
self.expect(.UpperIdent) catch {
|
||||
const malformed = try self.pushMalformed(AST.Statement.Idx, .expected_upper_name_after_import_as, start);
|
||||
return malformed;
|
||||
};
|
||||
} else {
|
||||
self.advance(); // Advance past identifier
|
||||
var nested_import = false;
|
||||
|
||||
// Parse all uppercase segments: first.Second.Third...
|
||||
var prev_upper_tok: ?TokenIdx = null;
|
||||
var module_name_tok = self.pos;
|
||||
self.advance(); // Advance past first UpperIdent
|
||||
|
||||
// Keep consuming additional .UpperIdent segments
|
||||
while (self.peek() == .NoSpaceDotUpperIdent or self.peek() == .DotUpperIdent) {
|
||||
prev_upper_tok = module_name_tok;
|
||||
module_name_tok = self.pos;
|
||||
self.advance();
|
||||
}
|
||||
|
||||
// Handle 'exposing' clause if present (can occur with or without 'as')
|
||||
if (self.peek() == .KwExposing) {
|
||||
self.advance(); // Advance past KwExposing
|
||||
self.expect(.OpenSquare) catch {
|
||||
return try self.pushMalformed(AST.Statement.Idx, .import_exposing_no_open, start);
|
||||
};
|
||||
// If we have multiple uppercase segments and no explicit 'as' or 'exposing',
|
||||
// auto-expose the final segment
|
||||
const has_explicit_clause = self.peek() == .KwAs or self.peek() == .KwExposing;
|
||||
if (prev_upper_tok != null and !has_explicit_clause) {
|
||||
// Auto-expose pattern: import json.Parser.Config
|
||||
// Module is everything before the last segment, last segment is auto-exposed
|
||||
const final_segment_tok = module_name_tok;
|
||||
module_name_tok = prev_upper_tok.?;
|
||||
nested_import = true;
|
||||
|
||||
// Create exposed item for the final segment
|
||||
const scratch_top = self.store.scratchExposedItemTop();
|
||||
self.parseCollectionSpan(AST.ExposedItem.Idx, .CloseSquare, NodeStore.addScratchExposedItem, Parser.parseExposedItem) catch |err| {
|
||||
switch (err) {
|
||||
error.ExpectedNotFound => {
|
||||
while (self.peek() != .CloseSquare and self.peek() != .EndOfFile) {
|
||||
self.advance();
|
||||
}
|
||||
self.expect(.CloseSquare) catch {};
|
||||
self.store.clearScratchExposedItemsFrom(scratch_top);
|
||||
return try self.pushMalformed(AST.Statement.Idx, .import_exposing_no_close, start);
|
||||
},
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.TooNested => return error.TooNested,
|
||||
}
|
||||
};
|
||||
const exposed_item = try self.store.addExposedItem(.{ .upper_ident = .{
|
||||
.region = .{ .start = final_segment_tok, .end = final_segment_tok },
|
||||
.ident = final_segment_tok,
|
||||
.as = null,
|
||||
} });
|
||||
try self.store.addScratchExposedItem(exposed_item);
|
||||
exposes = try self.store.exposedItemSpanFrom(scratch_top);
|
||||
} else {
|
||||
// Normal import: handle 'as' and 'exposing' clauses
|
||||
|
||||
// Handle 'as' clause if present
|
||||
if (self.peek() == .KwAs) {
|
||||
self.advance(); // Advance past KwAs
|
||||
alias_tok = self.pos;
|
||||
self.expect(.UpperIdent) catch {
|
||||
const malformed = try self.pushMalformed(AST.Statement.Idx, .expected_upper_name_after_import_as, start);
|
||||
return malformed;
|
||||
};
|
||||
}
|
||||
|
||||
// Handle 'exposing' clause if present (can occur with or without 'as')
|
||||
if (self.peek() == .KwExposing) {
|
||||
self.advance(); // Advance past KwExposing
|
||||
self.expect(.OpenSquare) catch {
|
||||
return try self.pushMalformed(AST.Statement.Idx, .import_exposing_no_open, start);
|
||||
};
|
||||
const scratch_top = self.store.scratchExposedItemTop();
|
||||
self.parseCollectionSpan(AST.ExposedItem.Idx, .CloseSquare, NodeStore.addScratchExposedItem, Parser.parseExposedItem) catch |err| {
|
||||
switch (err) {
|
||||
error.ExpectedNotFound => {
|
||||
while (self.peek() != .CloseSquare and self.peek() != .EndOfFile) {
|
||||
self.advance();
|
||||
}
|
||||
self.expect(.CloseSquare) catch {};
|
||||
self.store.clearScratchExposedItemsFrom(scratch_top);
|
||||
return try self.pushMalformed(AST.Statement.Idx, .import_exposing_no_close, start);
|
||||
},
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.TooNested => return error.TooNested,
|
||||
}
|
||||
};
|
||||
exposes = try self.store.exposedItemSpanFrom(scratch_top);
|
||||
}
|
||||
}
|
||||
|
||||
const statement_idx = try self.store.addStatement(.{ .import = .{
|
||||
.module_name_tok = module_name_tok,
|
||||
.qualifier_tok = qualifier,
|
||||
.alias_tok = alias_tok,
|
||||
.exposes = exposes,
|
||||
.nested_import = nested_import,
|
||||
.region = .{ .start = start, .end = self.pos },
|
||||
} });
|
||||
return statement_idx;
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ test "NodeStore round trip - Statement" {
|
|||
.qualifier_tok = null,
|
||||
.region = rand_region(),
|
||||
.exposes = AST.ExposedItem.Span{ .span = rand_span() },
|
||||
.nested_import = false,
|
||||
},
|
||||
});
|
||||
// Import with alias
|
||||
|
|
@ -199,6 +200,7 @@ test "NodeStore round trip - Statement" {
|
|||
.qualifier_tok = null,
|
||||
.region = rand_region(),
|
||||
.exposes = AST.ExposedItem.Span{ .span = rand_span() },
|
||||
.nested_import = false,
|
||||
},
|
||||
});
|
||||
// Import with qualifier but no alias
|
||||
|
|
@ -209,6 +211,7 @@ test "NodeStore round trip - Statement" {
|
|||
.qualifier_tok = rand_token_idx(),
|
||||
.region = rand_region(),
|
||||
.exposes = AST.ExposedItem.Span{ .span = rand_span() },
|
||||
.nested_import = false,
|
||||
},
|
||||
});
|
||||
// Import with both qualifier and alias
|
||||
|
|
@ -219,6 +222,7 @@ test "NodeStore round trip - Statement" {
|
|||
.qualifier_tok = rand_token_idx(),
|
||||
.region = rand_region(),
|
||||
.exposes = AST.ExposedItem.Span{ .span = rand_span() },
|
||||
.nested_import = false,
|
||||
},
|
||||
});
|
||||
try statements.append(gpa, AST.Statement{
|
||||
|
|
|
|||
|
|
@ -1925,7 +1925,7 @@ const Meta = struct {
|
|||
\\description=Hello world
|
||||
\\type=foobar
|
||||
);
|
||||
try std.testing.expectEqual(meta, Error.InvalidNodeType);
|
||||
try std.testing.expectError(Error.InvalidNodeType, meta);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,186 +31,50 @@ validateAuth : HttpAuth.Credentials -> Result(HttpAuth.Token, HttpAuth.Error)
|
|||
validateAuth = |creds| HttpAuth.validate(creds)
|
||||
~~~
|
||||
# EXPECTED
|
||||
PARSE ERROR - can_import_nested_modules.md:1:19:1:26
|
||||
PARSE ERROR - can_import_nested_modules.md:2:19:2:24
|
||||
PARSE ERROR - can_import_nested_modules.md:2:25:2:27
|
||||
PARSE ERROR - can_import_nested_modules.md:3:1:3:7
|
||||
PARSE ERROR - can_import_nested_modules.md:3:8:3:13
|
||||
PARSE ERROR - can_import_nested_modules.md:3:13:3:20
|
||||
PARSE ERROR - can_import_nested_modules.md:3:20:3:27
|
||||
PARSE ERROR - can_import_nested_modules.md:3:28:3:36
|
||||
PARSE ERROR - can_import_nested_modules.md:3:37:3:38
|
||||
PARSE ERROR - can_import_nested_modules.md:3:38:3:45
|
||||
PARSE ERROR - can_import_nested_modules.md:3:45:3:46
|
||||
MODULE NOT FOUND - can_import_nested_modules.md:1:1:1:19
|
||||
MODULE NOT FOUND - can_import_nested_modules.md:2:1:2:19
|
||||
MODULE NOT FOUND - can_import_nested_modules.md:1:1:1:26
|
||||
MODULE NOT FOUND - can_import_nested_modules.md:2:1:2:36
|
||||
MODULE NOT FOUND - can_import_nested_modules.md:3:1:3:46
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:6:15:6:30
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:7:26:7:41
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:10:28:10:42
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:11:29:11:43
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:14:15:14:37
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:14:58:14:77
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:16:5:16:37
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:20:23:20:30
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:20:37:20:58
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:23:16:23:36
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:23:47:23:61
|
||||
MODULE NOT IMPORTED - can_import_nested_modules.md:23:63:23:77
|
||||
UNDEFINED VARIABLE - can_import_nested_modules.md:24:24:24:41
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:1:19:1:26:**
|
||||
```roc
|
||||
import json.Parser.Config
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:2:19:2:24:**
|
||||
```roc
|
||||
import http.Client.Auth as HttpAuth
|
||||
```
|
||||
^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:2:25:2:27:**
|
||||
```roc
|
||||
import http.Client.Auth as HttpAuth
|
||||
```
|
||||
^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
Type applications require parentheses around their type arguments.
|
||||
|
||||
I found a type followed by what looks like a type argument, but they need to be connected with parentheses.
|
||||
|
||||
Instead of:
|
||||
**List U8**
|
||||
|
||||
Use:
|
||||
**List(U8)**
|
||||
|
||||
Other valid examples:
|
||||
`Dict(Str, Num)`
|
||||
`Result(a, Str)`
|
||||
`Maybe(List(U64))`
|
||||
|
||||
**can_import_nested_modules.md:3:1:3:7:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:8:3:13:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:13:3:20:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:20:3:27:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:28:3:36:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:37:3:38:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:38:3:45:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**can_import_nested_modules.md:3:45:3:46:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `json.Parser` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:1:1:1:19:**
|
||||
**can_import_nested_modules.md:1:1:1:26:**
|
||||
```roc
|
||||
import json.Parser.Config
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `http.Client` was not found in this Roc project.
|
||||
The module `http.Client.Auth` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:2:1:2:19:**
|
||||
**can_import_nested_modules.md:2:1:2:36:**
|
||||
```roc
|
||||
import http.Client.Auth as HttpAuth
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `utils.String.Format` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:3:1:3:46:**
|
||||
```roc
|
||||
import utils.String.Format exposing [padLeft]
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT IMPORTED**
|
||||
|
|
@ -235,17 +99,6 @@ parseConfig = |settings| Config.toString(settings)
|
|||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT IMPORTED**
|
||||
There is no module with the name `HttpAuth` imported into this Roc file.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:10:28:10:42:**
|
||||
```roc
|
||||
authenticate : Str, Str -> HttpAuth.Token
|
||||
```
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `login` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
|
@ -312,39 +165,6 @@ formatOutput = |text| padLeft(text, Config.defaultPadding)
|
|||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT IMPORTED**
|
||||
There is no module with the name `HttpAuth` imported into this Roc file.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:23:16:23:36:**
|
||||
```roc
|
||||
validateAuth : HttpAuth.Credentials -> Result(HttpAuth.Token, HttpAuth.Error)
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT IMPORTED**
|
||||
There is no module with the name `HttpAuth` imported into this Roc file.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:23:47:23:61:**
|
||||
```roc
|
||||
validateAuth : HttpAuth.Credentials -> Result(HttpAuth.Token, HttpAuth.Error)
|
||||
```
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT IMPORTED**
|
||||
There is no module with the name `HttpAuth` imported into this Roc file.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**can_import_nested_modules.md:23:63:23:77:**
|
||||
```roc
|
||||
validateAuth : HttpAuth.Credentials -> Result(HttpAuth.Token, HttpAuth.Error)
|
||||
```
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `validate` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
|
@ -379,19 +199,14 @@ EndOfFile,
|
|||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-import (raw "json.Parser"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-import (raw "http.Client"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-import (raw "json.Parser")
|
||||
(exposing
|
||||
(exposed-upper-ident (text "Config"))))
|
||||
(s-import (raw "http.Auth") (alias "HttpAuth"))
|
||||
(s-import (raw "utils.Format")
|
||||
(exposing
|
||||
(exposed-lower-ident
|
||||
(text "padLeft"))))
|
||||
(s-type-anno (name "parseConfig")
|
||||
(ty-fn
|
||||
(ty (name "Config.Settings"))
|
||||
|
|
@ -468,11 +283,9 @@ EndOfFile,
|
|||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
import json.Parser
|
||||
|
||||
import http.Client
|
||||
|
||||
|
||||
import json.Parser exposing [Config]
|
||||
import http.Auth as HttpAuth
|
||||
import utils.Format exposing [padLeft]
|
||||
|
||||
# Test multi-level type qualification
|
||||
parseConfig : Config.Settings -> Str
|
||||
|
|
@ -527,7 +340,7 @@ validateAuth = |creds| HttpAuth.validate(creds)
|
|||
(ty-fn (effectful false)
|
||||
(ty-lookup (name "Str") (external-module "Str"))
|
||||
(ty-lookup (name "Str") (external-module "Str"))
|
||||
(ty-malformed))))
|
||||
(ty-lookup (name "Token") (external-module "http.Client.Auth")))))
|
||||
(d-let
|
||||
(p-assign (ident "processData"))
|
||||
(e-lambda
|
||||
|
|
@ -572,14 +385,18 @@ validateAuth = |creds| HttpAuth.validate(creds)
|
|||
(p-assign (ident "creds")))))
|
||||
(annotation
|
||||
(ty-fn (effectful false)
|
||||
(ty-malformed)
|
||||
(ty-lookup (name "Credentials") (external-module "http.Client.Auth"))
|
||||
(ty-apply (name "Result") (external-module "Result")
|
||||
(ty-malformed)
|
||||
(ty-malformed)))))
|
||||
(ty-lookup (name "Token") (external-module "http.Client.Auth"))
|
||||
(ty-lookup (name "Error") (external-module "http.Client.Auth"))))))
|
||||
(s-import (module "json.Parser")
|
||||
(exposes
|
||||
(exposed (name "Config") (wildcard false))))
|
||||
(s-import (module "http.Client.Auth")
|
||||
(exposes))
|
||||
(s-import (module "http.Client")
|
||||
(exposes)))
|
||||
(s-import (module "utils.String.Format")
|
||||
(exposes
|
||||
(exposed (name "padLeft") (wildcard false)))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@ data : json.Core.Utf8.EncodedData
|
|||
data = json.Core.Utf8.encode("hello")
|
||||
~~~
|
||||
# EXPECTED
|
||||
PARSE ERROR - multi_qualified_import.md:1:17:1:22
|
||||
PARSE ERROR - multi_qualified_import.md:1:23:1:31
|
||||
PARSE ERROR - multi_qualified_import.md:1:32:1:33
|
||||
PARSE ERROR - multi_qualified_import.md:1:40:1:41
|
||||
PARSE ERROR - multi_qualified_import.md:12:12:12:17
|
||||
PARSE ERROR - multi_qualified_import.md:12:17:12:22
|
||||
PARSE ERROR - multi_qualified_import.md:12:22:12:29
|
||||
|
|
@ -31,7 +27,7 @@ PARSE ERROR - multi_qualified_import.md:12:30:12:31
|
|||
PARSE ERROR - multi_qualified_import.md:12:31:12:36
|
||||
PARSE ERROR - multi_qualified_import.md:12:36:12:37
|
||||
PARSE ERROR - multi_qualified_import.md:12:37:12:38
|
||||
MODULE NOT FOUND - multi_qualified_import.md:1:1:1:17
|
||||
MODULE NOT FOUND - multi_qualified_import.md:1:1:1:41
|
||||
UNDECLARED TYPE - multi_qualified_import.md:3:16:3:23
|
||||
UNDEFINED VARIABLE - multi_qualified_import.md:4:16:4:45
|
||||
MODULE NOT IMPORTED - multi_qualified_import.md:7:11:7:33
|
||||
|
|
@ -43,62 +39,6 @@ UNDEFINED VARIABLE - multi_qualified_import.md:12:8:12:12
|
|||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**multi_qualified_import.md:1:17:1:22:**
|
||||
```roc
|
||||
import json.Core.Utf8 exposing [Encoder]
|
||||
```
|
||||
^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**multi_qualified_import.md:1:23:1:31:**
|
||||
```roc
|
||||
import json.Core.Utf8 exposing [Encoder]
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**multi_qualified_import.md:1:32:1:33:**
|
||||
```roc
|
||||
import json.Core.Utf8 exposing [Encoder]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
Type applications require parentheses around their type arguments.
|
||||
|
||||
I found a type followed by what looks like a type argument, but they need to be connected with parentheses.
|
||||
|
||||
Instead of:
|
||||
**List U8**
|
||||
|
||||
Use:
|
||||
**List(U8)**
|
||||
|
||||
Other valid examples:
|
||||
`Dict(Str, Num)`
|
||||
`Result(a, Str)`
|
||||
`Maybe(List(U64))`
|
||||
|
||||
**multi_qualified_import.md:1:40:1:41:**
|
||||
```roc
|
||||
import json.Core.Utf8 exposing [Encoder]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**multi_qualified_import.md:12:12:12:17:**
|
||||
```roc
|
||||
data = json.Core.Utf8.encode("hello")
|
||||
|
|
@ -184,14 +124,14 @@ data = json.Core.Utf8.encode("hello")
|
|||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `json.Core` was not found in this Roc project.
|
||||
The module `json.Core.Utf8` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**multi_qualified_import.md:1:1:1:17:**
|
||||
**multi_qualified_import.md:1:1:1:41:**
|
||||
```roc
|
||||
import json.Core.Utf8 exposing [Encoder]
|
||||
```
|
||||
^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNDECLARED TYPE**
|
||||
|
|
@ -277,11 +217,9 @@ EndOfFile,
|
|||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-import (raw "json.Core"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-import (raw "json.Utf8")
|
||||
(exposing
|
||||
(exposed-upper-ident (text "Encoder"))))
|
||||
(s-type-anno (name "json_encoder")
|
||||
(ty (name "Encoder")))
|
||||
(s-decl
|
||||
|
|
@ -314,8 +252,7 @@ EndOfFile,
|
|||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
import json.Core
|
||||
|
||||
import json.Utf8 exposing [Encoder]
|
||||
|
||||
json_encoder : Encoder
|
||||
json_encoder = Json.Core.Utf8.defaultEncoder
|
||||
|
|
@ -352,8 +289,9 @@ data = json
|
|||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(annotation
|
||||
(ty-malformed)))
|
||||
(s-import (module "json.Core")
|
||||
(exposes)))
|
||||
(s-import (module "json.Core.Utf8")
|
||||
(exposes
|
||||
(exposed (name "Encoder") (wildcard false)))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -11,102 +11,18 @@ red : CE
|
|||
red = ... # not implemented
|
||||
~~~
|
||||
# EXPECTED
|
||||
PARSE ERROR - nominal_import_long_package.md:1:21:1:27
|
||||
PARSE ERROR - nominal_import_long_package.md:1:28:1:36
|
||||
PARSE ERROR - nominal_import_long_package.md:1:37:1:38
|
||||
PARSE ERROR - nominal_import_long_package.md:1:46:1:48
|
||||
PARSE ERROR - nominal_import_long_package.md:1:51:1:52
|
||||
MODULE NOT FOUND - nominal_import_long_package.md:1:1:1:21
|
||||
MODULE NOT FOUND - nominal_import_long_package.md:1:1:1:52
|
||||
UNDECLARED TYPE - nominal_import_long_package.md:3:7:3:9
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**nominal_import_long_package.md:1:21:1:27:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**nominal_import_long_package.md:1:28:1:36:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**nominal_import_long_package.md:1:37:1:38:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
Type applications require parentheses around their type arguments.
|
||||
|
||||
I found a type followed by what looks like a type argument, but they need to be connected with parentheses.
|
||||
|
||||
Instead of:
|
||||
**List U8**
|
||||
|
||||
Use:
|
||||
**List(U8)**
|
||||
|
||||
Other valid examples:
|
||||
`Dict(Str, Num)`
|
||||
`Result(a, Str)`
|
||||
`Maybe(List(U64))`
|
||||
|
||||
**nominal_import_long_package.md:1:46:1:48:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
Type applications require parentheses around their type arguments.
|
||||
|
||||
I found a type followed by what looks like a type argument, but they need to be connected with parentheses.
|
||||
|
||||
Instead of:
|
||||
**List U8**
|
||||
|
||||
Use:
|
||||
**List(U8)**
|
||||
|
||||
Other valid examples:
|
||||
`Dict(Str, Num)`
|
||||
`Result(a, Str)`
|
||||
`Maybe(List(U64))`
|
||||
|
||||
**nominal_import_long_package.md:1:51:1:52:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `design.Styles` was not found in this Roc project.
|
||||
The module `design.Styles.Color` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**nominal_import_long_package.md:1:1:1:21:**
|
||||
**nominal_import_long_package.md:1:1:1:52:**
|
||||
```roc
|
||||
import design.Styles.Color exposing [Encoder as CE]
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNDECLARED TYPE**
|
||||
|
|
@ -132,12 +48,9 @@ EndOfFile,
|
|||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-import (raw "design.Styles"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-import (raw "design.Color")
|
||||
(exposing
|
||||
(exposed-upper-ident (text "Encoder") (as "CE"))))
|
||||
(s-type-anno (name "red")
|
||||
(ty (name "CE")))
|
||||
(s-decl
|
||||
|
|
@ -146,8 +59,7 @@ EndOfFile,
|
|||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
import design.Styles
|
||||
|
||||
import design.Color exposing [Encoder as CE]
|
||||
|
||||
red : CE
|
||||
red = ... # not implemented
|
||||
|
|
@ -160,8 +72,9 @@ red = ... # not implemented
|
|||
(e-not-implemented)
|
||||
(annotation
|
||||
(ty-malformed)))
|
||||
(s-import (module "design.Styles")
|
||||
(exposes)))
|
||||
(s-import (module "design.Styles.Color")
|
||||
(exposes
|
||||
(exposed (name "Encoder") (alias "CE") (wildcard false)))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -53,12 +53,8 @@ transform = |result|
|
|||
# EXPECTED
|
||||
PARSE ERROR - qualified_type_canonicalization.md:8:1:8:7
|
||||
PARSE ERROR - qualified_type_canonicalization.md:8:14:8:21
|
||||
PARSE ERROR - qualified_type_canonicalization.md:10:15:10:23
|
||||
PARSE ERROR - qualified_type_canonicalization.md:10:24:10:32
|
||||
PARSE ERROR - qualified_type_canonicalization.md:10:33:10:34
|
||||
PARSE ERROR - qualified_type_canonicalization.md:10:39:10:40
|
||||
MODULE NOT FOUND - qualified_type_canonicalization.md:9:1:9:13
|
||||
MODULE NOT FOUND - qualified_type_canonicalization.md:10:1:10:15
|
||||
MODULE NOT FOUND - qualified_type_canonicalization.md:10:1:10:40
|
||||
MODULE NOT FOUND - qualified_type_canonicalization.md:11:1:11:32
|
||||
UNDECLARED TYPE - qualified_type_canonicalization.md:15:19:15:24
|
||||
MODULE NOT IMPORTED - qualified_type_canonicalization.md:22:23:22:44
|
||||
|
|
@ -106,62 +102,6 @@ import Basics.Result
|
|||
^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**qualified_type_canonicalization.md:10:15:10:23:**
|
||||
```roc
|
||||
import ModuleA.ModuleB exposing [TypeC]
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**qualified_type_canonicalization.md:10:24:10:32:**
|
||||
```roc
|
||||
import ModuleA.ModuleB exposing [TypeC]
|
||||
```
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**qualified_type_canonicalization.md:10:33:10:34:**
|
||||
```roc
|
||||
import ModuleA.ModuleB exposing [TypeC]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
Type applications require parentheses around their type arguments.
|
||||
|
||||
I found a type followed by what looks like a type argument, but they need to be connected with parentheses.
|
||||
|
||||
Instead of:
|
||||
**List U8**
|
||||
|
||||
Use:
|
||||
**List(U8)**
|
||||
|
||||
Other valid examples:
|
||||
`Dict(Str, Num)`
|
||||
`Result(a, Str)`
|
||||
`Maybe(List(U64))`
|
||||
|
||||
**qualified_type_canonicalization.md:10:39:10:40:**
|
||||
```roc
|
||||
import ModuleA.ModuleB exposing [TypeC]
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `Color` was not found in this Roc project.
|
||||
|
||||
|
|
@ -174,14 +114,14 @@ import Color
|
|||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
The module `ModuleA` was not found in this Roc project.
|
||||
The module `ModuleB` was not found in this Roc project.
|
||||
|
||||
You're attempting to use this module here:
|
||||
**qualified_type_canonicalization.md:10:1:10:15:**
|
||||
**qualified_type_canonicalization.md:10:1:10:40:**
|
||||
```roc
|
||||
import ModuleA.ModuleB exposing [TypeC]
|
||||
```
|
||||
^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**MODULE NOT FOUND**
|
||||
|
|
@ -358,11 +298,9 @@ EndOfFile,
|
|||
(statements
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-import (raw "Color"))
|
||||
(s-import (raw "ModuleA"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "expected_colon_after_type_annotation"))
|
||||
(s-import (raw ".ModuleB")
|
||||
(exposing
|
||||
(exposed-upper-ident (text "TypeC"))))
|
||||
(s-import (raw "ExternalModule") (alias "ExtMod"))
|
||||
(s-type-anno (name "simpleQualified")
|
||||
(ty (name "Color.RGB")))
|
||||
|
|
@ -456,8 +394,7 @@ EndOfFile,
|
|||
~~~roc
|
||||
|
||||
import Color
|
||||
import ModuleA
|
||||
|
||||
import ModuleB exposing [TypeC]
|
||||
import ExternalModule as ExtMod
|
||||
|
||||
# Simple qualified type
|
||||
|
|
@ -582,8 +519,9 @@ transform = |result|
|
|||
(ty-malformed))))
|
||||
(s-import (module "Color")
|
||||
(exposes))
|
||||
(s-import (module "ModuleA")
|
||||
(exposes))
|
||||
(s-import (module "ModuleB")
|
||||
(exposes
|
||||
(exposed (name "TypeC") (wildcard false))))
|
||||
(s-import (module "ExternalModule")
|
||||
(exposes)))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
[files]
|
||||
extend-exclude = [
|
||||
".git/",
|
||||
"design/language/Abilities.md",
|
||||
"src/snapshots/fuzz_crash",
|
||||
"crates/vendor/",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue