mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge pull request #3137 from laytan/show-defineable
Add flags to show/export defineable values and warn if a -define is unused in the project
This commit is contained in:
@@ -844,6 +844,8 @@ struct BuildContext {
|
|||||||
bool show_unused;
|
bool show_unused;
|
||||||
bool show_unused_with_location;
|
bool show_unused_with_location;
|
||||||
bool show_more_timings;
|
bool show_more_timings;
|
||||||
|
bool show_defineables;
|
||||||
|
String export_defineables_file;
|
||||||
bool show_system_calls;
|
bool show_system_calls;
|
||||||
bool keep_temp_files;
|
bool keep_temp_files;
|
||||||
bool ignore_unknown_attributes;
|
bool ignore_unknown_attributes;
|
||||||
|
|||||||
+27
-1
@@ -1756,9 +1756,21 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
|||||||
operand->mode = Addressing_Constant;
|
operand->mode = Addressing_Constant;
|
||||||
operand->value = exact_value_bool(is_defined);
|
operand->value = exact_value_bool(is_defined);
|
||||||
|
|
||||||
|
// If the arg is a selector expression we don't add it, `-define` only allows identifiers.
|
||||||
|
if (arg->kind == Ast_Ident) {
|
||||||
|
Defineable defineable = {};
|
||||||
|
defineable.docs = nullptr;
|
||||||
|
defineable.name = arg->Ident.token.string;
|
||||||
|
defineable.default_value = exact_value_bool(false);
|
||||||
|
defineable.pos = arg->Ident.token.pos;
|
||||||
|
|
||||||
|
MUTEX_GUARD(&c->info->defineables_mutex);
|
||||||
|
array_add(&c->info->defineables, defineable);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (name == "config") {
|
} else if (name == "config") {
|
||||||
if (ce->args.count != 2) {
|
if (ce->args.count != 2) {
|
||||||
error(call, "'#config' expects 2 argument, got %td", ce->args.count);
|
error(call, "'#config' expects 2 arguments, got %td", ce->args.count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Ast *arg = unparen_expr(ce->args[0]);
|
Ast *arg = unparen_expr(ce->args[0]);
|
||||||
@@ -1793,6 +1805,20 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
|||||||
operand->value = found->Constant.value;
|
operand->value = found->Constant.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Defineable defineable = {};
|
||||||
|
defineable.docs = nullptr;
|
||||||
|
defineable.name = name;
|
||||||
|
defineable.default_value = def.value;
|
||||||
|
defineable.pos = arg->Ident.token.pos;
|
||||||
|
|
||||||
|
if (c->decl) {
|
||||||
|
defineable.docs = c->decl->docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
MUTEX_GUARD(&c->info->defineables_mutex);
|
||||||
|
array_add(&c->info->defineables, defineable);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error(call, "Unknown directive call: #%.*s", LIT(name));
|
error(call, "Unknown directive call: #%.*s", LIT(name));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1302,6 +1302,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
|||||||
array_init(&i->init_procedures, a, 0, 0);
|
array_init(&i->init_procedures, a, 0, 0);
|
||||||
array_init(&i->fini_procedures, a, 0, 0);
|
array_init(&i->fini_procedures, a, 0, 0);
|
||||||
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
|
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
|
||||||
|
array_init(&i->defineables, a);
|
||||||
|
|
||||||
map_init(&i->objc_msgSend_types);
|
map_init(&i->objc_msgSend_types);
|
||||||
string_map_init(&i->load_file_cache);
|
string_map_init(&i->load_file_cache);
|
||||||
@@ -1331,6 +1332,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
|
|||||||
string_map_destroy(&i->packages);
|
string_map_destroy(&i->packages);
|
||||||
array_free(&i->variable_init_order);
|
array_free(&i->variable_init_order);
|
||||||
array_free(&i->required_foreign_imports_through_force);
|
array_free(&i->required_foreign_imports_through_force);
|
||||||
|
array_free(&i->defineables);
|
||||||
|
|
||||||
mpsc_destroy(&i->entity_queue);
|
mpsc_destroy(&i->entity_queue);
|
||||||
mpsc_destroy(&i->definition_queue);
|
mpsc_destroy(&i->definition_queue);
|
||||||
|
|||||||
@@ -382,6 +382,17 @@ struct GenTypesData {
|
|||||||
RecursiveMutex mutex;
|
RecursiveMutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Defineable {
|
||||||
|
String name;
|
||||||
|
ExactValue default_value;
|
||||||
|
TokenPos pos;
|
||||||
|
CommentGroup *docs;
|
||||||
|
|
||||||
|
// These strings are only computed from previous fields when defineables are being shown or exported.
|
||||||
|
String default_value_str;
|
||||||
|
String pos_str;
|
||||||
|
};
|
||||||
|
|
||||||
// CheckerInfo stores all the symbol information for a type-checked program
|
// CheckerInfo stores all the symbol information for a type-checked program
|
||||||
struct CheckerInfo {
|
struct CheckerInfo {
|
||||||
Checker *checker;
|
Checker *checker;
|
||||||
@@ -408,6 +419,9 @@ struct CheckerInfo {
|
|||||||
Array<Entity *> entities;
|
Array<Entity *> entities;
|
||||||
Array<Entity *> required_foreign_imports_through_force;
|
Array<Entity *> required_foreign_imports_through_force;
|
||||||
|
|
||||||
|
BlockingMutex defineables_mutex;
|
||||||
|
Array<Defineable> defineables;
|
||||||
|
|
||||||
|
|
||||||
// Below are accessed within procedures
|
// Below are accessed within procedures
|
||||||
RwMutex global_untyped_mutex;
|
RwMutex global_untyped_mutex;
|
||||||
|
|||||||
+156
@@ -288,6 +288,9 @@ enum BuildFlagKind {
|
|||||||
BuildFlag_NoThreadedChecker,
|
BuildFlag_NoThreadedChecker,
|
||||||
BuildFlag_ShowDebugMessages,
|
BuildFlag_ShowDebugMessages,
|
||||||
|
|
||||||
|
BuildFlag_ShowDefineables,
|
||||||
|
BuildFlag_ExportDefineables,
|
||||||
|
|
||||||
BuildFlag_Vet,
|
BuildFlag_Vet,
|
||||||
BuildFlag_VetShadowing,
|
BuildFlag_VetShadowing,
|
||||||
BuildFlag_VetUnused,
|
BuildFlag_VetUnused,
|
||||||
@@ -483,6 +486,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
|||||||
add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
|
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
|
||||||
|
|
||||||
|
add_flag(&build_flags, BuildFlag_ShowDefineables, str_lit("show-defineables"), BuildFlagParam_None, Command__does_check);
|
||||||
|
add_flag(&build_flags, BuildFlag_ExportDefineables, str_lit("export-defineables"), BuildFlagParam_String, Command__does_check);
|
||||||
|
|
||||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
|
||||||
@@ -814,6 +820,24 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case BuildFlag_ShowDefineables: {
|
||||||
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
|
build_context.show_defineables = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BuildFlag_ExportDefineables: {
|
||||||
|
GB_ASSERT(value.kind == ExactValue_String);
|
||||||
|
|
||||||
|
String export_path = string_trim_whitespace(value.value_string);
|
||||||
|
if (is_build_flag_path_valid(export_path)) {
|
||||||
|
build_context.export_defineables_file = path_to_full_path(heap_allocator(), export_path);
|
||||||
|
} else {
|
||||||
|
gb_printf_err("Invalid -export-defineables path, got %.*s\n", LIT(export_path));
|
||||||
|
bad_flags = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case BuildFlag_ShowSystemCalls: {
|
case BuildFlag_ShowSystemCalls: {
|
||||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
build_context.show_system_calls = true;
|
build_context.show_system_calls = true;
|
||||||
@@ -1553,6 +1577,115 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin
|
|||||||
gb_printf("Done.\n");
|
gb_printf("Done.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_internal void check_defines(BuildContext *bc, Checker *c) {
|
||||||
|
for (auto const &entry : bc->defined_values) {
|
||||||
|
String name = make_string_c(entry.key);
|
||||||
|
ExactValue value = entry.value;
|
||||||
|
GB_ASSERT(value.kind != ExactValue_Invalid);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for_array(i, c->info.defineables) {
|
||||||
|
Defineable *def = &c->info.defineables[i];
|
||||||
|
if (def->name == name) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
ERROR_BLOCK();
|
||||||
|
warning(nullptr, "given -define:%.*s is unused in the project", LIT(name));
|
||||||
|
error_line("\tSuggestion: use the -show-defineables flag for an overview of the possible defines\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_internal void temp_alloc_defineable_strings(Checker *c) {
|
||||||
|
for_array(i, c->info.defineables) {
|
||||||
|
Defineable *def = &c->info.defineables[i];
|
||||||
|
def->default_value_str = make_string_c(write_exact_value_to_string(gb_string_make(temporary_allocator(), ""), def->default_value));
|
||||||
|
def->pos_str = make_string_c(token_pos_to_string(def->pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_internal GB_COMPARE_PROC(defineables_cmp) {
|
||||||
|
Defineable *x = (Defineable *)a;
|
||||||
|
Defineable *y = (Defineable *)b;
|
||||||
|
|
||||||
|
int cmp = 0;
|
||||||
|
|
||||||
|
String x_file = get_file_path_string(x->pos.file_id);
|
||||||
|
String y_file = get_file_path_string(y->pos.file_id);
|
||||||
|
cmp = string_compare(x_file, y_file);
|
||||||
|
if (cmp) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i32_cmp(x->pos.offset, y->pos.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_internal void sort_defineables(Checker *c) {
|
||||||
|
gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_internal void export_defineables(Checker *c, String path) {
|
||||||
|
gbFile f = {};
|
||||||
|
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, (char *)path.text);
|
||||||
|
if (err != gbFileError_None) {
|
||||||
|
gb_printf_err("Failed to export defineables to: %.*s\n", LIT(path));
|
||||||
|
gb_exit(1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
gb_printf("Exporting defineables to '%.*s'...\n", LIT(path));
|
||||||
|
}
|
||||||
|
defer (gb_file_close(&f));
|
||||||
|
|
||||||
|
gbString docs = gb_string_make(heap_allocator(), "");
|
||||||
|
defer (gb_string_free(docs));
|
||||||
|
|
||||||
|
gb_fprintf(&f, "Defineable,Default Value,Docs,Location\n");
|
||||||
|
for_array(i, c->info.defineables) {
|
||||||
|
Defineable *def = &c->info.defineables[i];
|
||||||
|
|
||||||
|
gb_string_clear(docs);
|
||||||
|
if (def->docs) {
|
||||||
|
docs = gb_string_appendc(docs, "\"");
|
||||||
|
for (Token const &token : def->docs->list) {
|
||||||
|
for (isize i = 0; i < token.string.len; i++) {
|
||||||
|
u8 c = token.string.text[i];
|
||||||
|
if (c == '"') {
|
||||||
|
docs = gb_string_appendc(docs, "\"\"");
|
||||||
|
} else {
|
||||||
|
docs = gb_string_append_length(docs, &c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
docs = gb_string_appendc(docs, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_fprintf(&f,"%.*s,%.*s,%s,%.*s\n", LIT(def->name), LIT(def->default_value_str), docs, LIT(def->pos_str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_internal void show_defineables(Checker *c) {
|
||||||
|
for_array(i, c->info.defineables) {
|
||||||
|
Defineable *def = &c->info.defineables[i];
|
||||||
|
if (has_ansi_terminal_colours()) {
|
||||||
|
gb_printf("\x1b[0;90m");
|
||||||
|
}
|
||||||
|
printf("%.*s\n", LIT(def->pos_str));
|
||||||
|
if (def->docs) {
|
||||||
|
for (Token const &token : def->docs->list) {
|
||||||
|
gb_printf("%.*s\n", LIT(token.string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_ansi_terminal_colours()) {
|
||||||
|
gb_printf("\x1b[0m");
|
||||||
|
}
|
||||||
|
gb_printf("%.*s :: %.*s\n\n", LIT(def->name), LIT(def->default_value_str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gb_internal void show_timings(Checker *c, Timings *t) {
|
gb_internal void show_timings(Checker *c, Timings *t) {
|
||||||
Parser *p = c->parser;
|
Parser *p = c->parser;
|
||||||
isize lines = p->total_line_count;
|
isize lines = p->total_line_count;
|
||||||
@@ -1955,6 +2088,15 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
|||||||
print_usage_line(2, "Usage in code:");
|
print_usage_line(2, "Usage in code:");
|
||||||
print_usage_line(3, "#config(SPAM, default_value)");
|
print_usage_line(3, "#config(SPAM, default_value)");
|
||||||
print_usage_line(0, "");
|
print_usage_line(0, "");
|
||||||
|
|
||||||
|
print_usage_line(1, "-show-defineables");
|
||||||
|
print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project.");
|
||||||
|
print_usage_line(0, "");
|
||||||
|
|
||||||
|
print_usage_line(1, "-export-defineables:<filename>");
|
||||||
|
print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path.");
|
||||||
|
print_usage_line(2, "Example: -export-defineables:defineables.csv");
|
||||||
|
print_usage_line(0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (build) {
|
if (build) {
|
||||||
@@ -2953,6 +3095,7 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
defer (destroy_checker(checker));
|
defer (destroy_checker(checker));
|
||||||
|
|
||||||
check_parsed_files(checker);
|
check_parsed_files(checker);
|
||||||
|
check_defines(&build_context, checker);
|
||||||
if (any_errors()) {
|
if (any_errors()) {
|
||||||
print_all_errors();
|
print_all_errors();
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2961,6 +3104,19 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
print_all_errors();
|
print_all_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (build_context.show_defineables || build_context.export_defineables_file != "") {
|
||||||
|
TEMPORARY_ALLOCATOR_GUARD();
|
||||||
|
temp_alloc_defineable_strings(checker);
|
||||||
|
sort_defineables(checker);
|
||||||
|
|
||||||
|
if (build_context.show_defineables) {
|
||||||
|
show_defineables(checker);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.export_defineables_file != "") {
|
||||||
|
export_defineables(checker, build_context.export_defineables_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (build_context.command_kind == Command_strip_semicolon) {
|
if (build_context.command_kind == Command_strip_semicolon) {
|
||||||
return strip_semicolons(parser);
|
return strip_semicolons(parser);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ set TEST_ARGS=-fast-tests
|
|||||||
set TEST_ARGS=-no-random
|
set TEST_ARGS=-no-random
|
||||||
set TEST_ARGS=
|
set TEST_ARGS=
|
||||||
set OUT_NAME=math_big_test_library.dll
|
set OUT_NAME=math_big_test_library.dll
|
||||||
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style -define:ODIN_TEST_FANCY=false
|
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/big tests
|
echo Running core:math/big tests
|
||||||
echo ---
|
echo ---
|
||||||
|
|||||||
Reference in New Issue
Block a user