1217 lines
39 KiB
V
1217 lines
39 KiB
V
// 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 os
|
|
import v2.ast
|
|
import v2.parser
|
|
import v2.pref as vpref
|
|
import v2.token
|
|
|
|
fn should_expand_single_file_input(input string) bool {
|
|
if os.file_name(input).ends_with('_test.v') {
|
|
return true
|
|
}
|
|
module_name := file_module_name(input) or { return false }
|
|
return module_name != 'main'
|
|
}
|
|
|
|
fn is_module_line_space(ch u8) bool {
|
|
return ch == ` ` || ch == `\t` || ch == `\v` || ch == `\f`
|
|
}
|
|
|
|
fn is_module_source_space(ch u8) bool {
|
|
return is_module_line_space(ch) || ch == `\n` || ch == `\r`
|
|
}
|
|
|
|
fn file_module_name(path string) ?string {
|
|
content := os.read_file(path) or { return none }
|
|
mut start := 0
|
|
for start < content.len {
|
|
if is_module_source_space(content[start]) {
|
|
start++
|
|
continue
|
|
}
|
|
if start + 1 < content.len && content[start] == `/` && content[start + 1] == `/` {
|
|
start += 2
|
|
for start < content.len && content[start] != `\n` && content[start] != `\r` {
|
|
start++
|
|
}
|
|
continue
|
|
}
|
|
if start + 1 < content.len && content[start] == `/` && content[start + 1] == `*` {
|
|
start += 2
|
|
for start + 1 < content.len {
|
|
if content[start] == `*` && content[start + 1] == `/` {
|
|
start += 2
|
|
break
|
|
}
|
|
start++
|
|
}
|
|
if start >= content.len {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if content.len - start >= 7 && content[start] == `m` && content[start + 1] == `o`
|
|
&& content[start + 2] == `d` && content[start + 3] == `u` && content[start + 4] == `l`
|
|
&& content[start + 5] == `e` && is_module_line_space(content[start + 6]) {
|
|
mut name_start := start + 7
|
|
for name_start < content.len && is_module_line_space(content[name_start]) {
|
|
name_start++
|
|
}
|
|
mut name_end := name_start
|
|
for name_end < content.len && !is_module_source_space(content[name_end]) {
|
|
name_end++
|
|
}
|
|
if name_start < name_end {
|
|
return content[name_start..name_end]
|
|
}
|
|
return none
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn directory_primary_module(files []string) string {
|
|
for file in files {
|
|
if module_name := file_module_name(file) {
|
|
return module_name
|
|
}
|
|
}
|
|
return 'main'
|
|
}
|
|
|
|
fn collect_same_module_subdir_files(root string, dir string, module_name string, user_defines []string, target_os string, mut files []string, mut seen map[string]bool) {
|
|
for entry in list_dir_entries(dir) {
|
|
if entry == '' || entry.starts_with('.') {
|
|
continue
|
|
}
|
|
path := os.join_path(dir, entry)
|
|
if !os.is_dir(path) {
|
|
continue
|
|
}
|
|
if path != root && os.exists(os.join_path(path, 'v.mod')) {
|
|
continue
|
|
}
|
|
for file in get_v_files_from_dir(path, user_defines, target_os) {
|
|
if file in seen {
|
|
continue
|
|
}
|
|
if file_module_name(file) or { 'main' } != module_name {
|
|
continue
|
|
}
|
|
files << file
|
|
seen[file] = true
|
|
}
|
|
collect_same_module_subdir_files(root, path, module_name, user_defines, target_os, mut
|
|
files, mut seen)
|
|
}
|
|
}
|
|
|
|
fn get_user_v_files_from_dir(dir string, user_defines []string, target_os string) []string {
|
|
mut files := get_v_files_from_dir(dir, user_defines, target_os)
|
|
module_name := directory_primary_module(files)
|
|
mut seen := map[string]bool{}
|
|
for file in files {
|
|
seen[file] = true
|
|
}
|
|
collect_same_module_subdir_files(dir, dir, module_name, user_defines, target_os, mut files, mut
|
|
seen)
|
|
return files
|
|
}
|
|
|
|
fn ast_comptime_flag_matches(name string, user_defines []string, target_os string) bool {
|
|
lower_name := name.to_lower()
|
|
match lower_name {
|
|
'macos', 'darwin', 'mac', 'linux', 'windows', 'bsd', 'freebsd', 'openbsd', 'netbsd',
|
|
'dragonfly', 'android', 'termux', 'ios', 'solaris', 'qnx', 'serenity', 'plan9', 'vinix',
|
|
'none' {
|
|
return flag_os_matches(lower_name, target_os)
|
|
}
|
|
'cross' {
|
|
return flag_os_matches(lower_name, target_os) || lower_name in user_defines
|
|
}
|
|
'freestanding', 'bare' {
|
|
return lower_name in user_defines
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return lower_name in user_defines
|
|
}
|
|
|
|
fn ast_comptime_cond_matches(cond ast.Expr, user_defines []string, target_os string) bool {
|
|
return ast_comptime_cond_matches_with_explicit(cond, user_defines, user_defines, target_os)
|
|
}
|
|
|
|
fn ast_comptime_cond_matches_with_explicit(cond ast.Expr, user_defines []string, explicit_user_defines []string, target_os string) bool {
|
|
return ast_comptime_cond_matches_with_options(cond, user_defines, explicit_user_defines,
|
|
target_os, true)
|
|
}
|
|
|
|
fn ast_comptime_cond_matches_with_options(cond ast.Expr, user_defines []string, explicit_user_defines []string, target_os string, allow_pkgconfig bool) bool {
|
|
match cond {
|
|
ast.Ident {
|
|
return ast_comptime_flag_matches(cond.name, user_defines, target_os)
|
|
}
|
|
ast.ComptimeExpr {
|
|
return ast_comptime_cond_matches_with_options(cond.expr, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig)
|
|
}
|
|
ast.CallExpr {
|
|
if pkg_name := ast_pkgconfig_call_name(cond) {
|
|
if !allow_pkgconfig {
|
|
return false
|
|
}
|
|
return vpref.comptime_pkgconfig_value(pkg_name)
|
|
}
|
|
}
|
|
ast.CallOrCastExpr {
|
|
if pkg_name := ast_pkgconfig_call_name(cond) {
|
|
if !allow_pkgconfig {
|
|
return false
|
|
}
|
|
return vpref.comptime_pkgconfig_value(pkg_name)
|
|
}
|
|
}
|
|
ast.PrefixExpr {
|
|
if cond.op == .not {
|
|
return !ast_comptime_cond_matches_with_options(cond.expr, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig)
|
|
}
|
|
}
|
|
ast.InfixExpr {
|
|
if cond.op == .and {
|
|
return
|
|
ast_comptime_cond_matches_with_options(cond.lhs, user_defines, explicit_user_defines, target_os, allow_pkgconfig)
|
|
&& ast_comptime_cond_matches_with_options(cond.rhs, user_defines, explicit_user_defines, target_os, allow_pkgconfig)
|
|
}
|
|
if cond.op == .logical_or {
|
|
return
|
|
ast_comptime_cond_matches_with_options(cond.lhs, user_defines, explicit_user_defines, target_os, allow_pkgconfig)
|
|
|| ast_comptime_cond_matches_with_options(cond.rhs, user_defines, explicit_user_defines, target_os, allow_pkgconfig)
|
|
}
|
|
}
|
|
ast.PostfixExpr {
|
|
if cond.op == .question && cond.expr is ast.Ident {
|
|
return vpref.comptime_optional_define_value(cond.expr.name, user_defines,
|
|
explicit_user_defines)
|
|
}
|
|
}
|
|
ast.ParenExpr {
|
|
return ast_comptime_cond_matches_with_options(cond.expr, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig)
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fn ast_pkgconfig_call_name(expr ast.Expr) ?string {
|
|
match expr {
|
|
ast.CallExpr {
|
|
if expr.lhs is ast.Ident && expr.lhs.name == 'pkgconfig' && expr.args.len == 1 {
|
|
return ast_string_literal_value(expr.args[0])
|
|
}
|
|
}
|
|
ast.CallOrCastExpr {
|
|
if expr.lhs is ast.Ident && expr.lhs.name == 'pkgconfig' {
|
|
return ast_string_literal_value(expr.expr)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return none
|
|
}
|
|
|
|
fn ast_string_literal_value(expr ast.Expr) ?string {
|
|
if expr is ast.StringLiteral {
|
|
return unquote_ast_string_literal_value(expr.value)
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn unquote_ast_string_literal_value(value string) string {
|
|
if value.len >= 2 && ((value[0] == `"` && value[value.len - 1] == `"`)
|
|
|| (value[0] == `'` && value[value.len - 1] == `'`)) {
|
|
return value[1..value.len - 1]
|
|
}
|
|
return value
|
|
}
|
|
|
|
fn collect_active_imports_from_if_expr(node ast.IfExpr, user_defines []string, target_os string, mut imports []ast.ImportStmt) {
|
|
collect_active_imports_from_if_expr_with_explicit(node, user_defines, user_defines, target_os, mut
|
|
imports)
|
|
}
|
|
|
|
fn collect_active_imports_from_if_expr_with_explicit(node ast.IfExpr, user_defines []string, explicit_user_defines []string, target_os string, mut imports []ast.ImportStmt) {
|
|
collect_active_imports_from_if_expr_with_options(node, user_defines, explicit_user_defines,
|
|
target_os, true, mut imports)
|
|
}
|
|
|
|
fn collect_active_imports_from_if_expr_with_options(node ast.IfExpr, user_defines []string, explicit_user_defines []string, target_os string, allow_pkgconfig bool, mut imports []ast.ImportStmt) {
|
|
if ast_comptime_cond_matches_with_options(node.cond, user_defines, explicit_user_defines,
|
|
target_os, allow_pkgconfig)
|
|
{
|
|
collect_active_imports_from_stmts_with_options(node.stmts, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig, mut imports)
|
|
return
|
|
}
|
|
match node.else_expr {
|
|
ast.IfExpr {
|
|
if node.else_expr.cond is ast.EmptyExpr {
|
|
collect_active_imports_from_stmts_with_options(node.else_expr.stmts, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig, mut imports)
|
|
} else {
|
|
collect_active_imports_from_if_expr_with_options(node.else_expr, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig, mut imports)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
|
|
fn collect_active_imports_from_stmts(stmts []ast.Stmt, user_defines []string, target_os string, mut imports []ast.ImportStmt) {
|
|
collect_active_imports_from_stmts_with_explicit(stmts, user_defines, user_defines, target_os, mut
|
|
imports)
|
|
}
|
|
|
|
fn collect_active_imports_from_stmts_with_explicit(stmts []ast.Stmt, user_defines []string, explicit_user_defines []string, target_os string, mut imports []ast.ImportStmt) {
|
|
collect_active_imports_from_stmts_with_options(stmts, user_defines, explicit_user_defines,
|
|
target_os, true, mut imports)
|
|
}
|
|
|
|
fn collect_active_imports_from_stmts_with_options(stmts []ast.Stmt, user_defines []string, explicit_user_defines []string, target_os string, allow_pkgconfig bool, mut imports []ast.ImportStmt) {
|
|
for stmt in stmts {
|
|
match stmt {
|
|
ast.ImportStmt {
|
|
imports << stmt
|
|
}
|
|
ast.ExprStmt {
|
|
if stmt.expr is ast.ComptimeExpr && stmt.expr.expr is ast.IfExpr {
|
|
collect_active_imports_from_if_expr_with_options(stmt.expr.expr, user_defines,
|
|
explicit_user_defines, target_os, allow_pkgconfig, mut imports)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn imports_contain(imports []ast.ImportStmt, name string) bool {
|
|
for imp in imports {
|
|
if imp.name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn add_implicit_sync_import_if_needed(mut imports []ast.ImportStmt, current_module string, uses_channel bool) {
|
|
if !uses_channel || current_module == 'sync' || imports_contain(imports, 'sync') {
|
|
return
|
|
}
|
|
imports << ast.ImportStmt{
|
|
name: 'sync'
|
|
}
|
|
}
|
|
|
|
fn field_decl_uses_channel(field ast.FieldDecl) bool {
|
|
return type_expr_uses_channel(field.typ) || expr_type_slots_use_channel(field.value)
|
|
}
|
|
|
|
fn fields_use_channel(fields []ast.FieldDecl) bool {
|
|
for field in fields {
|
|
if field_decl_uses_channel(field) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
struct ChannelScanOptions {
|
|
user_defines []string
|
|
explicit_user_defines []string
|
|
target_os string
|
|
allow_pkgconfig bool
|
|
filter_comptime bool
|
|
}
|
|
|
|
fn field_inits_use_channel(fields []ast.FieldInit) bool {
|
|
return field_inits_use_channel_with_options(fields, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn field_inits_use_channel_with_options(fields []ast.FieldInit, options ChannelScanOptions) bool {
|
|
for field in fields {
|
|
if expr_type_slots_use_channel_with_options(field.value, options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn fn_type_uses_channel(typ ast.FnType) bool {
|
|
for gp in typ.generic_params {
|
|
if type_expr_uses_channel(gp) {
|
|
return true
|
|
}
|
|
}
|
|
for param in typ.params {
|
|
if type_expr_uses_channel(param.typ) {
|
|
return true
|
|
}
|
|
}
|
|
return type_expr_uses_channel(typ.return_type)
|
|
}
|
|
|
|
fn stmts_use_channel(stmts []ast.Stmt) bool {
|
|
return stmts_use_channel_with_options(stmts, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn stmts_use_channel_with_options(stmts []ast.Stmt, options ChannelScanOptions) bool {
|
|
for stmt in stmts {
|
|
if stmt_uses_channel_with_options(stmt, options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn stmt_uses_channel(stmt ast.Stmt) bool {
|
|
return stmt_uses_channel_with_options(stmt, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn fn_decl_body_is_active_for_channel_scan(decl ast.FnDecl, options ChannelScanOptions) bool {
|
|
for attr in decl.attributes {
|
|
if attr.comptime_cond is ast.EmptyExpr {
|
|
continue
|
|
}
|
|
if !ast_comptime_cond_matches_with_options(attr.comptime_cond, options.user_defines,
|
|
options.explicit_user_defines, options.target_os, options.allow_pkgconfig) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
fn stmt_uses_channel_with_options(stmt ast.Stmt, options ChannelScanOptions) bool {
|
|
match stmt {
|
|
ast.AssertStmt {
|
|
return expr_type_slots_use_channel_with_options(stmt.expr, options)
|
|
|| expr_type_slots_use_channel_with_options(stmt.extra, options)
|
|
}
|
|
ast.AssignStmt {
|
|
return exprs_type_slots_use_channel_with_options(stmt.lhs, options)
|
|
|| exprs_type_slots_use_channel_with_options(stmt.rhs, options)
|
|
}
|
|
ast.BlockStmt {
|
|
return stmts_use_channel_with_options(stmt.stmts, options)
|
|
}
|
|
ast.ComptimeStmt {
|
|
return stmt_uses_channel_with_options(stmt.stmt, options)
|
|
}
|
|
ast.ConstDecl {
|
|
return field_inits_use_channel_with_options(stmt.fields, options)
|
|
}
|
|
ast.DeferStmt {
|
|
return stmts_use_channel_with_options(stmt.stmts, options)
|
|
}
|
|
ast.EnumDecl {
|
|
for field in stmt.fields {
|
|
if expr_type_slots_use_channel_with_options(field.value, options) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
ast.ExprStmt {
|
|
if options.filter_comptime && stmt.expr is ast.ComptimeExpr
|
|
&& stmt.expr.expr is ast.IfExpr {
|
|
return comptime_if_expr_uses_channel_with_options(stmt.expr.expr, options)
|
|
}
|
|
return expr_type_slots_use_channel_with_options(stmt.expr, options)
|
|
}
|
|
ast.FnDecl {
|
|
signature_uses_channel := type_expr_uses_channel(stmt.receiver.typ)
|
|
|| fn_type_uses_channel(stmt.typ)
|
|
if !fn_decl_body_is_active_for_channel_scan(stmt, options) {
|
|
return signature_uses_channel
|
|
}
|
|
return signature_uses_channel || stmts_use_channel_with_options(stmt.stmts, options)
|
|
}
|
|
ast.ForInStmt {
|
|
return expr_type_slots_use_channel_with_options(stmt.expr, options)
|
|
}
|
|
ast.ForStmt {
|
|
return stmt_uses_channel_with_options(stmt.init, options)
|
|
|| expr_type_slots_use_channel_with_options(stmt.cond, options)
|
|
|| stmt_uses_channel_with_options(stmt.post, options)
|
|
|| stmts_use_channel_with_options(stmt.stmts, options)
|
|
}
|
|
ast.GlobalDecl {
|
|
return fields_use_channel(stmt.fields)
|
|
}
|
|
ast.InterfaceDecl {
|
|
return fields_use_channel(stmt.fields) || type_exprs_use_channel(stmt.embedded)
|
|
}
|
|
ast.LabelStmt {
|
|
return stmt_uses_channel_with_options(stmt.stmt, options)
|
|
}
|
|
ast.ReturnStmt {
|
|
return exprs_type_slots_use_channel_with_options(stmt.exprs, options)
|
|
}
|
|
ast.StructDecl {
|
|
return type_exprs_use_channel(stmt.implements) || type_exprs_use_channel(stmt.embedded)
|
|
|| type_exprs_use_channel(stmt.generic_params) || fields_use_channel(stmt.fields)
|
|
}
|
|
ast.TypeDecl {
|
|
return type_expr_uses_channel(stmt.base_type) || type_exprs_use_channel(stmt.variants)
|
|
|| type_exprs_use_channel(stmt.generic_params)
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fn type_exprs_use_channel(exprs []ast.Expr) bool {
|
|
for expr in exprs {
|
|
if type_expr_uses_channel(expr) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn exprs_type_slots_use_channel(exprs []ast.Expr) bool {
|
|
return exprs_type_slots_use_channel_with_options(exprs, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn exprs_type_slots_use_channel_with_options(exprs []ast.Expr, options ChannelScanOptions) bool {
|
|
for expr in exprs {
|
|
if expr_type_slots_use_channel_with_options(expr, options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn string_inters_use_channel(inters []ast.StringInter) bool {
|
|
return string_inters_use_channel_with_options(inters, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn string_inters_use_channel_with_options(inters []ast.StringInter, options ChannelScanOptions) bool {
|
|
for inter in inters {
|
|
if expr_type_slots_use_channel_with_options(inter.expr, options)
|
|
|| expr_type_slots_use_channel_with_options(inter.format_expr, options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn comptime_if_expr_uses_channel_with_options(node ast.IfExpr, options ChannelScanOptions) bool {
|
|
if ast_comptime_cond_matches_with_options(node.cond, options.user_defines,
|
|
options.explicit_user_defines, options.target_os, options.allow_pkgconfig)
|
|
{
|
|
return stmts_use_channel_with_options(node.stmts, options)
|
|
}
|
|
match node.else_expr {
|
|
ast.IfExpr {
|
|
if node.else_expr.cond is ast.EmptyExpr {
|
|
return stmts_use_channel_with_options(node.else_expr.stmts, options)
|
|
}
|
|
return comptime_if_expr_uses_channel_with_options(node.else_expr, options)
|
|
}
|
|
else {
|
|
return expr_type_slots_use_channel_with_options(node.else_expr, options)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn expr_type_slots_use_channel(expr ast.Expr) bool {
|
|
return expr_type_slots_use_channel_with_options(expr, ChannelScanOptions{
|
|
allow_pkgconfig: true
|
|
})
|
|
}
|
|
|
|
fn expr_type_slots_use_channel_with_options(expr ast.Expr, options ChannelScanOptions) bool {
|
|
match expr {
|
|
ast.ArrayInitExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| expr_type_slots_use_channel_with_options(expr.init, options)
|
|
|| exprs_type_slots_use_channel_with_options(expr.exprs, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.cap, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.len, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.update_expr, options)
|
|
}
|
|
ast.AsCastExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.AssocExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
|| field_inits_use_channel_with_options(expr.fields, options)
|
|
}
|
|
ast.CallExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.lhs, options)
|
|
|| exprs_type_slots_use_channel_with_options(expr.args, options)
|
|
}
|
|
ast.CallOrCastExpr {
|
|
return type_expr_uses_channel(expr.lhs)
|
|
|| expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.CastExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.ComptimeExpr {
|
|
if options.filter_comptime && expr.expr is ast.IfExpr {
|
|
return comptime_if_expr_uses_channel_with_options(expr.expr, options)
|
|
}
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.FieldInit {
|
|
return expr_type_slots_use_channel_with_options(expr.value, options)
|
|
}
|
|
ast.FnLiteral {
|
|
return fn_type_uses_channel(expr.typ)
|
|
|| stmts_use_channel_with_options(expr.stmts, options)
|
|
}
|
|
ast.GenericArgOrIndexExpr {
|
|
return type_expr_uses_channel(expr.lhs) || type_expr_uses_channel(expr.expr)
|
|
}
|
|
ast.GenericArgs {
|
|
return type_expr_uses_channel(expr.lhs) || type_exprs_use_channel(expr.args)
|
|
}
|
|
ast.IfExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.cond, options)
|
|
|| stmts_use_channel_with_options(expr.stmts, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.else_expr, options)
|
|
}
|
|
ast.IfGuardExpr {
|
|
return stmt_uses_channel_with_options(ast.Stmt(expr.stmt), options)
|
|
}
|
|
ast.IndexExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.lhs, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.InfixExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.lhs, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.rhs, options)
|
|
}
|
|
ast.InitExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| field_inits_use_channel_with_options(expr.fields, options)
|
|
}
|
|
ast.KeywordOperator {
|
|
return exprs_type_slots_use_channel_with_options(expr.exprs, options)
|
|
}
|
|
ast.LambdaExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.ModifierExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.LockExpr {
|
|
return exprs_type_slots_use_channel_with_options(expr.lock_exprs, options)
|
|
|| exprs_type_slots_use_channel_with_options(expr.rlock_exprs, options)
|
|
|| stmts_use_channel_with_options(expr.stmts, options)
|
|
}
|
|
ast.MapInitExpr {
|
|
return type_expr_uses_channel(expr.typ)
|
|
|| exprs_type_slots_use_channel_with_options(expr.keys, options)
|
|
|| exprs_type_slots_use_channel_with_options(expr.vals, options)
|
|
}
|
|
ast.MatchExpr {
|
|
if expr_type_slots_use_channel_with_options(expr.expr, options) {
|
|
return true
|
|
}
|
|
for branch in expr.branches {
|
|
if exprs_type_slots_use_channel_with_options(branch.cond, options)
|
|
|| stmts_use_channel_with_options(branch.stmts, options) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
ast.OrExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
|| stmts_use_channel_with_options(expr.stmts, options)
|
|
}
|
|
ast.ParenExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.PostfixExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.PrefixExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.RangeExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.start, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.end, options)
|
|
}
|
|
ast.SelectExpr {
|
|
return stmt_uses_channel_with_options(expr.stmt, options)
|
|
|| stmts_use_channel_with_options(expr.stmts, options)
|
|
|| expr_type_slots_use_channel_with_options(expr.next, options)
|
|
}
|
|
ast.SelectorExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.lhs, options)
|
|
}
|
|
ast.SqlExpr {
|
|
return expr_type_slots_use_channel_with_options(expr.expr, options)
|
|
}
|
|
ast.StringInterLiteral {
|
|
return string_inters_use_channel_with_options(expr.inters, options)
|
|
}
|
|
ast.Tuple {
|
|
return exprs_type_slots_use_channel_with_options(expr.exprs, options)
|
|
}
|
|
ast.UnsafeExpr {
|
|
return stmts_use_channel_with_options(expr.stmts, options)
|
|
}
|
|
ast.Type {
|
|
return type_expr_uses_channel(expr)
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fn type_expr_uses_channel(expr ast.Expr) bool {
|
|
match expr {
|
|
ast.Type {
|
|
match expr {
|
|
ast.ChannelType {
|
|
return true
|
|
}
|
|
ast.ArrayType {
|
|
return type_expr_uses_channel(expr.elem_type)
|
|
}
|
|
ast.ArrayFixedType {
|
|
return type_expr_uses_channel(expr.elem_type)
|
|
}
|
|
ast.FnType {
|
|
return fn_type_uses_channel(expr)
|
|
}
|
|
ast.GenericType {
|
|
return type_expr_uses_channel(expr.name) || type_exprs_use_channel(expr.params)
|
|
}
|
|
ast.MapType {
|
|
return type_expr_uses_channel(expr.key_type)
|
|
|| type_expr_uses_channel(expr.value_type)
|
|
}
|
|
ast.OptionType {
|
|
return type_expr_uses_channel(expr.base_type)
|
|
}
|
|
ast.PointerType {
|
|
return type_expr_uses_channel(expr.base_type)
|
|
}
|
|
ast.ResultType {
|
|
return type_expr_uses_channel(expr.base_type)
|
|
}
|
|
ast.ThreadType {
|
|
return type_expr_uses_channel(expr.elem_type)
|
|
}
|
|
ast.TupleType {
|
|
return type_exprs_use_channel(expr.types)
|
|
}
|
|
ast.AnonStructType {
|
|
return type_exprs_use_channel(expr.generic_params)
|
|
|| type_exprs_use_channel(expr.embedded) || fields_use_channel(expr.fields)
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fn active_file_imports(file ast.File, user_defines []string, target_os string) []ast.ImportStmt {
|
|
return active_file_imports_with_explicit(file, user_defines, user_defines, target_os)
|
|
}
|
|
|
|
fn active_file_imports_with_explicit(file ast.File, user_defines []string, explicit_user_defines []string, target_os string) []ast.ImportStmt {
|
|
return active_file_imports_with_options(file, user_defines, explicit_user_defines, target_os,
|
|
true)
|
|
}
|
|
|
|
fn active_file_imports_with_options(file ast.File, user_defines []string, explicit_user_defines []string, target_os string, allow_pkgconfig bool) []ast.ImportStmt {
|
|
mut imports := file.imports.clone()
|
|
collect_active_imports_from_stmts_with_options(file.stmts, user_defines, explicit_user_defines,
|
|
target_os, allow_pkgconfig, mut imports)
|
|
add_implicit_sync_import_if_needed(mut imports, file.mod, stmts_use_channel_with_options(file.stmts, ChannelScanOptions{
|
|
user_defines: user_defines
|
|
explicit_user_defines: explicit_user_defines
|
|
target_os: target_os
|
|
allow_pkgconfig: allow_pkgconfig
|
|
filter_comptime: true
|
|
}))
|
|
return imports
|
|
}
|
|
|
|
// active_file_imports_from_flat mirrors active_file_imports but reads the
|
|
// import list and top-level stmts straight from the FlatAst, so import
|
|
// discovery can run without rehydrating an ast.File.
|
|
fn active_file_imports_from_flat(flat &ast.FlatAst, ff ast.FlatFile, user_defines []string, explicit_user_defines []string, target_os string) []ast.ImportStmt {
|
|
return active_file_imports_from_flat_with_options(flat, ff, user_defines,
|
|
explicit_user_defines, target_os, true)
|
|
}
|
|
|
|
fn active_file_imports_from_flat_with_options(flat &ast.FlatAst, ff ast.FlatFile, user_defines []string, explicit_user_defines []string, target_os string, allow_pkgconfig bool) []ast.ImportStmt {
|
|
// s253: walk the FlatAst via cursors instead of rehydrating the whole file to
|
|
// legacy ast.Stmt with a top-level decode. The decode-then-walk path (a) costs a
|
|
// full legacy-AST materialisation per file just to answer "does this file use a
|
|
// channel / which comptime imports are active?", and (b) crashes on the arm64
|
|
// self-host — the legacy `*_use_channel` walkers pass `[]Expr`/sum types by
|
|
// value through deep recursion over decoded nodes, hitting the arm64
|
|
// chained-access bug. Cursor reads are plain int-array lookups, so the walk is
|
|
// cheap and arm64-safe; only the tiny comptime-condition sub-exprs are decoded
|
|
// (reusing the exact legacy evaluator) so semantics match the AST path.
|
|
mut imports := ast.Cursor{
|
|
flat: unsafe { flat }
|
|
id: ff.file_id
|
|
}.list_at(1).import_stmts()
|
|
options := ChannelScanOptions{
|
|
user_defines: user_defines
|
|
explicit_user_defines: explicit_user_defines
|
|
target_os: target_os
|
|
allow_pkgconfig: allow_pkgconfig
|
|
filter_comptime: true
|
|
}
|
|
file_node := ast.Cursor{
|
|
flat: unsafe { flat }
|
|
id: ff.file_id
|
|
}
|
|
stmts := file_node.list_at(2) // file node edge 2 = top-level statement list
|
|
flat_collect_active_imports(stmts, options, mut imports)
|
|
module_name := if ff.mod_idx >= 0 && ff.mod_idx < flat.strings.len {
|
|
flat.strings[ff.mod_idx]
|
|
} else {
|
|
'main'
|
|
}
|
|
add_implicit_sync_import_if_needed(mut imports, module_name, flat_stmts_use_channel(stmts,
|
|
options))
|
|
return imports
|
|
}
|
|
|
|
// flat_collect_active_imports is the cursor-native mirror of
|
|
// `collect_active_imports_from_stmts_with_options`: it appends top-level
|
|
// `import` statements and the imports of active comptime `$if` branches.
|
|
fn flat_collect_active_imports(stmts ast.CursorList, options ChannelScanOptions, mut imports []ast.ImportStmt) {
|
|
for i in 0 .. stmts.len() {
|
|
flat_collect_active_imports_stmt(stmts.at(i), options, mut imports)
|
|
}
|
|
}
|
|
|
|
fn flat_collect_active_imports_stmt(s ast.Cursor, options ChannelScanOptions, mut imports []ast.ImportStmt) {
|
|
match s.kind() {
|
|
.stmt_import {
|
|
imports << s.import_stmt()
|
|
}
|
|
.stmt_expr {
|
|
inner := s.edge(0)
|
|
if inner.kind() == .expr_comptime {
|
|
cif := inner.edge(0)
|
|
if cif.kind() == .expr_if {
|
|
flat_collect_active_imports_from_if(cif, options, mut imports)
|
|
}
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
|
|
// flat_collect_active_imports_from_if mirrors
|
|
// `collect_active_imports_from_if_expr_with_options`. expr_if layout:
|
|
// edge0 = cond, edge1 = else_expr, edge2.. = then-branch statements.
|
|
fn flat_collect_active_imports_from_if(if_c ast.Cursor, options ChannelScanOptions, mut imports []ast.ImportStmt) {
|
|
if flat_comptime_cond_matches(if_c.edge(0), options) {
|
|
for i in 2 .. if_c.edge_count() {
|
|
flat_collect_active_imports_stmt(if_c.edge(i), options, mut imports)
|
|
}
|
|
return
|
|
}
|
|
else_c := if_c.edge(1)
|
|
if else_c.kind() == .expr_if {
|
|
if else_c.edge(0).kind() == .expr_empty {
|
|
for i in 2 .. else_c.edge_count() {
|
|
flat_collect_active_imports_stmt(else_c.edge(i), options, mut imports)
|
|
}
|
|
} else {
|
|
flat_collect_active_imports_from_if(else_c, options, mut imports)
|
|
}
|
|
}
|
|
}
|
|
|
|
// flat_stmts_use_channel is the cursor-native mirror of
|
|
// `stmts_use_channel_with_options`.
|
|
fn flat_stmts_use_channel(stmts ast.CursorList, options ChannelScanOptions) bool {
|
|
for i in 0 .. stmts.len() {
|
|
if flat_node_uses_channel(stmts.at(i), options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// flat_node_uses_channel returns true if the active subtree rooted at `c`
|
|
// references a channel type. Channel types are always encoded as `.typ_channel`
|
|
// nodes, so a generic structural recursion over every edge finds them without
|
|
// per-kind edge knowledge (aux_list nodes are transparent — their edges are the
|
|
// list items). Two arms must prune to match the legacy scan's comptime
|
|
// filtering: a FnDecl body is scanned only when its comptime attributes keep it
|
|
// active, and a comptime `$if` (ComptimeExpr wrapping an IfExpr) scans only the
|
|
// branch selected by the condition.
|
|
fn flat_node_uses_channel(c ast.Cursor, options ChannelScanOptions) bool {
|
|
if !c.is_valid() {
|
|
return false
|
|
}
|
|
k := c.kind()
|
|
if k == .typ_channel {
|
|
return true
|
|
}
|
|
if k == .stmt_fn_decl {
|
|
// stmt_fn_decl layout: edge0 = receiver, edge1 = fn type, edge2 = attrs,
|
|
// edge3 = body. Signature (receiver + fn type) is always active; the body
|
|
// only when comptime-active. Attributes never hold a channel type (matches
|
|
// the legacy FnDecl arm, which scans only receiver.typ + fn_type + body).
|
|
if flat_node_uses_channel(c.edge(0), options) {
|
|
return true
|
|
}
|
|
if flat_node_uses_channel(c.edge(1), options) {
|
|
return true
|
|
}
|
|
if flat_fn_decl_body_active_for_channel_scan(c, options) {
|
|
return flat_node_uses_channel(c.edge(3), options)
|
|
}
|
|
return false
|
|
}
|
|
if k == .expr_comptime && options.filter_comptime {
|
|
inner := c.edge(0)
|
|
if inner.kind() == .expr_if {
|
|
return flat_comptime_if_uses_channel(inner, options)
|
|
}
|
|
return flat_node_uses_channel(inner, options)
|
|
}
|
|
for i in 0 .. c.edge_count() {
|
|
if flat_node_uses_channel(c.edge(i), options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// flat_comptime_if_uses_channel mirrors
|
|
// `comptime_if_expr_uses_channel_with_options`: only the branch selected by the
|
|
// comptime condition is scanned for channel usage.
|
|
fn flat_comptime_if_uses_channel(if_c ast.Cursor, options ChannelScanOptions) bool {
|
|
if flat_comptime_cond_matches(if_c.edge(0), options) {
|
|
for i in 2 .. if_c.edge_count() {
|
|
if flat_node_uses_channel(if_c.edge(i), options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
else_c := if_c.edge(1)
|
|
if else_c.kind() == .expr_if {
|
|
if else_c.edge(0).kind() == .expr_empty {
|
|
for i in 2 .. else_c.edge_count() {
|
|
if flat_node_uses_channel(else_c.edge(i), options) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
return flat_comptime_if_uses_channel(else_c, options)
|
|
}
|
|
return flat_node_uses_channel(else_c, options)
|
|
}
|
|
|
|
// flat_fn_decl_body_active_for_channel_scan mirrors
|
|
// `fn_decl_body_is_active_for_channel_scan`: a FnDecl whose comptime attributes
|
|
// (`@[if ...]`) don't match the current target is inactive, so its body is
|
|
// skipped. aux_attribute layout: edge0 = value, edge1 = comptime_cond.
|
|
fn flat_fn_decl_body_active_for_channel_scan(fn_c ast.Cursor, options ChannelScanOptions) bool {
|
|
attrs := fn_c.list_at(2)
|
|
for i in 0 .. attrs.len() {
|
|
cond := attrs.at(i).edge(1)
|
|
if !cond.is_valid() || cond.kind() == .expr_empty {
|
|
continue
|
|
}
|
|
if !flat_comptime_cond_matches(cond, options) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// flat_comptime_cond_matches evaluates a comptime condition from FlatAst
|
|
// cursors. Call/pkgconfig conditions still fall back to the legacy expression
|
|
// evaluator until call argument cursors are ported.
|
|
fn flat_comptime_cond_matches(cond ast.Cursor, options ChannelScanOptions) bool {
|
|
if !cond.is_valid() {
|
|
return false
|
|
}
|
|
match cond.kind() {
|
|
.expr_ident {
|
|
return ast_comptime_flag_matches(cond.name(), options.user_defines, options.target_os)
|
|
}
|
|
.expr_comptime, .expr_paren {
|
|
return flat_comptime_cond_matches(cond.edge(0), options)
|
|
}
|
|
.expr_prefix {
|
|
op := unsafe { token.Token(int(cond.aux())) }
|
|
if op == .not {
|
|
return !flat_comptime_cond_matches(cond.edge(0), options)
|
|
}
|
|
}
|
|
.expr_infix {
|
|
op := unsafe { token.Token(int(cond.aux())) }
|
|
if op == .and {
|
|
return flat_comptime_cond_matches(cond.edge(0), options)
|
|
&& flat_comptime_cond_matches(cond.edge(1), options)
|
|
}
|
|
if op == .logical_or {
|
|
return flat_comptime_cond_matches(cond.edge(0), options)
|
|
|| flat_comptime_cond_matches(cond.edge(1), options)
|
|
}
|
|
}
|
|
.expr_postfix {
|
|
op := unsafe { token.Token(int(cond.aux())) }
|
|
inner := cond.edge(0)
|
|
if op == .question && inner.kind() == .expr_ident {
|
|
return vpref.comptime_optional_define_value(inner.name(), options.user_defines,
|
|
options.explicit_user_defines)
|
|
}
|
|
}
|
|
.expr_call, .expr_call_or_cast {
|
|
if pkg_name := flat_pkgconfig_call_name(cond) {
|
|
if !options.allow_pkgconfig {
|
|
return false
|
|
}
|
|
return vpref.comptime_pkgconfig_value(pkg_name)
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fn flat_pkgconfig_call_name(expr ast.Cursor) ?string {
|
|
match expr.kind() {
|
|
.expr_call {
|
|
lhs := expr.edge(0)
|
|
if lhs.is_valid() && lhs.kind() == .expr_ident && lhs.name() == 'pkgconfig'
|
|
&& expr.edge_count() == 2 {
|
|
return flat_string_literal_value(expr.edge(1))
|
|
}
|
|
}
|
|
.expr_call_or_cast {
|
|
lhs := expr.edge(0)
|
|
if lhs.is_valid() && lhs.kind() == .expr_ident && lhs.name() == 'pkgconfig' {
|
|
return flat_string_literal_value(expr.edge(1))
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return none
|
|
}
|
|
|
|
fn flat_string_literal_value(expr ast.Cursor) ?string {
|
|
if !expr.is_valid() || expr.kind() != .expr_string {
|
|
return none
|
|
}
|
|
return unquote_ast_string_literal_value(expr.name())
|
|
}
|
|
|
|
// parse_batch routes normal parsing through the streaming-into-shared-
|
|
// FlatBuilder path.
|
|
fn (mut b Builder) parse_batch(mut parser_reused parser.Parser, files []string) {
|
|
b.ensure_flat_builder_inited()
|
|
parser_reused.parse_files_into_flat(files, mut b.file_set, mut b.flat_builder)
|
|
}
|
|
|
|
// ensure_flat_builder_inited lazily seeds b.flat_builder on first use.
|
|
// Use init_flat_builder_for_paths upfront when the input set is known to
|
|
// avoid arena reallocs; this fallback only kicks in when the streaming
|
|
// path is entered without that pre-sizing pass.
|
|
fn (mut b Builder) ensure_flat_builder_inited() {
|
|
if b.flat_builder_inited {
|
|
return
|
|
}
|
|
b.flat_builder = ast.new_flat_builder()
|
|
b.flat_builder_inited = true
|
|
}
|
|
|
|
// init_flat_builder_for_paths sizes the persistent flat_builder arenas
|
|
// from the total source bytes of the supplied paths. Used by parse_files
|
|
// to pre-size before any parse_batch call so the streaming path avoids
|
|
// the geometric realloc churn the one-shot flatten_files() side-steps.
|
|
// The 2x scale factor accounts for imports, which the caller does not
|
|
// yet know — empirically core+user is ~half of the final byte total.
|
|
fn (mut b Builder) init_flat_builder_for_paths(paths []string) {
|
|
if b.flat_builder_inited {
|
|
return
|
|
}
|
|
mut total_bytes := i64(0)
|
|
for path in paths {
|
|
if path == '' {
|
|
continue
|
|
}
|
|
total_bytes += os.file_size(path)
|
|
}
|
|
nodes_cap, edges_cap, strings_cap := ast.arena_caps_for_bytes(total_bytes * 2)
|
|
b.flat_builder = ast.new_flat_builder_with_capacity(nodes_cap, edges_cap, strings_cap)
|
|
b.flat_builder_inited = true
|
|
}
|
|
|
|
fn (mut b Builder) parse_files(files []string) []ast.File {
|
|
mut parser_reused := parser.Parser.new(b.pref)
|
|
skip_builtin := b.pref.skip_builtin
|
|
target_os := b.pref.source_filter_target_os()
|
|
allow_pkgconfig_imports := !b.pref.is_cross_target()
|
|
mut use_core_headers := false
|
|
// Resolve core-source paths upfront so the pre-size pass below can
|
|
// stat them alongside user files. We still parse in the same batch
|
|
// shape as before so module-init ordering is unchanged.
|
|
mut core_module_files := [][]string{}
|
|
mut cached_core_files := []string{}
|
|
if !skip_builtin {
|
|
use_core_headers = b.can_use_cached_core_headers_for_parse()
|
|
b.used_vh_for_parse = use_core_headers
|
|
if use_core_headers {
|
|
cached_core_files = b.core_cached_parse_paths()
|
|
} else {
|
|
for module_path in core_cached_module_paths {
|
|
vlib_path := b.pref.get_vlib_module_path(module_path)
|
|
core_module_files << get_v_files_from_dir(vlib_path, b.pref.user_defines, target_os)
|
|
}
|
|
}
|
|
}
|
|
// Expand user input paths: allow compiling module directories (e.g. `v2 .`).
|
|
mut expanded_user_files := []string{}
|
|
mut seen_user_files := map[string]bool{}
|
|
for input in files {
|
|
if input == '' {
|
|
continue
|
|
}
|
|
if os.is_dir(input) {
|
|
dir_files := get_user_v_files_from_dir(input, b.pref.user_defines, target_os)
|
|
for dir_file in dir_files {
|
|
if dir_file != '' && dir_file !in seen_user_files {
|
|
expanded_user_files << dir_file
|
|
seen_user_files[dir_file] = true
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if should_expand_single_file_input(input) {
|
|
if input !in seen_user_files {
|
|
expanded_user_files << input
|
|
seen_user_files[input] = true
|
|
}
|
|
dir_files := get_v_files_from_dir(os.dir(input), b.pref.user_defines, target_os)
|
|
for dir_file in dir_files {
|
|
if dir_file != '' && dir_file !in seen_user_files {
|
|
expanded_user_files << dir_file
|
|
seen_user_files[dir_file] = true
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if input !in seen_user_files {
|
|
expanded_user_files << input
|
|
seen_user_files[input] = true
|
|
}
|
|
}
|
|
// Directory inputs and non-main module files were expanded above. Single-file
|
|
// `main` programs stay isolated, so `v2 hello.v` parses only `hello.v`,
|
|
// while `v2 .` and `v2 vlib/math/math_test.v` still parse their module files.
|
|
virtual_main_modules := b.collect_virtual_main_modules_from_paths(expanded_user_files)
|
|
if b.can_use_cached_virtual_headers_for_parse(virtual_main_modules) {
|
|
expanded_user_files = b.replace_virtual_sources_with_headers(expanded_user_files,
|
|
virtual_main_modules)
|
|
b.used_virtual_vh_for_parse = true
|
|
}
|
|
mut pre_size_paths := []string{}
|
|
pre_size_paths << cached_core_files
|
|
for module_files in core_module_files {
|
|
pre_size_paths << module_files
|
|
}
|
|
pre_size_paths << expanded_user_files
|
|
b.init_flat_builder_for_paths(pre_size_paths)
|
|
if !skip_builtin {
|
|
if use_core_headers {
|
|
b.parse_batch(mut parser_reused, cached_core_files)
|
|
} else {
|
|
for module_files in core_module_files {
|
|
b.parse_batch(mut parser_reused, module_files)
|
|
}
|
|
}
|
|
}
|
|
b.parse_batch(mut parser_reused, expanded_user_files)
|
|
skip_imports := b.pref.skip_imports
|
|
if skip_imports {
|
|
// Flat path: b.flat_builder is the canonical store; build() derives
|
|
// b.flat from it after parse completes.
|
|
return []ast.File{}
|
|
}
|
|
// parse imports
|
|
use_import_headers := b.can_use_cached_import_headers_for_parse()
|
|
b.used_import_vh_for_parse = use_import_headers
|
|
mut parsed_imports := []string{}
|
|
if !skip_builtin {
|
|
parsed_imports << core_cached_module_paths
|
|
}
|
|
// Walk the flat store directly. parse_batch may append new files to
|
|
// b.flat_builder, so re-read the length each iteration.
|
|
for afi := 0; afi < b.flat_builder.flat.files.len; afi++ {
|
|
ff := b.flat_builder.flat.files[afi]
|
|
ast_file_name := b.flat_builder.flat.file_name(ff)
|
|
for mod in active_file_imports_from_flat_with_options(&b.flat_builder.flat, ff,
|
|
b.pref.user_defines, b.pref.explicit_user_defines, target_os, allow_pkgconfig_imports) {
|
|
if mod.name in parsed_imports {
|
|
continue
|
|
}
|
|
if use_core_headers || use_import_headers {
|
|
if cached_path := b.cached_import_parse_path(mod.name) {
|
|
b.parse_batch(mut parser_reused, [cached_path])
|
|
parsed_imports << mod.name
|
|
continue
|
|
}
|
|
}
|
|
mod_path := b.pref.get_module_path(mod.name, ast_file_name)
|
|
module_files := get_v_files_from_dir(mod_path, b.pref.user_defines, target_os)
|
|
if module_files.len == 0 {
|
|
continue
|
|
}
|
|
b.parse_batch(mut parser_reused, module_files)
|
|
parsed_imports << mod.name
|
|
}
|
|
}
|
|
return []ast.File{}
|
|
}
|