mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-09 06:12:05 -04:00
Add -vet-when-shadowing; Resolve global whens and foreign blocks on demand via name placeholders
This commit is contained in:
1 parent
5f6e95a620
commit
2b90b616e1
8 files changed
+529
-261
No files matched your search
@@ -317,10 +317,11 @@ enum VetFlags : u64 {
|
||||
VetFlag_Tabs = 1u<<9,
|
||||
VetFlag_UnusedProcedures = 1u<<10,
|
||||
VetFlag_ExplicitAllocators = 1u<<11,
|
||||
VetFlag_WhenShadowing = 1u<<12,
|
||||
|
||||
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
|
||||
|
||||
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated|VetFlag_Cast,
|
||||
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated|VetFlag_Cast|VetFlag_WhenShadowing,
|
||||
|
||||
VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
|
||||
};
|
||||
@@ -352,6 +353,8 @@ u64 get_vet_flag_from_name(String const &name) {
|
||||
return VetFlag_UnusedProcedures;
|
||||
} else if (name == "explicit-allocators") {
|
||||
return VetFlag_ExplicitAllocators;
|
||||
} else if (name == "when-shadowing") {
|
||||
return VetFlag_WhenShadowing;
|
||||
}
|
||||
return VetFlag_NONE;
|
||||
}
|
||||
|
||||
+36
-253
@@ -400,6 +400,8 @@ gb_internal void check_close_scope(CheckerContext *c) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal Entity *force_scope_placeholders(Scope *s, InternedString name, u32 hash);
|
||||
|
||||
gb_internal Entity *scope_lookup_current(Scope *s, InternedString name, u32 hash) {
|
||||
// Entity **found = string_map_get(&s->elements, name);
|
||||
if (hash == 0) {
|
||||
@@ -409,6 +411,9 @@ gb_internal Entity *scope_lookup_current(Scope *s, InternedString name, u32 hash
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
if (s->placeholders != nullptr) {
|
||||
return force_scope_placeholders(s, name, hash);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -428,6 +433,9 @@ gb_internal void scope_lookup_parent(Scope *scope, InternedString name, Scope **
|
||||
if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex);
|
||||
found = scope_map_get(&s->elements, name, hash);
|
||||
if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex);
|
||||
if (found == nullptr && s->placeholders != nullptr) {
|
||||
found = force_scope_placeholders(s, name, hash);
|
||||
}
|
||||
if (found) {
|
||||
Entity *e = found;
|
||||
if (gone_thru_proc) {
|
||||
@@ -5127,9 +5135,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
|
||||
|
||||
gb_internal bool check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
||||
gb_internal void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
||||
ast_node(fb, ForeignBlockDecl, decl);
|
||||
Ast *foreign_library = fb->foreign_library;
|
||||
|
||||
@@ -5144,11 +5150,7 @@ gb_internal bool check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
||||
check_decl_attributes(&c, fb->attributes, foreign_block_decl_attribute, nullptr);
|
||||
|
||||
ast_node(block, BlockStmt, fb->body);
|
||||
if (c.collect_delayed_decls && (c.scope->flags&ScopeFlag_File) != 0) {
|
||||
return collect_file_decls(&c, block->stmts);
|
||||
}
|
||||
check_collect_entities(&c, block->stmts);
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) {
|
||||
@@ -5156,7 +5158,11 @@ gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) {
|
||||
DeclInfo *d = e->decl_info;
|
||||
if (d != nullptr && d->init_expr != nullptr) {
|
||||
Ast *init = d->init_expr;
|
||||
// NOTE: in the scope of its own file, as a package's files may bind a name differently
|
||||
Scope *prev_scope = c->scope;
|
||||
c->scope = d->scope;
|
||||
Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
|
||||
c->scope = prev_scope;
|
||||
if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
|
||||
e->kind = Entity_TypeName;
|
||||
return true;
|
||||
@@ -5189,7 +5195,7 @@ gb_internal bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *
|
||||
}
|
||||
|
||||
|
||||
gb_internal void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
|
||||
gb_internal void correct_type_aliases_in_package(CheckerContext *c, AstPackage *pkg) {
|
||||
// NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
|
||||
// of type aliases being "confused" as constants
|
||||
//
|
||||
@@ -5200,8 +5206,12 @@ gb_internal void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
|
||||
// See @TypeAliasingProblem for more information
|
||||
for (;;) {
|
||||
bool corrections = false;
|
||||
corrections |= correct_type_alias_in_scope_backwards(c, s);
|
||||
corrections |= correct_type_alias_in_scope_forwards(c, s);
|
||||
corrections |= correct_type_alias_in_scope_backwards(c, pkg->scope);
|
||||
corrections |= correct_type_alias_in_scope_forwards(c, pkg->scope);
|
||||
for (AstFile *f : pkg->files) {
|
||||
corrections |= correct_type_alias_in_scope_backwards(c, f->scope);
|
||||
corrections |= correct_type_alias_in_scope_forwards(c, f->scope);
|
||||
}
|
||||
if (!corrections) {
|
||||
return;
|
||||
}
|
||||
@@ -5220,17 +5230,7 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
|
||||
for_array(decl_index, nodes) {
|
||||
Ast *decl = nodes[decl_index];
|
||||
if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
|
||||
if (curr_file && decl->kind == Ast_ExprStmt) {
|
||||
Ast *expr = decl->ExprStmt.expr;
|
||||
if (expr->kind == Ast_CallExpr && expr->CallExpr.proc->kind == Ast_BasicDirective) {
|
||||
if (c->collect_delayed_decls) {
|
||||
if (decl->state_flags & StateFlag_BeenHandled) return;
|
||||
decl->state_flags |= StateFlag_BeenHandled;
|
||||
array_add(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], expr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// NOTE: global directives such as '#assert' are queued by `scan_global_decl_sources`
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5266,9 +5266,7 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
|
||||
case_end;
|
||||
|
||||
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||
if (curr_file != nullptr) {
|
||||
array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
|
||||
}
|
||||
// NOTE: global ones are resolved like global 'when's, see `resolve_global_decl_sources`
|
||||
case_end;
|
||||
|
||||
default:
|
||||
@@ -5785,7 +5783,6 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) {
|
||||
AstFile *f = decl->file();
|
||||
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
ctx.collect_delayed_decls = false;
|
||||
|
||||
GB_ASSERT(ctx.scope == e->scope);
|
||||
|
||||
@@ -5935,165 +5932,6 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
|
||||
|
||||
}
|
||||
|
||||
// Returns true if a new package is present
|
||||
gb_internal bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
|
||||
gb_internal bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
|
||||
|
||||
gb_internal bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
if (!ws->is_cond_determined) {
|
||||
check_expr(ctx, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error(ws->cond, "Non-boolean condition in 'when' statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error(ws->cond, "Non-constant condition in 'when' statement");
|
||||
}
|
||||
|
||||
ws->is_cond_determined = true;
|
||||
ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool;
|
||||
}
|
||||
|
||||
if (ws->body == nullptr || ws->body->kind != Ast_BlockStmt) {
|
||||
error(ws->cond, "Invalid body for 'when' statement");
|
||||
} else {
|
||||
if (ws->determined_cond) {
|
||||
check_collect_entities(ctx, ws->body->BlockStmt.stmts);
|
||||
return true;
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case Ast_BlockStmt:
|
||||
check_collect_entities(ctx, ws->else_stmt->BlockStmt.stmts);
|
||||
return true;
|
||||
case Ast_WhenStmt:
|
||||
collect_when_stmt_from_file(ctx, &ws->else_stmt->WhenStmt);
|
||||
return true;
|
||||
default:
|
||||
error(ws->else_stmt, "Invalid 'else' statement in 'when' statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
if (!ws->is_cond_determined) {
|
||||
check_expr(ctx, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error(ws->cond, "Non-boolean condition in 'when' statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error(ws->cond, "Non-constant condition in 'when' statement");
|
||||
}
|
||||
|
||||
ws->is_cond_determined = true;
|
||||
ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool;
|
||||
}
|
||||
|
||||
if (ws->body == nullptr || ws->body->kind != Ast_BlockStmt) {
|
||||
error(ws->cond, "Invalid body for 'when' statement");
|
||||
} else {
|
||||
if (ws->determined_cond) {
|
||||
return collect_file_decls(ctx, ws->body->BlockStmt.stmts);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case Ast_BlockStmt:
|
||||
return collect_file_decls(ctx, ws->else_stmt->BlockStmt.stmts);
|
||||
case Ast_WhenStmt:
|
||||
return collect_file_decls_from_when_stmt(ctx, &ws->else_stmt->WhenStmt);
|
||||
default:
|
||||
error(ws->else_stmt, "Invalid 'else' statement in 'when' statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool collect_file_decl(CheckerContext *ctx, Ast *decl) {
|
||||
GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
|
||||
|
||||
AstFile *curr_file = ctx->scope->file;
|
||||
GB_ASSERT(curr_file != nullptr);
|
||||
|
||||
if (decl->state_flags & StateFlag_BeenHandled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (decl->kind) {
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
check_collect_value_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
check_add_import_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(fl, ForeignImportDecl, decl);
|
||||
check_add_foreign_import_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||
GB_ASSERT(ctx->collect_delayed_decls);
|
||||
decl->state_flags |= StateFlag_BeenHandled;
|
||||
array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, decl);
|
||||
if (!ws->is_cond_determined) {
|
||||
if (collect_when_stmt_from_file(ctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CheckerContext nctx = *ctx;
|
||||
nctx.collect_delayed_decls = true;
|
||||
|
||||
if (collect_file_decls_from_when_stmt(&nctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
CheckerContext nctx = *ctx;
|
||||
nctx.collect_delayed_decls = true;
|
||||
|
||||
if (collect_file_decls_from_when_stmt(&nctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(es, ExprStmt, decl);
|
||||
GB_ASSERT(ctx->collect_delayed_decls);
|
||||
decl->state_flags |= StateFlag_BeenHandled;
|
||||
if (es->expr->kind == Ast_CallExpr) {
|
||||
ast_node(ce, CallExpr, es->expr);
|
||||
if (ce->proc->kind == Ast_BasicDirective) {
|
||||
array_add(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], es->expr);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
|
||||
GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
|
||||
|
||||
for_array(i, decls) {
|
||||
if (collect_file_decl(ctx, decls[i])) {
|
||||
correct_type_aliases_in_scope(ctx, ctx->scope);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
correct_type_aliases_in_scope(ctx, ctx->scope);
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal GB_COMPARE_PROC(sort_file_by_name) {
|
||||
AstFile const *x = *cast(AstFile const **)a;
|
||||
AstFile const *y = *cast(AstFile const **)b;
|
||||
@@ -6140,7 +5978,6 @@ gb_internal WORKER_TASK_PROC(check_collect_entities_all_worker_proc) {
|
||||
reset_checker_context(ctx, f, untyped);
|
||||
|
||||
check_collect_entities(ctx, f->decls);
|
||||
GB_ASSERT(ctx->collect_delayed_decls == false);
|
||||
|
||||
add_untyped_expressions(&c->info, ctx->untyped);
|
||||
|
||||
@@ -6205,6 +6042,8 @@ gb_internal void check_export_entities(Checker *c) {
|
||||
thread_pool_wait();
|
||||
}
|
||||
|
||||
#include "checker_global_when.cpp"
|
||||
|
||||
gb_internal void check_import_entities(Checker *c) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
@@ -6261,101 +6100,45 @@ gb_internal void check_import_entities(Checker *c) {
|
||||
array_add(&package_order, n);
|
||||
}
|
||||
|
||||
TIME_SECTION("check_import_entities - collect file decls");
|
||||
CheckerContext ctx = {};
|
||||
init_checker_context(&ctx, c);
|
||||
defer (destroy_checker_context(&ctx));
|
||||
|
||||
UntypedExprInfoMap untyped = {};
|
||||
defer (map_destroy(&untyped));
|
||||
|
||||
isize min_pkg_index = 0;
|
||||
TIME_SECTION("check_import_entities - imports");
|
||||
// NOTE(bill): every import first, as resolving a 'when' may check declarations in any package
|
||||
u64 stage_start = global_import_stage_begin();
|
||||
for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
|
||||
ImportGraphNode *node = package_order[pkg_index];
|
||||
AstPackage *pkg = node->pkg;
|
||||
AstPackage *pkg = package_order[pkg_index]->pkg;
|
||||
pkg->order = 1+pkg_index;
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
|
||||
for (AstFile *f : pkg->files) {
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
ctx.collect_delayed_decls = true;
|
||||
|
||||
// Check import declarations first to simplify things
|
||||
for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_Import]) {
|
||||
check_add_import_decl(&ctx, decl);
|
||||
}
|
||||
array_clear(&f->delayed_decls_queues[AstDelayQueue_Import]);
|
||||
|
||||
if (collect_file_decls(&ctx, f->decls)) {
|
||||
check_export_entities_in_pkg(&ctx, pkg, &untyped);
|
||||
pkg_index = min_pkg_index-1;
|
||||
break;
|
||||
}
|
||||
|
||||
add_untyped_expressions(ctx.info, &untyped);
|
||||
}
|
||||
if (pkg_index < 0) {
|
||||
continue;
|
||||
}
|
||||
min_pkg_index = pkg_index;
|
||||
}
|
||||
global_import_stage_end(GlobalImportStage_CollectFileDecls, stage_start);
|
||||
global_import_stage_end(GlobalImportStage_Imports, stage_start);
|
||||
|
||||
TIME_SECTION("check_import_entities - resolve 'when' and 'foreign' blocks");
|
||||
resolve_global_decl_sources(c, package_order);
|
||||
|
||||
TIME_SECTION("check_import_entities - check delayed entities");
|
||||
for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
|
||||
ImportGraphNode *node = package_order[pkg_index];
|
||||
for (ImportGraphNode *node : package_order) {
|
||||
GB_ASSERT(node->scope->flags&ScopeFlag_Pkg);
|
||||
AstPackage *pkg = node->scope->pkg;
|
||||
|
||||
stage_start = global_import_stage_begin();
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
|
||||
for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_Import]) {
|
||||
check_add_import_decl(&ctx, decl);
|
||||
}
|
||||
array_clear(&f->delayed_decls_queues[AstDelayQueue_Import]);
|
||||
add_untyped_expressions(ctx.info, &untyped);
|
||||
}
|
||||
global_import_stage_end(GlobalImportStage_Imports, stage_start);
|
||||
|
||||
stage_start = global_import_stage_begin();
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
correct_type_aliases_in_scope(&ctx, pkg->scope);
|
||||
}
|
||||
correct_type_aliases_in_package(&ctx, pkg);
|
||||
global_import_stage_end(GlobalImportStage_TypeAliases, stage_start);
|
||||
|
||||
stage_start = global_import_stage_begin();
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
|
||||
ctx.collect_delayed_decls = true;
|
||||
|
||||
bool will_recheck_foreign_block = false;
|
||||
for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) {
|
||||
if (check_add_foreign_block_decl(&ctx, decl)) {
|
||||
pkg_index -= 1; // Re-check package
|
||||
will_recheck_foreign_block = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (will_recheck_foreign_block) {
|
||||
break;
|
||||
}
|
||||
|
||||
array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]);
|
||||
}
|
||||
global_import_stage_end(GlobalImportStage_ForeignBlocks, stage_start);
|
||||
|
||||
stage_start = global_import_stage_begin();
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
for (AstFile *f : pkg->files) {
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
|
||||
for (Ast *expr : f->delayed_decls_queues[AstDelayQueue_Expr]) {
|
||||
|
||||
+1
-1
@@ -564,6 +564,7 @@ struct Scope {
|
||||
RwMutex mutex;
|
||||
ScopeMap elements;
|
||||
PtrSet<Scope *> imported;
|
||||
PtrMap<u64, struct GlobalDeclSource *> *placeholders; // multi-map; names a global 'when' or 'foreign' block may declare, until all are resolved
|
||||
|
||||
DeclInfo *decl_info;
|
||||
|
||||
@@ -838,7 +839,6 @@ struct CheckerContext {
|
||||
u32 stmt_flags;
|
||||
bool in_enum_type;
|
||||
bool in_proc_group;
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
|
||||
bool no_polymorphic_errors;
|
||||
|
||||
@@ -13,20 +13,20 @@ struct GlobalEntityTimingFrame {
|
||||
};
|
||||
|
||||
enum GlobalImportStagePart {
|
||||
GlobalImportStage_CollectFileDecls,
|
||||
GlobalImportStage_Imports,
|
||||
GlobalImportStage_Placeholders,
|
||||
GlobalImportStage_DeclSources,
|
||||
GlobalImportStage_TypeAliases,
|
||||
GlobalImportStage_ForeignBlocks,
|
||||
GlobalImportStage_DelayedExprs,
|
||||
|
||||
GlobalImportStage_COUNT,
|
||||
};
|
||||
|
||||
gb_global char const *global_import_stage_names[GlobalImportStage_COUNT] = {
|
||||
"collect file decls ('when')",
|
||||
"delayed imports",
|
||||
"imports",
|
||||
"'when' and 'foreign' placeholders",
|
||||
"resolve 'when' and 'foreign' blocks",
|
||||
"type alias correction",
|
||||
"foreign blocks",
|
||||
"delayed expressions (#assert etc.)",
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
// Global 'when's and 'foreign' blocks: every name one may declare is a placeholder in its scope, and the
|
||||
// first lookup of a placeholder resolves them, so the order of files and declarations does not matter
|
||||
|
||||
struct GlobalDeclSource {
|
||||
Ast * node; // WhenStmt or ForeignBlockDecl
|
||||
AstFile * file;
|
||||
GlobalDeclSource *parent;
|
||||
bool in_else; // within the else branch of `parent`
|
||||
bool reachable;
|
||||
bool reported_cycle;
|
||||
EntityState state;
|
||||
ForeignContext foreign_context; // of a resolved 'foreign' block
|
||||
};
|
||||
|
||||
struct GlobalDeclSourceFrame {
|
||||
GlobalDeclSource *source;
|
||||
InternedString needs; // the placeholder being resolved for it
|
||||
};
|
||||
|
||||
gb_global Array<GlobalDeclSource *> global_decl_sources;
|
||||
gb_global Array<GlobalDeclSourceFrame> global_decl_source_stack;
|
||||
gb_global Array<Scope *> global_placeholder_scopes;
|
||||
gb_global CheckerContext global_decl_source_export_ctx;
|
||||
gb_global UntypedExprInfoMap global_decl_source_export_untyped;
|
||||
|
||||
enum : u8 {
|
||||
PlaceholderScope_File = 1<<0,
|
||||
PlaceholderScope_Pkg = 1<<1,
|
||||
};
|
||||
|
||||
// -1 when 'private' has a value that is not a string literal
|
||||
gb_internal i32 syntactic_visibility(Array<Ast *> const &attributes) {
|
||||
for (Ast *attr : attributes) {
|
||||
if (attr->kind != Ast_Attribute) {
|
||||
continue;
|
||||
}
|
||||
for (Ast *elem : attr->Attribute.elems) {
|
||||
if (elem->kind == Ast_Ident && elem->Ident.token.string == "private") {
|
||||
return EntityVisiblity_PrivateToPackage;
|
||||
}
|
||||
if (elem->kind == Ast_FieldValue &&
|
||||
elem->FieldValue.field->kind == Ast_Ident &&
|
||||
elem->FieldValue.field->Ident.token.string == "private") {
|
||||
Ast *value = elem->FieldValue.value;
|
||||
if (value != nullptr && value->tav.value.kind == ExactValue_String) {
|
||||
return value->tav.value.value_string == "file" ? EntityVisiblity_PrivateToFile : EntityVisiblity_PrivateToPackage;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return EntityVisiblity_Public;
|
||||
}
|
||||
|
||||
gb_internal bool has_syntactic_attribute(Array<Ast *> const &attributes, String const &name) {
|
||||
for (Ast *attr : attributes) {
|
||||
if (attr->kind != Ast_Attribute) {
|
||||
continue;
|
||||
}
|
||||
for (Ast *elem : attr->Attribute.elems) {
|
||||
Ast *field = elem->kind == Ast_FieldValue ? elem->FieldValue.field : elem;
|
||||
if (field->kind == Ast_Ident && field->Ident.token.string == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void add_placeholder(Scope *s, InternedString name, GlobalDeclSource *src) {
|
||||
if (name.value == 0 || name.is_blank()) {
|
||||
return;
|
||||
}
|
||||
if (s->placeholders == nullptr) {
|
||||
s->placeholders = permanent_alloc_item<PtrMap<u64, GlobalDeclSource *>>();
|
||||
map_init(s->placeholders);
|
||||
array_add(&global_placeholder_scopes, s);
|
||||
}
|
||||
u64 key = name.value;
|
||||
for (auto *e = multi_map_find_first(s->placeholders, key); e != nullptr; e = multi_map_find_next(s->placeholders, e)) {
|
||||
if (e->value == src) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
multi_map_insert(s->placeholders, key, src);
|
||||
}
|
||||
|
||||
gb_internal void add_placeholders(AstFile *f, u8 scopes, InternedString name, GlobalDeclSource *src) {
|
||||
if (scopes & PlaceholderScope_File) {
|
||||
add_placeholder(f->scope, name, src);
|
||||
}
|
||||
if (scopes & PlaceholderScope_Pkg) {
|
||||
add_placeholder(f->pkg->scope, name, src);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal GlobalDeclSource *add_global_decl_source(Ast *node, AstFile *f, GlobalDeclSource *parent, bool in_else) {
|
||||
GlobalDeclSource *src = permanent_alloc_item<GlobalDeclSource>();
|
||||
src->node = node;
|
||||
src->file = f;
|
||||
src->parent = parent;
|
||||
src->in_else = in_else;
|
||||
src->reachable = true;
|
||||
src->state = EntityState_Unresolved;
|
||||
array_add(&global_decl_sources, src);
|
||||
return src;
|
||||
}
|
||||
|
||||
gb_internal void scan_global_decl_sources(AstFile *f, Slice<Ast *> const &stmts, GlobalDeclSource *owner, bool in_else, i32 foreign_visibility);
|
||||
|
||||
gb_internal void scan_global_when_stmt(AstFile *f, Ast *node, GlobalDeclSource *parent, bool in_else, i32 foreign_visibility) {
|
||||
ast_node(ws, WhenStmt, node);
|
||||
GlobalDeclSource *src = add_global_decl_source(node, f, parent, in_else);
|
||||
if (ws->body != nullptr && ws->body->kind == Ast_BlockStmt) {
|
||||
scan_global_decl_sources(f, ws->body->BlockStmt.stmts, src, false, foreign_visibility);
|
||||
}
|
||||
if (ws->else_stmt != nullptr) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case Ast_BlockStmt:
|
||||
scan_global_decl_sources(f, ws->else_stmt->BlockStmt.stmts, src, true, foreign_visibility);
|
||||
break;
|
||||
case Ast_WhenStmt:
|
||||
scan_global_when_stmt(f, ws->else_stmt, src, true, foreign_visibility);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void scan_global_decl_sources(AstFile *f, Slice<Ast *> const &stmts, GlobalDeclSource *owner, bool in_else, i32 foreign_visibility) {
|
||||
// NOTE(bill): `owner == nullptr` is the file scope itself, whose other declarations are already collected
|
||||
for (Ast *decl : stmts) {
|
||||
switch (decl->kind) {
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (owner == nullptr) {
|
||||
break;
|
||||
}
|
||||
i32 visibility = syntactic_visibility(vd->attributes);
|
||||
if (visibility == EntityVisiblity_Public) {
|
||||
visibility = foreign_visibility;
|
||||
}
|
||||
if (visibility == EntityVisiblity_Public && (f->flags & AstFile_IsPrivateFile)) {
|
||||
visibility = EntityVisiblity_PrivateToFile;
|
||||
}
|
||||
u8 scopes = PlaceholderScope_Pkg;
|
||||
if (visibility == EntityVisiblity_PrivateToFile) {
|
||||
scopes = PlaceholderScope_File;
|
||||
} else if (visibility < 0) {
|
||||
scopes = PlaceholderScope_File|PlaceholderScope_Pkg;
|
||||
}
|
||||
for (Ast *name : vd->names) {
|
||||
if (name->kind == Ast_Ident) {
|
||||
add_placeholders(f, scopes, name->Ident.interned, owner);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(fl, ForeignImportDecl, decl);
|
||||
if (owner == nullptr) {
|
||||
break;
|
||||
}
|
||||
String library_name = fl->library_name.string;
|
||||
if (library_name.len == 0 && fl->fullpaths.count != 0) {
|
||||
library_name = path_to_entity_name(fl->library_name.string, fl->fullpaths[0]);
|
||||
}
|
||||
if (library_name.len != 0) {
|
||||
u8 scopes = has_syntactic_attribute(fl->attributes, str_lit("export")) ? PlaceholderScope_Pkg : PlaceholderScope_File;
|
||||
add_placeholders(f, scopes, string_interner_insert(library_name), owner);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||
GlobalDeclSource *src = add_global_decl_source(decl, f, owner, in_else);
|
||||
if (fb->body != nullptr && fb->body->kind == Ast_BlockStmt) {
|
||||
scan_global_decl_sources(f, fb->body->BlockStmt.stmts, src, false, syntactic_visibility(fb->attributes));
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, decl);
|
||||
scan_global_when_stmt(f, decl, owner, in_else, foreign_visibility);
|
||||
case_end;
|
||||
|
||||
case_ast_node(es, ExprStmt, decl);
|
||||
if (owner == nullptr && es->expr->kind == Ast_CallExpr &&
|
||||
es->expr->CallExpr.proc->kind == Ast_BasicDirective &&
|
||||
(decl->state_flags & StateFlag_BeenHandled) == 0) {
|
||||
decl->state_flags |= StateFlag_BeenHandled;
|
||||
array_add(&f->delayed_decls_queues[AstDelayQueue_Expr], es->expr);
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal bool is_global_decl_source_in_when(GlobalDeclSource *src) {
|
||||
for (; src != nullptr; src = src->parent) {
|
||||
if (src->node->kind == Ast_WhenStmt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal Slice<Ast *> global_decl_source_taken_stmts(GlobalDeclSource *src) {
|
||||
if (src->node->kind == Ast_ForeignBlockDecl) {
|
||||
Ast *body = src->node->ForeignBlockDecl.body;
|
||||
if (body != nullptr && body->kind == Ast_BlockStmt) {
|
||||
return body->BlockStmt.stmts;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
ast_node(ws, WhenStmt, src->node);
|
||||
if (ws->determined_cond) {
|
||||
if (ws->body != nullptr && ws->body->kind == Ast_BlockStmt) {
|
||||
return ws->body->BlockStmt.stmts;
|
||||
}
|
||||
} else if (ws->else_stmt != nullptr && ws->else_stmt->kind == Ast_BlockStmt) {
|
||||
return ws->else_stmt->BlockStmt.stmts;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
gb_internal void collect_global_decl_source_stmts(CheckerContext *ctx, Slice<Ast *> const &stmts) {
|
||||
AstFile *f = ctx->file;
|
||||
for (Ast *decl : stmts) {
|
||||
if (decl->kind == Ast_ValueDecl) {
|
||||
check_collect_value_decl(ctx, decl);
|
||||
}
|
||||
}
|
||||
check_export_entities_in_pkg(&global_decl_source_export_ctx, f->pkg, &global_decl_source_export_untyped);
|
||||
|
||||
// NOTE(bill): after the value declarations, as their attributes are evaluated
|
||||
for (Ast *decl : stmts) {
|
||||
switch (decl->kind) {
|
||||
case_ast_node(fl, ForeignImportDecl, decl);
|
||||
check_add_foreign_import_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(es, ExprStmt, decl);
|
||||
if (es->expr->kind == Ast_CallExpr && es->expr->CallExpr.proc->kind == Ast_BasicDirective &&
|
||||
(decl->state_flags & StateFlag_BeenHandled) == 0) {
|
||||
decl->state_flags |= StateFlag_BeenHandled;
|
||||
array_add(&f->delayed_decls_queues[AstDelayQueue_Expr], es->expr);
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal Token global_decl_source_token(GlobalDeclSource *src) {
|
||||
if (src->node->kind == Ast_WhenStmt) {
|
||||
return src->node->WhenStmt.token;
|
||||
}
|
||||
return src->node->ForeignBlockDecl.token;
|
||||
}
|
||||
|
||||
gb_internal void report_global_decl_source_cycle(GlobalDeclSource *src, InternedString needed) {
|
||||
if (src->reported_cycle) {
|
||||
return;
|
||||
}
|
||||
src->reported_cycle = true;
|
||||
|
||||
isize start = 0;
|
||||
for (isize i = global_decl_source_stack.count-1; i >= 0; i--) {
|
||||
if (global_decl_source_stack[i].source == src) {
|
||||
start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_BLOCK();
|
||||
Token token = global_decl_source_token(src);
|
||||
error(token, "Cyclic dependency between global '%.*s' declarations", LIT(token.string));
|
||||
for (isize i = start; i < global_decl_source_stack.count; i++) {
|
||||
Token t = global_decl_source_token(global_decl_source_stack[i].source);
|
||||
InternedString name = i+1 < global_decl_source_stack.count ? global_decl_source_stack[i].needs : needed;
|
||||
error_line("\t'%.*s' at %s needs '%s', which may be declared by\n", LIT(t.string), token_pos_to_string(t.pos), name.cstring());
|
||||
}
|
||||
error_line("\t'%.*s' at %s\n", LIT(token.string), token_pos_to_string(token.pos));
|
||||
}
|
||||
|
||||
gb_internal void resolve_global_decl_source(GlobalDeclSource *src, InternedString needed) {
|
||||
if (src->state == EntityState_Resolved) {
|
||||
return;
|
||||
}
|
||||
if (src->state == EntityState_InProgress) {
|
||||
report_global_decl_source_cycle(src, needed);
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalDeclSource *foreign_block = nullptr;
|
||||
if (src->parent != nullptr) {
|
||||
GlobalDeclSource *parent = src->parent;
|
||||
resolve_global_decl_source(parent, needed);
|
||||
if (parent->state != EntityState_Resolved) {
|
||||
return;
|
||||
}
|
||||
bool reachable = parent->reachable;
|
||||
if (parent->node->kind == Ast_WhenStmt) {
|
||||
reachable = reachable && parent->node->WhenStmt.determined_cond != src->in_else;
|
||||
}
|
||||
if (!reachable) {
|
||||
src->reachable = false;
|
||||
src->state = EntityState_Resolved;
|
||||
return;
|
||||
}
|
||||
for (GlobalDeclSource *p = parent; p != nullptr; p = p->parent) {
|
||||
if (p->node->kind == Ast_ForeignBlockDecl) {
|
||||
foreign_block = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
src->state = EntityState_InProgress;
|
||||
array_add(&global_decl_source_stack, GlobalDeclSourceFrame{src, {}});
|
||||
|
||||
CheckerContext ctx = {};
|
||||
init_checker_context(&ctx, global_checker_ptr.load(std::memory_order_relaxed));
|
||||
UntypedExprInfoMap untyped = {};
|
||||
reset_checker_context(&ctx, src->file, &untyped);
|
||||
if (foreign_block != nullptr) {
|
||||
ctx.foreign_context = foreign_block->foreign_context;
|
||||
}
|
||||
|
||||
if (src->node->kind == Ast_WhenStmt) {
|
||||
ast_node(ws, WhenStmt, src->node);
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(&ctx, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error(ws->cond, "Non-boolean condition in 'when' statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error(ws->cond, "Non-constant condition in 'when' statement");
|
||||
}
|
||||
ws->is_cond_determined = true;
|
||||
ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool;
|
||||
if (ws->body == nullptr || ws->body->kind != Ast_BlockStmt) {
|
||||
error(ws->cond, "Invalid body for 'when' statement");
|
||||
} else if (ws->else_stmt != nullptr && ws->else_stmt->kind != Ast_BlockStmt && ws->else_stmt->kind != Ast_WhenStmt) {
|
||||
error(ws->else_stmt, "Invalid 'else' statement in 'when' statement");
|
||||
}
|
||||
} else {
|
||||
ast_node(fb, ForeignBlockDecl, src->node);
|
||||
if (fb->foreign_library->kind == Ast_Ident) {
|
||||
ctx.foreign_context.curr_library = fb->foreign_library;
|
||||
} else {
|
||||
error(fb->foreign_library, "Foreign block name must be an identifier or 'export'");
|
||||
ctx.foreign_context.curr_library = nullptr;
|
||||
}
|
||||
check_decl_attributes(&ctx, fb->attributes, foreign_block_decl_attribute, nullptr);
|
||||
src->foreign_context = ctx.foreign_context;
|
||||
}
|
||||
|
||||
// NOTE: resolved before its declarations are collected, which evaluates the attributes of 'foreign import's
|
||||
src->state = EntityState_Resolved;
|
||||
array_pop(&global_decl_source_stack);
|
||||
|
||||
collect_global_decl_source_stmts(&ctx, global_decl_source_taken_stmts(src));
|
||||
|
||||
add_untyped_expressions(ctx.info, &untyped);
|
||||
map_destroy(&untyped);
|
||||
destroy_checker_context(&ctx);
|
||||
}
|
||||
|
||||
gb_internal Entity *force_scope_placeholders(Scope *s, InternedString name, u32 hash) {
|
||||
PtrMap<u64, GlobalDeclSource *> *m = s->placeholders;
|
||||
bool forced = false;
|
||||
for (auto *e = multi_map_find_first(m, cast(u64)name.value); e != nullptr; e = multi_map_find_next(m, e)) {
|
||||
GlobalDeclSource *src = e->value;
|
||||
if (src->state != EntityState_Resolved) {
|
||||
if (global_decl_source_stack.count > 0) {
|
||||
global_decl_source_stack[global_decl_source_stack.count-1].needs = name;
|
||||
}
|
||||
resolve_global_decl_source(src, name);
|
||||
forced = true;
|
||||
}
|
||||
}
|
||||
if (!forced) {
|
||||
return nullptr;
|
||||
}
|
||||
rw_mutex_shared_lock(&s->mutex);
|
||||
Entity *found = scope_map_get(&s->elements, name, hash);
|
||||
rw_mutex_shared_unlock(&s->mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
gb_internal void check_vet_when_shadowing_entity(Entity *e) {
|
||||
if (e == nullptr || e->scope == nullptr || (e->scope->flags & ScopeFlag_File) == 0) {
|
||||
return;
|
||||
}
|
||||
InternedString name = entity_interned_name(e);
|
||||
u32 hash = e->interned_name_hash.load(std::memory_order_relaxed);
|
||||
Scope *outer = e->scope->parent;
|
||||
if (scope_map_get(&e->scope->elements, name, hash) != e) {
|
||||
outer = outer->parent; // in the package scope
|
||||
}
|
||||
if (outer == nullptr) {
|
||||
return;
|
||||
}
|
||||
Entity *shadowed = scope_lookup(outer, name, hash);
|
||||
if (shadowed == nullptr || shadowed == e) {
|
||||
return;
|
||||
}
|
||||
if (shadowed->scope == builtin_pkg->scope) {
|
||||
error(e->token, "Declaration of '%.*s' within a global 'when' shadows the builtin '%.*s'", LIT(e->token.string), LIT(e->token.string));
|
||||
} else {
|
||||
error(e->token, "Declaration of '%.*s' within a global 'when' shadows the declaration at %s", LIT(e->token.string), token_pos_to_string(shadowed->token.pos));
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void check_vet_when_shadowing(void) {
|
||||
for (GlobalDeclSource *src : global_decl_sources) {
|
||||
if (!src->reachable || src->state != EntityState_Resolved) {
|
||||
continue;
|
||||
}
|
||||
if ((ast_file_vet_flags(src->file) & VetFlag_WhenShadowing) == 0 || !is_global_decl_source_in_when(src)) {
|
||||
continue;
|
||||
}
|
||||
for (Ast *decl : global_decl_source_taken_stmts(src)) {
|
||||
if (decl->kind == Ast_ValueDecl) {
|
||||
for (Ast *name : decl->ValueDecl.names) {
|
||||
if (name->kind == Ast_Ident) {
|
||||
check_vet_when_shadowing_entity(name->Ident.entity.load());
|
||||
}
|
||||
}
|
||||
} else if (decl->kind == Ast_ForeignImportDecl) {
|
||||
Token token = decl->ForeignImportDecl.library_name;
|
||||
InternedString name = string_interner_insert(token.string);
|
||||
for (Scope *s = src->file->scope; s != nullptr && s != builtin_pkg->scope; s = s->parent) {
|
||||
Entity *e = scope_map_get(&s->elements, name, name.hash());
|
||||
if (e != nullptr && e->kind == Entity_LibraryName && e->LibraryName.decl == decl) {
|
||||
check_vet_when_shadowing_entity(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholders for every file, then every source resolved in package, file and source order, which
|
||||
// only matters for which errors are reported
|
||||
gb_internal void resolve_global_decl_sources(Checker *c, Array<ImportGraphNode *> const &package_order) {
|
||||
array_init(&global_decl_sources, heap_allocator());
|
||||
array_init(&global_decl_source_stack, heap_allocator());
|
||||
array_init(&global_placeholder_scopes, heap_allocator());
|
||||
init_checker_context(&global_decl_source_export_ctx, c);
|
||||
defer (destroy_checker_context(&global_decl_source_export_ctx));
|
||||
|
||||
u64 stage_start = global_import_stage_begin();
|
||||
for (ImportGraphNode *node : package_order) {
|
||||
for (AstFile *f : node->pkg->files) {
|
||||
scan_global_decl_sources(f, f->decls, nullptr, false, EntityVisiblity_Public);
|
||||
}
|
||||
}
|
||||
global_import_stage_end(GlobalImportStage_Placeholders, stage_start);
|
||||
|
||||
stage_start = global_import_stage_begin();
|
||||
for (GlobalDeclSource *src : global_decl_sources) {
|
||||
resolve_global_decl_source(src, {});
|
||||
}
|
||||
GB_ASSERT(global_decl_source_stack.count == 0);
|
||||
|
||||
for (Scope *s : global_placeholder_scopes) {
|
||||
map_destroy(s->placeholders);
|
||||
s->placeholders = nullptr;
|
||||
}
|
||||
array_clear(&global_placeholder_scopes);
|
||||
|
||||
check_vet_when_shadowing();
|
||||
global_import_stage_end(GlobalImportStage_DeclSources, stage_start);
|
||||
|
||||
map_destroy(&global_decl_source_export_untyped);
|
||||
}
|
||||
@@ -460,6 +460,7 @@ enum BuildFlagKind {
|
||||
BuildFlag_VetSemicolon,
|
||||
BuildFlag_VetCast,
|
||||
BuildFlag_VetTabs,
|
||||
BuildFlag_VetWhenShadowing,
|
||||
BuildFlag_VetPackages,
|
||||
|
||||
BuildFlag_CustomAttribute,
|
||||
@@ -725,6 +726,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetWhenShadowing, str_lit("vet-when-shadowing"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true);
|
||||
@@ -1493,6 +1495,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
|
||||
case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break;
|
||||
case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break;
|
||||
case BuildFlag_VetWhenShadowing: build_context.vet_flags |= VetFlag_WhenShadowing; break;
|
||||
case BuildFlag_VetUnusedProcedures: build_context.vet_flags |= VetFlag_UnusedProcedures; break;
|
||||
|
||||
case BuildFlag_VetPackages:
|
||||
@@ -3408,6 +3411,7 @@ gb_internal int print_show_help(String const arg0, String command, String option
|
||||
print_usage_line(3, "-vet-unused-imports");
|
||||
print_usage_line(3, "-vet-shadowing");
|
||||
print_usage_line(3, "-vet-using-stmt");
|
||||
print_usage_line(3, "-vet-when-shadowing");
|
||||
}
|
||||
|
||||
if (print_flag("-vet-cast")) {
|
||||
@@ -3467,6 +3471,10 @@ gb_internal int print_show_help(String const arg0, String command, String option
|
||||
print_usage_line(2, "Checks for the use of 'using' as a statement.");
|
||||
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
|
||||
}
|
||||
|
||||
if (print_flag("-vet-when-shadowing")) {
|
||||
print_usage_line(2, "Checks for declarations within a global 'when' that shadow a builtin or package-level name.");
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
|
||||
@@ -7243,6 +7243,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s, u64 base_vet_flags)
|
||||
error_line("\tcast\n");
|
||||
error_line("\ttabs\n");
|
||||
error_line("\texplicit-allocators\n");
|
||||
error_line("\twhen-shadowing\n");
|
||||
return vet_flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ enum AstFileFlag : u32 {
|
||||
enum AstDelayQueueKind {
|
||||
AstDelayQueue_Import,
|
||||
AstDelayQueue_Expr,
|
||||
AstDelayQueue_ForeignBlock,
|
||||
AstDelayQueue_COUNT,
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user