conditional optional unwrapping
This commit is contained in:
@@ -3710,3 +3710,198 @@ deeply_nested_child_packages_load_recursively :: proc(t: ^testing.T) {
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 9)
|
||||
}
|
||||
|
||||
@(test)
|
||||
function_returning_only_in_if_branch_is_diagnosed :: proc(t: ^testing.T) {
|
||||
text := `classify :: func(n i32) i32 {
|
||||
if n > 0 {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
main :: func() void {
|
||||
_ = classify(5)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "does not return a value")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
function_returning_in_both_if_arms_is_accepted :: proc(t: ^testing.T) {
|
||||
text := `classify :: func(n i32) i32 {
|
||||
if n > 0 {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
main :: func() void {
|
||||
_ = classify(5)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "does not return a value")
|
||||
}
|
||||
testing.expect(t, !found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
function_returning_after_if_is_accepted :: proc(t: ^testing.T) {
|
||||
text := `classify :: func(n i32) i32 {
|
||||
if n > 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
main :: func() void {
|
||||
_ = classify(5)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "does not return a value")
|
||||
}
|
||||
testing.expect(t, !found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
conditional_unwrap_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-conditional-unwrap"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/conditional_unwrap", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
// present scalar binds and unwraps (40), none takes the else (+2), a present
|
||||
// optional pointer binds and derefs (+0), a none optional pointer is skipped.
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(test)
|
||||
if_unwrap_on_non_optional_is_diagnosed :: proc(t: ^testing.T) {
|
||||
text := `main :: func() i32 {
|
||||
x i32 = 5
|
||||
if x |v| {
|
||||
return v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "unwrap requires an optional")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
if_unwrap_binding_is_scoped_to_then_block :: proc(t: ^testing.T) {
|
||||
// The binding `v` is usable in the then-block but not in the else-block.
|
||||
text := `main :: func() i32 {
|
||||
a ?i32 = 1
|
||||
if a |v| {
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "unresolved global 'v'")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
if_unwrap_binding_is_immutable :: proc(t: ^testing.T) {
|
||||
text := `main :: func() i32 {
|
||||
a ?i32 = 1
|
||||
if a |v| {
|
||||
v = 2
|
||||
return v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "cannot assign immutable local 'v'")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user