file-local / package-local decls

This commit is contained in:
2026-08-09 17:01:05 +02:00
parent a7a32894f3
commit 8785912b73
15 changed files with 369 additions and 152 deletions
+40 -8
View File
@@ -3251,6 +3251,7 @@ inferred_error_channels_reject_unstable_or_untyped_contracts :: proc(t: ^testing
Case :: struct {
source: string,
message: string,
absent: string,
}
cases := [?]Case{
{
@@ -3259,7 +3260,7 @@ fail func() void ! Error { return .failed }
visible func() void! { try fail() }
main func() void {}
`,
message="inferred error channels are only allowed on hidden functions and root main",
message="inferred error channels are only allowed on local functions and root main",
},
{
source=`Error :: enum { failed }
@@ -3281,6 +3282,20 @@ main func() void { empty() catch |_| {} }
`,
message="could not infer a named error channel for 'empty'",
},
{
source=`Error :: enum { failed }
fail func() void ! Error { return .failed }
value func() i32 { return 1 }
main func() void! {
fail() catch |_| {
return
}
_ = try value()
}
`,
message="could not infer a named error channel for 'main'",
absent="non-void function must return a value",
},
}
for test_case in cases {
source_file := source.Source{path="test.bro", text=test_case.source}
@@ -3291,10 +3306,14 @@ main func() void { empty() catch |_| {} }
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
found := false
unexpected := false
for diagnostic in diagnostics.items {
found = found || strings.contains(diagnostic.message, test_case.message)
unexpected = unexpected ||
len(test_case.absent) > 0 && strings.contains(diagnostic.message, test_case.absent)
}
testing.expect(t, found)
testing.expect(t, !unexpected)
hir.destroy_module(&hir_module)
ast.destroy_module(&ast_module)
@@ -10345,14 +10364,18 @@ imports_are_file_local :: proc(t: ^testing.T) {
}
@(test)
hide_is_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) {
visibility_modifiers_are_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) {
cases := [?]string{
`hide import "../dep"`,
`hide dep :: import "../dep"`,
`hide(file) dep :: import "../dep"`,
`hide func() void {}`,
`main func(hide value i32) void {}`,
`Box :: struct { hide i32 }`,
`main func() void { hide value i32 := 1 }`,
`hide() value :: 1`,
`hide(module) value :: 1`,
`hide("file") value :: 1`,
`hide(file value :: 1`,
}
for text in cases {
source_file := source.Source{path="test.bro", text=text}
@@ -10370,17 +10393,17 @@ hide_is_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) {
}
@(test)
hide_declarations_are_package_hidden :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-hidden-declarations"
local_declarations_resolve_at_their_declared_scope :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-local-declarations"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/packages/hidden_valid/app", output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 9)
testing.expect_value(t, state.exit_code, 15)
}
@(test)
package_hidden_declarations_resolve_locally_but_not_through_imports :: proc(t: ^testing.T) {
package_and_file_local_visibility_is_enforced :: proc(t: ^testing.T) {
sources := source.init_store()
defer source.destroy_store(&sources)
diagnostics := source.init_store_diagnostics(&sources)
@@ -10398,18 +10421,27 @@ package_hidden_declarations_resolve_locally_but_not_through_imports :: proc(t: ^
found_sibling_type := false
found_import := false
found_collision := false
found_file_sibling := false
found_file_sibling_value := false
found_file_sibling_type := false
for diagnostic in diagnostics.items {
found_sibling = found_sibling || strings.contains(diagnostic.message, "unknown symbol 'sibling'")
found_sibling_value = found_sibling_value || strings.contains(diagnostic.message, "unknown symbol 'sibling_value'")
found_sibling_type = found_sibling_type || strings.contains(diagnostic.message, "unknown or opaque record type 'Sibling'")
found_import = found_import || strings.contains(diagnostic.message, "package 'dep' has no member 'secret'")
found_collision = found_collision || strings.contains(diagnostic.message, "duplicate function 'collision'")
found_file_sibling = found_file_sibling || strings.contains(diagnostic.message, "unknown symbol 'file_sibling'")
found_file_sibling_value = found_file_sibling_value || strings.contains(diagnostic.message, "unknown symbol 'file_sibling_value'")
found_file_sibling_type = found_file_sibling_type || strings.contains(diagnostic.message, "unknown or opaque record type 'File_Sibling'")
}
testing.expect(t, !found_sibling)
testing.expect(t, !found_sibling_value)
testing.expect(t, !found_sibling_type)
testing.expect(t, found_import)
testing.expect(t, found_collision)
testing.expect(t, found_file_sibling)
testing.expect(t, found_file_sibling_value)
testing.expect(t, found_file_sibling_type)
}
@(test)
@@ -10704,7 +10736,7 @@ main func() void {}
testing.expect(t, loaded)
wants := []string{
"has no member 'missing'",
"is package-hidden",
"is not public",
"unknown symbol 'nope'",
"unavailable imported package 'gone'",
"package member 'dep.ambiguous' is ambiguous",