diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4f06c2913..5c1babe0c 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -143,6 +143,7 @@ struct BuildContext { bool generate_docs; i32 optimization_level; bool show_timings; + bool show_unused; bool show_more_timings; bool show_system_calls; bool keep_temp_files; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index da07fe4bc..5234955fb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1007,11 +1007,10 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) continue; } - if (ptr_set_exists(&entity_set, e)) { + if (ptr_set_update(&entity_set, e)) { error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string)); continue; } - ptr_set_add(&entity_set, e); array_add(&pge->entities, e); } diff --git a/src/checker.cpp b/src/checker.cpp index f02b927c3..f8018506c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -700,7 +700,7 @@ void init_universal(void) { builtin_pkg->kind = Package_Normal; builtin_pkg->scope = create_scope(nullptr); - builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; builtin_pkg->scope->pkg = builtin_pkg; intrinsics_pkg = gb_alloc_item(a, AstPackage); @@ -708,7 +708,7 @@ void init_universal(void) { intrinsics_pkg->kind = Package_Normal; intrinsics_pkg->scope = create_scope(nullptr); - intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; intrinsics_pkg->scope->pkg = intrinsics_pkg; config_pkg = gb_alloc_item(a, AstPackage); @@ -716,7 +716,7 @@ void init_universal(void) { config_pkg->kind = Package_Normal; config_pkg->scope = create_scope(nullptr); - config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global; + config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; config_pkg->scope->pkg = config_pkg; @@ -1501,11 +1501,10 @@ void add_min_dep_type_info(Checker *c, Type *t) { ti_index = type_info_index(&c->info, t, false); } GB_ASSERT(ti_index >= 0); - if (ptr_set_exists(set, ti_index)) { + if (ptr_set_update(set, ti_index)) { // Type Already exists return; } - ptr_set_add(set, ti_index); // Add nested types if (t->kind == Type_Named) { @@ -1688,12 +1687,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) { } } - if (ptr_set_exists(set, entity)) { + if (ptr_set_update(set, entity)) { return; } - - ptr_set_add(set, entity); DeclInfo *decl = decl_info_of_entity(entity); if (decl == nullptr) { return; @@ -3567,11 +3564,9 @@ struct ImportPathItem { Array find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet *visited) { Array empty_path = {}; - if (ptr_set_exists(visited, start)) { + if (ptr_set_update(visited, start)) { return empty_path; } - ptr_set_add(visited, start); - String path = start->fullpath; AstPackage **found = string_map_get(&c->info.packages, path); @@ -3657,10 +3652,8 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { GB_ASSERT(scope->flags&ScopeFlag_Pkg); - if (ptr_set_exists(&parent_scope->imported, scope)) { + if (ptr_set_update(&parent_scope->imported, scope)) { // error(token, "Multiple import of the same file within this scope"); - } else { - ptr_set_add(&parent_scope->imported, scope); } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); @@ -4013,10 +4006,9 @@ void check_import_entities(Checker *c) { if (pkg == nullptr) { continue; } - if (ptr_set_exists(&emitted, pkg)) { + if (ptr_set_update(&emitted, pkg)) { continue; } - ptr_set_add(&emitted, pkg); array_add(&package_order, n); } @@ -4259,11 +4251,9 @@ void calculate_global_init_order(Checker *c) { // if (!decl_info_has_init(d)) { // continue; // } - if (ptr_set_exists(&emitted, d)) { + if (ptr_set_update(&emitted, d)) { continue; } - ptr_set_add(&emitted, d); - array_add(&info->variable_init_order, d); } diff --git a/src/checker.hpp b/src/checker.hpp index 9c9b77ac3..e672a477b 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -160,12 +160,13 @@ struct ProcInfo { enum ScopeFlag : i32 { - ScopeFlag_Pkg = 1<<1, - ScopeFlag_Global = 1<<2, - ScopeFlag_File = 1<<3, - ScopeFlag_Init = 1<<4, - ScopeFlag_Proc = 1<<5, - ScopeFlag_Type = 1<<6, + ScopeFlag_Pkg = 1<<1, + ScopeFlag_Builtin = 1<<2, + ScopeFlag_Global = 1<<3, + ScopeFlag_File = 1<<4, + ScopeFlag_Init = 1<<5, + ScopeFlag_Proc = 1<<6, + ScopeFlag_Type = 1<<7, ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes diff --git a/src/main.cpp b/src/main.cpp index 67dc5da00..17477384e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -568,6 +568,7 @@ enum BuildFlagKind { BuildFlag_OutFile, BuildFlag_OptimizationLevel, BuildFlag_ShowTimings, + BuildFlag_ShowUnused, BuildFlag_ShowMoreTimings, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, @@ -669,6 +670,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); + add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer); @@ -860,6 +862,14 @@ bool parse_build_flags(Array args) { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; break; + case BuildFlag_ShowUnused: + GB_ASSERT(value.kind == ExactValue_Invalid); + build_context.show_unused = true; + if (build_context.command != "check") { + gb_printf_err("%.*s is only allowed with 'odin check'\n", LIT(name)); + bad_flags = true; + } + break; case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; @@ -1487,6 +1497,7 @@ void print_show_help(String const arg0, String const &command) { bool build = command == "build"; bool run_or_build = command == "run" || command == "build"; + bool check_only = command == "check"; bool check = command == "run" || command == "build" || command == "check"; print_usage_line(0, ""); @@ -1521,6 +1532,12 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); } + if (check_only) { + print_usage_line(1, "-show-unused"); + print_usage_line(2, "Shows unused package declarations within the current project"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-keep-temp-files"); print_usage_line(2, "Keeps the temporary files generated during compilation"); @@ -1599,6 +1616,23 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-extra-linker-flags:"); print_usage_line(2, "Adds extra linker specific flags in a string"); print_usage_line(0, ""); + + print_usage_line(1, "-microarch:"); + print_usage_line(2, "Specifies the specific micro-architecture for the build in a string"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-microarch:sandybridge"); + print_usage_line(3, "-microarch:native"); + print_usage_line(0, ""); + } + + if (check) { + print_usage_line(1, "-disallow-do"); + print_usage_line(2, "Disallows the 'do' keyword in the project"); + print_usage_line(0, ""); + + print_usage_line(1, "-default-to-nil-allocator"); + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); + print_usage_line(0, ""); } if (run_or_build) { @@ -1632,6 +1666,127 @@ void print_show_help(String const arg0, String const &command) { } } +int unused_entity_kind_ordering[Entity_Count] = { + /*Invalid*/ -1, + /*Constant*/ 0, + /*Variable*/ 1, + /*TypeName*/ 4, + /*Procedure*/ 2, + /*ProcGroup*/ 3, + /*Builtin*/ -1, + /*ImportName*/ -1, + /*LibraryName*/ -1, + /*Nil*/ -1, + /*Label*/ -1, +}; +char const *unused_entity_names[Entity_Count] = { + /*Invalid*/ "", + /*Constant*/ "constants", + /*Variable*/ "variables", + /*TypeName*/ "types", + /*Procedure*/ "procedures", + /*ProcGroup*/ "proc_group", + /*Builtin*/ "", + /*ImportName*/ "import names", + /*LibraryName*/ "library names", + /*Nil*/ "", + /*Label*/ "", +}; + + +GB_COMPARE_PROC(cmp_entities_for_unused) { + GB_ASSERT(a != nullptr); + GB_ASSERT(b != nullptr); + Entity *x = *cast(Entity **)a; + Entity *y = *cast(Entity **)b; + int res = 0; + res = string_compare(x->pkg->name, y->pkg->name); + if (res != 0) { + return res; + } + int ox = unused_entity_kind_ordering[x->kind]; + int oy = unused_entity_kind_ordering[y->kind]; + if (ox < oy) { + return -1; + } else if (ox > oy) { + return +1; + } + res = string_compare(x->token.string, y->token.string); + return res; +} + + +void print_show_unused(Checker *c) { + CheckerInfo *info = &c->info; + + auto unused = array_make(permanent_allocator(), 0, info->entities.count); + for_array(i, info->entities) { + Entity *e = info->entities[i]; + if (e == nullptr) { + continue; + } + if (e->pkg == nullptr || e->pkg->scope == nullptr) { + continue; + } + if (e->pkg->scope->flags & ScopeFlag_Builtin) { + continue; + } + switch (e->kind) { + case Entity_Invalid: + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + case Entity_Constant: + case Entity_Variable: + case Entity_TypeName: + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_ImportName: + case Entity_LibraryName: + // Fine + break; + } + if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) { + continue; + } + if (e->token.string.len == 0) { + continue; + } + if (e->token.string == "_") { + continue; + } + if (ptr_set_exists(&info->minimum_dependency_set, e)) { + continue; + } + array_add(&unused, e); + } + + gb_sort_array(unused.data, unused.count, cmp_entities_for_unused); + + print_usage_line(0, "Unused Package Declarations"); + + AstPackage *curr_pkg = nullptr; + EntityKind curr_entity_kind = Entity_Invalid; + for_array(i, unused) { + Entity *e = unused[i]; + if (curr_pkg != e->pkg) { + curr_pkg = e->pkg; + curr_entity_kind = Entity_Invalid; + print_usage_line(0, ""); + print_usage_line(0, "package %.*s", LIT(curr_pkg->name)); + } + if (curr_entity_kind != e->kind) { + curr_entity_kind = e->kind; + print_usage_line(1, "%s", unused_entity_names[e->kind]); + } + // TokenPos pos = e->token.pos; + // print_usage_line(2, "%.*s(%td:%td) %.*s", LIT(pos.file), pos.line, pos.column, LIT(e->token.string)); + print_usage_line(2, "%.*s", LIT(e->token.string)); + } + print_usage_line(0, ""); +} + int main(int arg_count, char const **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -1821,6 +1976,10 @@ int main(int arg_count, char const **arg_ptr) { temp_allocator_free_all(&temporary_allocator_data); if (build_context.no_output_files) { + if (build_context.show_unused) { + print_show_unused(&checker); + } + if (build_context.query_data_set_settings.ok) { generate_and_print_query_data(&checker, timings); } else { diff --git a/src/parser.cpp b/src/parser.cpp index cf464f149..4470f979b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1099,7 +1099,7 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) { } Ast *ast_value_decl(AstFile *f, Array const &names, Ast *type, Array const &values, bool is_mutable, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ValueDecl); result->ValueDecl.names = slice_from_array(names); result->ValueDecl.type = type; @@ -1122,7 +1122,7 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C } Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ImportDecl); result->ImportDecl.token = token; result->ImportDecl.is_using = is_using; @@ -1134,7 +1134,7 @@ Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Toke } Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, Token library_name, - CommentGroup *docs, CommentGroup *comment) { + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; result->ForeignImportDecl.filepaths = slice_from_array(filepaths); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index e75202663..5432fa094 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -24,6 +24,7 @@ struct PtrSet { template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy(PtrSet *s); template T ptr_set_add (PtrSet *s, T ptr); +template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existsed template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); @@ -152,9 +153,7 @@ T ptr_set_add(PtrSet *s, T ptr) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); - if (fr.entry_index != PTR_SET_SENTINEL) { - index = fr.entry_index; - } else { + if (fr.entry_index == PTR_SET_SENTINEL) { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev != PTR_SET_SENTINEL) { s->entries.data[fr.entry_prev].next = index; @@ -168,6 +167,32 @@ T ptr_set_add(PtrSet *s, T ptr) { return ptr; } +template +bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it previously existsed + bool exists = false; + PtrSetIndex index; + PtrSetFindResult fr; + if (s->hashes.count == 0) { + ptr_set_grow(s); + } + fr = ptr_set__find(s, ptr); + if (fr.entry_index != PTR_SET_SENTINEL) { + exists = true; + } else { + index = ptr_set__add_entry(s, ptr); + if (fr.entry_prev != PTR_SET_SENTINEL) { + s->entries.data[fr.entry_prev].next = index; + } else { + s->hashes.data[fr.hash_index] = index; + } + } + if (ptr_set__full(s)) { + ptr_set_grow(s); + } + return exists; +} + + template void ptr_set__erase(PtrSet *s, PtrSetFindResult fr) {