// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module builder import v2.ast import v2.pref import v2.token fn (b &Builder) validate_freestanding_cleanc_contract() bool { if b.pref == unsafe { nil } || !b.pref.is_freestanding() { return true } mut diagnostics := []string{} mut seen := map[string]bool{} target_os := b.pref.target_os_or_host() if b.pref.prealloc { msg := 'error: freestanding target cannot use -prealloc because it requires hosted allocation support' diagnostics << msg seen[msg] = true } if b.pref.is_shared_lib { msg := 'error: freestanding target cannot use -shared because dynamic library output requires hosted loader support' if !seen[msg] { diagnostics << msg seen[msg] = true } } if b.pref.hot_fn.len > 0 { msg := 'error: freestanding target cannot use -hot-fn because live code reload needs hosted OS services' if !seen[msg] { diagnostics << msg seen[msg] = true } } if b.pref.has_freestanding_hooks() && (!b.pref.skip_builtin || !b.pref.skip_type_check) { msg := 'error: freestanding target platform hooks currently require --skip-builtin and --skip-type-check until cleanc builtin hook signatures are implemented' if !seen[msg] { diagnostics << msg seen[msg] = true } } scan_ctx := freestanding_scan_context(b.pref, target_os) allow_pkgconfig_imports := !b.pref.is_cross_target() for i, ff in b.flat.files { if !b.should_scan_freestanding_diagnostic_file(b.flat.file_name(ff)) { continue } for imported in active_file_imports_from_flat_with_options(&b.flat, ff, b.pref.user_defines, b.pref.explicit_user_defines, target_os, allow_pkgconfig_imports) { if msg := freestanding_restricted_import_diagnostic(imported.name) { if !seen[msg] { diagnostics << msg seen[msg] = true } } } call_name := freestanding_restricted_call_in_cursor_stmts(b.flat.file_cursor(i).stmts(), scan_ctx) if call_name != '' { msg := freestanding_restricted_call_diagnostic(call_name) if !seen[msg] { diagnostics << msg seen[msg] = true } } } for msg in diagnostics { eprintln(msg) } if diagnostics.len > 0 { eprintln('hint: provide platform bindings or build without -freestanding') return false } return true } fn (b &Builder) should_scan_freestanding_diagnostic_file(file_name string) bool { if file_name == '' || b.pref == unsafe { nil } || b.pref.vroot == '' { return true } path := file_name.replace('\\', '/') vroot := b.pref.vroot.replace('\\', '/').trim_right('/') return !path.starts_with('${vroot}/vlib/') } fn freestanding_restricted_import_diagnostic(import_name string) ?string { root := import_name.all_before('.') return match root { 'os' { 'error: freestanding target cannot use module os without a platform implementation' } 'time' { 'error: freestanding target cannot use module time without a platform time source' } 'term' { 'error: freestanding target cannot use module term without terminal support' } 'net' { 'error: freestanding target cannot use module net without platform networking support' } 'sync' { 'error: freestanding target cannot use module sync without threading/locking support' } else { none } } } fn freestanding_restricted_call_diagnostic(call_name string) string { return match call_name { 'panic' { 'error: freestanding target cannot use builtin panic without panic platform hook' } 'spawn' { 'error: freestanding target cannot use spawn because threading support is not provided' } 'go' { 'error: freestanding target cannot use go because threading support is not provided' } 'lock' { 'error: freestanding target cannot use lock/rlock because locking support is not provided' } 'shared' { 'error: freestanding target cannot use shared data because locking support is not provided' } 'live' { 'error: freestanding target cannot use @[live] because live reload needs hosted OS services' } 'assert' { 'error: freestanding target cannot use assert because failed assertions need hosted output/exit support' } else { 'error: freestanding target cannot use builtin ${call_name} without output platform hook' } } } struct FreestandingScanContext { user_defines []string explicit_defines []string target_os string freestanding_hooks []string } fn freestanding_scan_context(p &pref.Preferences, target_os string) FreestandingScanContext { return FreestandingScanContext{ user_defines: p.user_defines explicit_defines: p.explicit_user_defines target_os: target_os freestanding_hooks: p.freestanding_hook_list() } } fn (ctx FreestandingScanContext) has_hook(hook string) bool { return hook in ctx.freestanding_hooks } fn freestanding_output_builtin_call_name(name string, ctx FreestandingScanContext) string { if name in ['print', 'println', 'eprint', 'eprintln'] && !ctx.has_hook('output') { return name } if name == 'panic' && !ctx.has_hook('panic') { return name } return '' } fn freestanding_attributes_are_inactive(attributes []ast.Attribute, ctx FreestandingScanContext) bool { for attr in attributes { if attr.comptime_cond !is ast.EmptyExpr && !ast_comptime_cond_matches_with_explicit(attr.comptime_cond, ctx.user_defines, ctx.explicit_defines, ctx.target_os) { return true } } return false } fn freestanding_channel_scan_options(ctx FreestandingScanContext) ChannelScanOptions { return ChannelScanOptions{ user_defines: ctx.user_defines explicit_user_defines: ctx.explicit_defines target_os: ctx.target_os allow_pkgconfig: true filter_comptime: true } } fn freestanding_cursor_attributes_are_inactive(attributes ast.CursorList, ctx FreestandingScanContext) bool { options := freestanding_channel_scan_options(ctx) for i in 0 .. attributes.len() { attr := attributes.at(i) if !attr.is_valid() || attr.kind() != .aux_attribute { continue } cond := attr.edge(1) if cond.is_valid() && cond.kind() != .expr_empty && !flat_comptime_cond_matches(cond, options) { return true } } return false } fn freestanding_cursor_attributes_has(attributes ast.CursorList, name string) bool { for i in 0 .. attributes.len() { attr := attributes.at(i) if !attr.is_valid() || attr.kind() != .aux_attribute { continue } if attr.name() == name { return true } value := attr.edge(0) if attr.name() == '' && value.is_valid() && value.kind() == .expr_ident && value.name() == name { return true } } return false } fn freestanding_restricted_call_in_stmts(stmts []ast.Stmt, ctx FreestandingScanContext) string { for stmt in stmts { call_name := freestanding_restricted_call_in_stmt(stmt, ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_stmts(stmts ast.CursorList, ctx FreestandingScanContext) string { for i in 0 .. stmts.len() { call_name := freestanding_restricted_call_in_cursor_stmt(stmts.at(i), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_stmt(stmt ast.Cursor, ctx FreestandingScanContext) string { if !stmt.is_valid() { return '' } return match stmt.kind() { .stmt_assert { expr_call := freestanding_restricted_call_in_cursor_edges(stmt, 0, stmt.edge_count(), ctx) if expr_call != '' { expr_call } else { 'assert' } } .stmt_assign { freestanding_restricted_call_in_cursor_edges(stmt, 0, stmt.edge_count(), ctx) } .stmt_block, .stmt_defer { freestanding_restricted_call_in_cursor_stmt_edges(stmt, 0, stmt.edge_count(), ctx) } .stmt_comptime { freestanding_restricted_call_in_cursor_stmt(stmt.edge(0), ctx) } .stmt_const_decl { freestanding_restricted_call_in_cursor_field_inits(stmt.list_at(0), ctx) } .stmt_expr { freestanding_restricted_call_in_cursor_expr(stmt.edge(0), ctx) } .stmt_fn_decl { attributes := stmt.list_at(2) if freestanding_cursor_attributes_are_inactive(attributes, ctx) { '' } else if freestanding_cursor_attributes_has(attributes, 'live') { 'live' } else { freestanding_restricted_call_in_cursor_stmts(stmt.list_at(3), ctx) } } .stmt_for_in { freestanding_restricted_call_in_cursor_edges(stmt, 0, stmt.edge_count(), ctx) } .stmt_for { init_call := freestanding_restricted_call_in_cursor_stmt(stmt.edge(0), ctx) if init_call != '' { init_call } else { cond_call := freestanding_restricted_call_in_cursor_expr(stmt.edge(1), ctx) if cond_call != '' { cond_call } else { post_call := freestanding_restricted_call_in_cursor_stmt(stmt.edge(2), ctx) if post_call != '' { post_call } else { freestanding_restricted_call_in_cursor_stmts(stmt.for_body_list(), ctx) } } } } .stmt_global_decl { freestanding_restricted_call_in_cursor_field_decls(stmt.list_at(1), ctx) } .stmt_interface_decl { freestanding_restricted_call_in_cursor_field_decls(stmt.list_at(3), ctx) } .stmt_label { freestanding_restricted_call_in_cursor_stmt(stmt.edge(0), ctx) } .stmt_return { freestanding_restricted_call_in_cursor_edges(stmt, 0, stmt.edge_count(), ctx) } .stmt_struct_decl { freestanding_restricted_call_in_cursor_field_decls(stmt.list_at(4), ctx) } else { '' } } } fn freestanding_restricted_call_in_cursor_stmt_edges(stmt ast.Cursor, start int, end int, ctx FreestandingScanContext) string { for i in start .. end { call_name := freestanding_restricted_call_in_cursor_stmt(stmt.edge(i), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_edges(expr_parent ast.Cursor, start int, end int, ctx FreestandingScanContext) string { for i in start .. end { call_name := freestanding_restricted_call_in_cursor_expr(expr_parent.edge(i), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_expr_list(exprs ast.CursorList, ctx FreestandingScanContext) string { for i in 0 .. exprs.len() { call_name := freestanding_restricted_call_in_cursor_expr(exprs.at(i), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_stmt(stmt ast.Stmt, ctx FreestandingScanContext) string { return match stmt { ast.AssertStmt { expr_call := freestanding_restricted_call_in_exprs([stmt.expr, stmt.extra], ctx) if expr_call != '' { expr_call } else { 'assert' } } ast.AssignStmt { lhs_call := freestanding_restricted_call_in_exprs(stmt.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_exprs(stmt.rhs, ctx) } } ast.BlockStmt { freestanding_restricted_call_in_stmts(stmt.stmts, ctx) } ast.ComptimeStmt { freestanding_restricted_call_in_stmt(stmt.stmt, ctx) } ast.ConstDecl { freestanding_restricted_call_in_field_inits(stmt.fields, ctx) } ast.DeferStmt { freestanding_restricted_call_in_stmts(stmt.stmts, ctx) } ast.ExprStmt { freestanding_restricted_call_in_expr(stmt.expr, ctx) } ast.FnDecl { if freestanding_attributes_are_inactive(stmt.attributes, ctx) { '' } else if stmt.attributes.has('live') { 'live' } else { freestanding_restricted_call_in_stmts(stmt.stmts, ctx) } } ast.ForInStmt { freestanding_restricted_call_in_exprs([stmt.key, stmt.value, stmt.expr], ctx) } ast.ForStmt { init_call := freestanding_restricted_call_in_stmt(stmt.init, ctx) if init_call != '' { init_call } else { cond_call := freestanding_restricted_call_in_expr(stmt.cond, ctx) if cond_call != '' { cond_call } else { post_call := freestanding_restricted_call_in_stmt(stmt.post, ctx) if post_call != '' { post_call } else { freestanding_restricted_call_in_stmts(stmt.stmts, ctx) } } } } ast.GlobalDecl { freestanding_restricted_call_in_field_decls(stmt.fields, ctx) } ast.InterfaceDecl { freestanding_restricted_call_in_field_decls(stmt.fields, ctx) } ast.LabelStmt { freestanding_restricted_call_in_stmt(stmt.stmt, ctx) } ast.ReturnStmt { freestanding_restricted_call_in_exprs(stmt.exprs, ctx) } ast.StructDecl { freestanding_restricted_call_in_field_decls(stmt.fields, ctx) } else { '' } } } fn freestanding_restricted_call_in_exprs(exprs []ast.Expr, ctx FreestandingScanContext) string { for expr in exprs { call_name := freestanding_restricted_call_in_expr(expr, ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_field_inits(fields ast.CursorList, ctx FreestandingScanContext) string { for i in 0 .. fields.len() { call_name := freestanding_restricted_call_in_cursor_expr(fields.at(i).edge(0), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_field_inits(fields []ast.FieldInit, ctx FreestandingScanContext) string { for field in fields { call_name := freestanding_restricted_call_in_expr(field.value, ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_field_decls(fields ast.CursorList, ctx FreestandingScanContext) string { for i in 0 .. fields.len() { field := fields.at(i) typ_call := freestanding_restricted_call_in_cursor_expr(field.edge(0), ctx) if typ_call != '' { return typ_call } value_call := freestanding_restricted_call_in_cursor_expr(field.edge(1), ctx) if value_call != '' { return value_call } } return '' } fn freestanding_restricted_call_in_field_decls(fields []ast.FieldDecl, ctx FreestandingScanContext) string { for field in fields { typ_call := freestanding_restricted_call_in_expr(field.typ, ctx) if typ_call != '' { return typ_call } value_call := freestanding_restricted_call_in_expr(field.value, ctx) if value_call != '' { return value_call } } return '' } fn freestanding_restricted_call_in_cursor_expr(expr ast.Cursor, ctx FreestandingScanContext) string { if !expr.is_valid() { return '' } return match expr.kind() { .expr_array_init { meta_call := freestanding_restricted_call_in_cursor_edges(expr, 0, 5, ctx) if meta_call != '' { meta_call } else { freestanding_restricted_call_in_cursor_edges(expr, 5, expr.edge_count(), ctx) } } .expr_as_cast { freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) } .expr_cast { freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) } .expr_assoc { base_call := freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) if base_call != '' { base_call } else { freestanding_restricted_call_in_cursor_field_inits(ast.CursorList{ flat: expr.flat parent_id: expr.id offset: 2 }, ctx) } } .expr_call { call_name := freestanding_restricted_builtin_call_name_cursor(expr.edge(0), ctx) if call_name != '' { call_name } else { lhs_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_cursor_edges(expr, 1, expr.edge_count(), ctx) } } } .expr_call_or_cast { call_name := freestanding_restricted_builtin_call_name_cursor(expr.edge(0), ctx) if call_name != '' { call_name } else { lhs_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) } } } .expr_comptime { freestanding_restricted_call_in_cursor_comptime_expr(expr, ctx) } .expr_fn_literal { captured_len := expr.extra_int() capture_call := freestanding_restricted_call_in_cursor_edges(expr, 1, 1 + captured_len, ctx) if capture_call != '' { capture_call } else { freestanding_restricted_call_in_cursor_stmt_edges(expr, 1 + captured_len, expr.edge_count(), ctx) } } .expr_generic_arg_or_index, .expr_index, .expr_infix { lhs_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) } } .expr_generic_args { lhs_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_cursor_edges(expr, 1, expr.edge_count(), ctx) } } .expr_if { cond_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if cond_call != '' { cond_call } else { stmts_call := freestanding_restricted_call_in_cursor_stmt_edges(expr, 2, expr.edge_count(), ctx) if stmts_call != '' { stmts_call } else { freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) } } } .expr_if_guard { freestanding_restricted_call_in_cursor_stmt(expr.edge(0), ctx) } .expr_init { typ_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if typ_call != '' { typ_call } else { freestanding_restricted_call_in_cursor_field_inits(ast.CursorList{ flat: expr.flat parent_id: expr.id offset: 1 }, ctx) } } .expr_keyword_operator { op := unsafe { token.Token(int(expr.aux())) } if op in [.key_go, .key_spawn] { op.str() } else { freestanding_restricted_call_in_cursor_edges(expr, 0, expr.edge_count(), ctx) } } .expr_lambda, .expr_paren, .expr_postfix, .expr_prefix, .expr_selector { freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) } .expr_lock { 'lock' } .expr_map_init { keys_len := expr.extra_int() key_call := freestanding_restricted_call_in_cursor_edges(expr, 1, 1 + keys_len, ctx) if key_call != '' { key_call } else { freestanding_restricted_call_in_cursor_edges(expr, 1 + keys_len, expr.edge_count(), ctx) } } .expr_match { expr_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if expr_call != '' { expr_call } else { freestanding_restricted_call_in_cursor_match_branch_edges(expr, 1, expr.edge_count(), ctx) } } .expr_modifier { kind := unsafe { token.Token(int(expr.aux())) } if kind == .key_shared { 'shared' } else { freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) } } .expr_or { expr_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if expr_call != '' { expr_call } else { freestanding_restricted_call_in_cursor_stmt_edges(expr, 1, expr.edge_count(), ctx) } } .expr_range { start_call := freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) if start_call != '' { start_call } else { freestanding_restricted_call_in_cursor_expr(expr.edge(1), ctx) } } .expr_select { stmt_call := freestanding_restricted_call_in_cursor_stmt(expr.edge(0), ctx) if stmt_call != '' { stmt_call } else { freestanding_restricted_call_in_cursor_stmt_edges(expr, 2, expr.edge_count(), ctx) } } .expr_string_inter { freestanding_restricted_call_in_cursor_string_inters(expr.list_at(1), ctx) } .expr_tuple { freestanding_restricted_call_in_cursor_edges(expr, 0, expr.edge_count(), ctx) } .expr_unsafe { freestanding_restricted_call_in_cursor_stmt_edges(expr, 0, expr.edge_count(), ctx) } .aux_field_init { freestanding_restricted_call_in_cursor_expr(expr.edge(0), ctx) } else { '' } } } fn freestanding_restricted_call_in_cursor_comptime_expr(expr ast.Cursor, ctx FreestandingScanContext) string { inner := expr.edge(0) if inner.is_valid() && inner.kind() == .expr_if { return freestanding_restricted_call_in_cursor_active_comptime_if(inner, ctx) } return freestanding_restricted_call_in_cursor_expr(inner, ctx) } fn freestanding_restricted_call_in_cursor_active_comptime_if(node ast.Cursor, ctx FreestandingScanContext) string { options := freestanding_channel_scan_options(ctx) if flat_comptime_cond_matches(node.edge(0), options) { return freestanding_restricted_call_in_cursor_stmt_edges(node, 2, node.edge_count(), ctx) } else_expr := node.edge(1) if else_expr.is_valid() && else_expr.kind() == .expr_if { if else_expr.edge(0).is_valid() && else_expr.edge(0).kind() == .expr_empty { return freestanding_restricted_call_in_cursor_stmt_edges(else_expr, 2, else_expr.edge_count(), ctx) } return freestanding_restricted_call_in_cursor_active_comptime_if(else_expr, ctx) } return '' } fn freestanding_restricted_call_in_cursor_match_branch_edges(expr ast.Cursor, start int, end int, ctx FreestandingScanContext) string { for i in start .. end { call_name := freestanding_restricted_call_in_cursor_match_branch(expr.edge(i), ctx) if call_name != '' { return call_name } } return '' } fn freestanding_restricted_call_in_cursor_match_branch(branch ast.Cursor, ctx FreestandingScanContext) string { if !branch.is_valid() || branch.kind() != .aux_match_branch { return '' } cond_call := freestanding_restricted_call_in_cursor_expr_list(branch.list_at(0), ctx) if cond_call != '' { return cond_call } return freestanding_restricted_call_in_cursor_stmts(branch.list_at(1), ctx) } fn freestanding_restricted_call_in_cursor_string_inters(inters ast.CursorList, ctx FreestandingScanContext) string { for i in 0 .. inters.len() { inter := inters.at(i) if !inter.is_valid() || inter.kind() != .aux_string_inter { continue } expr_call := freestanding_restricted_call_in_cursor_expr(inter.edge(0), ctx) if expr_call != '' { return expr_call } format_call := freestanding_restricted_call_in_cursor_expr(inter.edge(1), ctx) if format_call != '' { return format_call } } return '' } fn freestanding_restricted_call_in_expr(expr ast.Expr, ctx FreestandingScanContext) string { return match expr { ast.ArrayInitExpr { meta_call := freestanding_restricted_call_in_exprs([expr.typ, expr.init, expr.cap, expr.len, expr.update_expr], ctx) if meta_call != '' { meta_call } else { freestanding_restricted_call_in_exprs(expr.exprs, ctx) } } ast.AsCastExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.AssocExpr { base_call := freestanding_restricted_call_in_expr(expr.expr, ctx) if base_call != '' { base_call } else { freestanding_restricted_call_in_field_inits(expr.fields, ctx) } } ast.CallExpr { call_name := freestanding_restricted_builtin_call_name(expr.lhs, ctx) if call_name != '' { call_name } else { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_exprs(expr.args, ctx) } } } ast.CallOrCastExpr { call_name := freestanding_restricted_builtin_call_name(expr.lhs, ctx) if call_name != '' { call_name } else { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_expr(expr.expr, ctx) } } } ast.CastExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.ComptimeExpr { freestanding_restricted_call_in_comptime_expr(expr, ctx) } ast.FieldInit { freestanding_restricted_call_in_expr(expr.value, ctx) } ast.FnLiteral { capture_call := freestanding_restricted_call_in_exprs(expr.captured_vars, ctx) if capture_call != '' { capture_call } else { freestanding_restricted_call_in_stmts(expr.stmts, ctx) } } ast.GenericArgOrIndexExpr { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_expr(expr.expr, ctx) } } ast.GenericArgs { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_exprs(expr.args, ctx) } } ast.IfExpr { cond_call := freestanding_restricted_call_in_expr(expr.cond, ctx) if cond_call != '' { cond_call } else { stmts_call := freestanding_restricted_call_in_stmts(expr.stmts, ctx) if stmts_call != '' { stmts_call } else { freestanding_restricted_call_in_expr(expr.else_expr, ctx) } } } ast.IfGuardExpr { freestanding_restricted_call_in_stmt(ast.Stmt(expr.stmt), ctx) } ast.IndexExpr { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_expr(expr.expr, ctx) } } ast.InfixExpr { lhs_call := freestanding_restricted_call_in_expr(expr.lhs, ctx) if lhs_call != '' { lhs_call } else { freestanding_restricted_call_in_expr(expr.rhs, ctx) } } ast.InitExpr { typ_call := freestanding_restricted_call_in_expr(expr.typ, ctx) if typ_call != '' { typ_call } else { freestanding_restricted_call_in_field_inits(expr.fields, ctx) } } ast.KeywordOperator { if expr.op in [.key_go, .key_spawn] { expr.op.str() } else { freestanding_restricted_call_in_exprs(expr.exprs, ctx) } } ast.LambdaExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.LockExpr { 'lock' } ast.MapInitExpr { key_call := freestanding_restricted_call_in_exprs(expr.keys, ctx) if key_call != '' { key_call } else { freestanding_restricted_call_in_exprs(expr.vals, ctx) } } ast.MatchExpr { expr_call := freestanding_restricted_call_in_expr(expr.expr, ctx) if expr_call != '' { expr_call } else { freestanding_restricted_call_in_match_branches(expr.branches, ctx) } } ast.ModifierExpr { if expr.kind == .key_shared { 'shared' } else { freestanding_restricted_call_in_expr(expr.expr, ctx) } } ast.OrExpr { expr_call := freestanding_restricted_call_in_expr(expr.expr, ctx) if expr_call != '' { expr_call } else { freestanding_restricted_call_in_stmts(expr.stmts, ctx) } } ast.ParenExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.PostfixExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.PrefixExpr { freestanding_restricted_call_in_expr(expr.expr, ctx) } ast.RangeExpr { start_call := freestanding_restricted_call_in_expr(expr.start, ctx) if start_call != '' { start_call } else { freestanding_restricted_call_in_expr(expr.end, ctx) } } ast.SelectExpr { stmt_call := freestanding_restricted_call_in_stmt(expr.stmt, ctx) if stmt_call != '' { stmt_call } else { freestanding_restricted_call_in_stmts(expr.stmts, ctx) } } ast.SelectorExpr { freestanding_restricted_call_in_expr(expr.lhs, ctx) } ast.StringInterLiteral { freestanding_restricted_call_in_string_inters(expr.inters, ctx) } ast.Tuple { freestanding_restricted_call_in_exprs(expr.exprs, ctx) } ast.UnsafeExpr { freestanding_restricted_call_in_stmts(expr.stmts, ctx) } else { '' } } } fn freestanding_restricted_call_in_comptime_expr(expr ast.ComptimeExpr, ctx FreestandingScanContext) string { if expr.expr is ast.IfExpr { return freestanding_restricted_call_in_active_comptime_if(expr.expr, ctx) } return freestanding_restricted_call_in_expr(expr.expr, ctx) } fn freestanding_restricted_call_in_active_comptime_if(node ast.IfExpr, ctx FreestandingScanContext) string { if ast_comptime_cond_matches_with_explicit(node.cond, ctx.user_defines, ctx.explicit_defines, ctx.target_os) { return freestanding_restricted_call_in_stmts(node.stmts, ctx) } if node.else_expr is ast.IfExpr { if node.else_expr.cond is ast.EmptyExpr { return freestanding_restricted_call_in_stmts(node.else_expr.stmts, ctx) } return freestanding_restricted_call_in_active_comptime_if(node.else_expr, ctx) } return '' } fn freestanding_restricted_call_in_match_branches(branches []ast.MatchBranch, ctx FreestandingScanContext) string { for branch in branches { cond_call := freestanding_restricted_call_in_exprs(branch.cond, ctx) if cond_call != '' { return cond_call } stmts_call := freestanding_restricted_call_in_stmts(branch.stmts, ctx) if stmts_call != '' { return stmts_call } } return '' } fn freestanding_restricted_call_in_string_inters(inters []ast.StringInter, ctx FreestandingScanContext) string { for inter in inters { expr_call := freestanding_restricted_call_in_expr(inter.expr, ctx) if expr_call != '' { return expr_call } format_call := freestanding_restricted_call_in_expr(inter.format_expr, ctx) if format_call != '' { return format_call } } return '' } fn freestanding_restricted_builtin_call_name(lhs ast.Expr, ctx FreestandingScanContext) string { if lhs is ast.Ident { return freestanding_output_builtin_call_name(lhs.name, ctx) } return '' } fn freestanding_restricted_builtin_call_name_cursor(lhs ast.Cursor, ctx FreestandingScanContext) string { if lhs.is_valid() && lhs.kind() == .expr_ident { return freestanding_output_builtin_call_name(lhs.name(), ctx) } return '' }