diagnostics upgrade

This commit is contained in:
2026-07-15 22:55:31 +02:00
parent c4fa8e930f
commit 7de0b7f268
11 changed files with 857 additions and 75 deletions
+98 -1
View File
@@ -1486,7 +1486,7 @@ validate_declaration_aliases :: proc(state: ^State) {
import_id := find_type_import(state.module, alias.file, alias.qualifier)
if import_id == ast.INVALID_IMPORT {
alias.diagnostic = source.addf(state.diagnostics, alias.span, "unknown package alias '%s'", symbol.resolve(state.symbols, alias.qualifier))
alias.diagnostic = source.addf(state.diagnostics, alias.span, "unknown symbol '%s'", symbol.resolve(state.symbols, alias.qualifier))
alias.valid = false
continue
}
@@ -1613,6 +1613,81 @@ canonical_type :: proc(
return resolved
}
diagnose_qualified_type_uses :: proc(state: ^State) {
module := state.module
for &type_use in module.type_uses {
item, ok := types.node(&module.type_store, type_use.type)
if !ok || item.qualifier == 0 || item.kind != .Named {
continue
}
qualifier := symbol.Id(item.qualifier)
name := symbol.Id(item.name)
import_id := find_type_import(module, type_use.file, qualifier)
if import_id == ast.INVALID_IMPORT {
type_use.diagnostic = source.addf(
state.diagnostics,
type_use.span,
"unknown symbol '%s'",
symbol.resolve(state.symbols, qualifier),
)
source.set_primary_label(state.diagnostics, type_use.diagnostic, "unknown symbol")
continue
}
import_item := module.imports[import_id]
if !import_item.valid || import_item.target == ast.INVALID_PACKAGE ||
int(import_item.target) >= len(module.packages) || !module.packages[import_item.target].available {
type_use.diagnostic = source.addf(
state.diagnostics,
type_use.span,
"unavailable imported package '%s'",
symbol.resolve(state.symbols, qualifier),
)
continue
}
if !types.is_valid(types.find_named(&module.type_store, u32(import_item.target), u32(name))) {
type_use.diagnostic = source.addf(
state.diagnostics,
type_use.span,
"package '%s' has no member '%s'",
symbol.resolve(state.symbols, qualifier),
symbol.resolve(state.symbols, name),
)
}
}
}
type_resolution_diagnostic :: proc(module: ^ast.Module, value: types.Type, file: ast.File_Id, depth := 0) -> source.Diagnostic_Id {
if depth > 64 {
return source.INVALID_DIAGNOSTIC
}
for type_use in module.type_uses {
if type_use.file == file && type_use.type == value && type_use.diagnostic != source.INVALID_DIAGNOSTIC {
return type_use.diagnostic
}
}
item, ok := types.node(&module.type_store, value)
if !ok {
return source.INVALID_DIAGNOSTIC
}
if diagnostic := type_resolution_diagnostic(module, item.child, file, depth+1);
diagnostic != source.INVALID_DIAGNOSTIC {
return diagnostic
}
if diagnostic := type_resolution_diagnostic(module, item.extra, file, depth+1);
diagnostic != source.INVALID_DIAGNOSTIC {
return diagnostic
}
if item.kind == .Function {
for param in types.params_for(&module.type_store, value) {
if diagnostic := type_resolution_diagnostic(module, param.type, file, depth+1);
diagnostic != source.INVALID_DIAGNOSTIC {
return diagnostic
}
}
}
return source.INVALID_DIAGNOSTIC
}
canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
original_count := len(module.type_store.nodes)
mapping := make([]types.Type, original_count, allocator)
@@ -1620,6 +1695,19 @@ canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
defer delete(mapping, allocator)
defer delete(visiting, allocator)
for &function in module.functions {
for param in function.params {
if diagnostic := type_resolution_diagnostic(module, param.type, function.file);
diagnostic != source.INVALID_DIAGNOSTIC && function.diagnostic == source.INVALID_DIAGNOSTIC {
function.diagnostic = diagnostic
}
}
signature_results := [2]types.Type{function.result, function.error}
for value in signature_results {
if diagnostic := type_resolution_diagnostic(module, value, function.file);
diagnostic != source.INVALID_DIAGNOSTIC && function.diagnostic == source.INVALID_DIAGNOSTIC {
function.diagnostic = diagnostic
}
}
for &param in function.params {
param.type = canonical_type(module, param.type, mapping, visiting)
}
@@ -1627,9 +1715,17 @@ canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
function.error = canonical_type(module, function.error, mapping, visiting)
}
for &global in module.globals {
if diagnostic := type_resolution_diagnostic(module, global.type, global.file);
diagnostic != source.INVALID_DIAGNOSTIC && global.diagnostic == source.INVALID_DIAGNOSTIC {
global.diagnostic = diagnostic
}
global.type = canonical_type(module, global.type, mapping, visiting)
}
for &statement in module.statements {
if diagnostic := type_resolution_diagnostic(module, statement.type, ast.File_Id(statement.span.file));
diagnostic != source.INVALID_DIAGNOSTIC && statement.diagnostic == source.INVALID_DIAGNOSTIC {
statement.diagnostic = diagnostic
}
statement.type = canonical_type(module, statement.type, mapping, visiting)
}
for &field in module.type_fields {
@@ -1687,6 +1783,7 @@ load :: proc(
}
validate_imports(&state)
validate_declaration_aliases(&state)
diagnose_qualified_type_uses(&state)
canonicalize_types(&module, allocator)
return module, !state.root_failed
}