added '#deprecated'; revamped error/warning system
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 27 Sep 2022 14:37:34 +0000 (09:37 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 27 Sep 2022 14:37:34 +0000 (09:37 -0500)
compiler/include/astnodes.h
compiler/include/errors.h
compiler/src/checker.c
compiler/src/errors.c
compiler/src/onyx.c
compiler/src/parser.c
compiler/src/symres.c
core/io/reader.onyx
core/string.onyx
core/test/testing.onyx
scripts/onyx-pkg.onyx

index 66f0a2b778fa5a606db3ce46e7b36a9adc4ada66..f52e2f003eaeea4079c4a6ebf8f4ffdd91b8dd6e 100644 (file)
@@ -1251,6 +1251,8 @@ struct AstFunction {
 
     bh_arr(AstTyped *) tags;
 
+    AstStrLit *deprecated_warning;
+
     // Polymorphic procedures use the following fields
     Scope *parent_scope_of_poly_proc;
     bh_arr(AstPolyParam) poly_params;
index 9df95deffb17cd6e461d0d7dc034bd291b716687..c9420d466f88b1954c0db7bbffa6cc3acbecdcba 100644 (file)
@@ -33,6 +33,8 @@ typedef struct OnyxErrors {
 extern OnyxErrors msgs;
 
 void onyx_errors_init(bh_arr(bh_file_contents)* files);
+void onyx_errors_enable();
+void onyx_errors_disable();
 void onyx_submit_error(OnyxError error);
 void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...);
 void onyx_submit_warning(OnyxError error);
index ddc3977b68dd8a61804cf09a618d57054539cf51..e184f08babbc8fa2776a65119c6bcf50f1cc2439 100644 (file)
@@ -761,6 +761,13 @@ CheckStatus check_call(AstCall** pcall) {
         return Check_Return_To_Symres;
     }
 
+    if (callee->kind == Ast_Kind_Function && callee->deprecated_warning) {
+        onyx_report_warning(callee->token->pos, "Calling a deprecated function: %b",
+            callee->deprecated_warning->token->text, callee->deprecated_warning->token->length);
+
+        onyx_report_warning(call->token->pos, "Here is where the deprecated function was called.");
+    }
+
     return Check_Success;
 }
 
@@ -1758,7 +1765,7 @@ CheckStatus check_field_access(AstFieldAccess** pfield) {
     // constraints relay on Check_Error being returned, not Check_Yield_Macro. For
     // this reason, I have to produce an error at the last minute, BEFORE the loop
     // enters a cycle detected state, when there is no point of return.
-    if (!context.cycle_almost_detected) {
+    if (!context.cycle_almost_detected && !context.cycle_detected) {
         // Skipping the slightly expensive symbol lookup
         // below by not using YIELD_ERROR.
         return Check_Yield_Macro;
@@ -2430,15 +2437,14 @@ CheckStatus check_struct_defaults(AstStructType* s_node) {
 }
 
 CheckStatus check_temp_function_header(AstFunction* func) {
-    CheckStatus cs = check_function_header(func);
-    if (cs == Check_Error) {
-        if (func->flags & Ast_Flag_Header_Check_No_Error) {
-            onyx_clear_errors();
-        }
-
-        return Check_Failed;
+    if (func->flags & Ast_Flag_Header_Check_No_Error) {
+        onyx_errors_disable();
     }
 
+    CheckStatus cs = check_function_header(func);
+    onyx_errors_enable();
+
+    if (cs == Check_Error)  return Check_Failed;
     if (cs != Check_Success) return cs;
 
     return Check_Complete;
@@ -2543,6 +2549,13 @@ CheckStatus check_function_header(AstFunction* func) {
 
     if (func->return_type != NULL) CHECK(type, &func->return_type);
 
+    if (func->deprecated_warning) {
+        CHECK(expression, (AstTyped **) &func->deprecated_warning);
+        if (func->deprecated_warning->kind != Ast_Kind_StrLit) {
+            ERROR(func->token->pos, "Expected deprecation warning to be a string literal.");
+        }
+    }
+
     func->type = type_build_function_type(context.ast_alloc, func);
     if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
 
@@ -2878,12 +2891,14 @@ CheckStatus check_constraint(AstConstraint *constraint) {
         }
 
         case Constraint_Phase_Checking_Expressions: {
+            onyx_errors_disable();
+
             fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
                 InterfaceConstraint* ic = &constraint->exprs[i];
 
                 CheckStatus cs = check_expression(&ic->expr);
                 if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
-                    onyx_clear_errors();
+                    onyx_errors_enable();
                     return cs;
                 }
 
@@ -2898,6 +2913,7 @@ CheckStatus check_constraint(AstConstraint *constraint) {
                 if (ic->expected_type_expr) {
                     cs = check_type(&ic->expected_type_expr);
                     if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+                        onyx_errors_enable();
                         return cs;
                     }
 
@@ -2923,6 +2939,7 @@ CheckStatus check_constraint(AstConstraint *constraint) {
                             ic->expected_type = type_lookup_by_id(ic->expected_type_expr->type_id);
 
                         } else {
+                            onyx_errors_enable();
                             YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
                         }
                     }
@@ -2937,19 +2954,19 @@ CheckStatus check_constraint(AstConstraint *constraint) {
                 continue;
 
               constraint_error:
-                // HACK HACK HACK
-                onyx_clear_errors();
+                onyx_errors_enable();
                 *constraint->report_status = Constraint_Check_Status_Failed;
                 return Check_Failed;
             }
 
             // HACK HACK HACK
-            onyx_clear_errors();
+            onyx_errors_enable();
             *constraint->report_status = Constraint_Check_Status_Success;
             return Check_Complete;
         }
     }
 
+    onyx_errors_enable();
     return Check_Success;
 }
 
index f6811ca661b3d817b2031ed2efc374999635c84f..35731588b14e9894e82a9026ac67ec2d8fa0a046 100644 (file)
@@ -2,6 +2,7 @@
 #include "utils.h"
 
 OnyxErrors errors;
+static b32 errors_enabled = 1;
 
 void onyx_errors_init(bh_arr(bh_file_contents)* files) {
     errors.file_contents = files;
@@ -84,8 +85,25 @@ void onyx_errors_print() {
     }
 }
 
+void onyx_errors_enable() {
+    errors_enabled = 1;
+}
+
+void onyx_errors_disable() {
+    if (context.cycle_detected) {
+        errors_enabled = 1;
+        return;
+    }
+    
+    errors_enabled = 0;
+}
+
 b32 onyx_has_errors() {
-    return bh_arr_length(errors.errors) > 0;
+    bh_arr_each(OnyxError, err, errors.errors) {
+        if (err->rank >= Error_Waiting_On) return 1;
+    }
+
+    return 0;
 }
 
 void onyx_clear_errors() {
@@ -95,10 +113,13 @@ void onyx_clear_errors() {
 }
 
 void onyx_submit_error(OnyxError error) {
+    if (!errors_enabled) return;
+
     bh_arr_push(errors.errors, error);
 }
 
 void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...) {
+    if (!errors_enabled) return;
 
     va_list vargs;
     va_start(vargs, format);
@@ -115,6 +136,8 @@ void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...)
 }
 
 void onyx_submit_warning(OnyxError error) {
+    if (!errors_enabled) return;
+
     bh_file_contents file_contents = { 0 };
     bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
         if (!strcmp(fc->filename, error.pos.filename)) {
@@ -128,6 +151,8 @@ void onyx_submit_warning(OnyxError error) {
 
 // This definitely doesn't do what I thought it did?
 void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
+    if (!errors_enabled) return;
+
     va_list vargs;
     va_start(vargs, format);
     char* msg = bh_bprintf_va(format, vargs);
@@ -136,7 +161,7 @@ void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
     OnyxError err = {
         .pos = pos,
         .rank = Error_Warning,
-        .text = msg,
+        .text = bh_strdup(errors.msg_alloc, msg),
     };
 
     bh_arr_push(errors.errors, err);
index 0f3790baa5acdb68b98c274312ef06b584002b8f..628adfb4f3a8fbf56cb75d80eb1fc9728e86119f 100644 (file)
@@ -609,6 +609,10 @@ static i32 onyx_compile() {
         }
 #endif
 
+        // Mostly a preventative thing to ensure that even if somehow
+        // errors were left disabled, they are re-enabled in this cycle.
+        onyx_errors_enable();
+
         entity_heap_remove_top(&context.entities);
         b32 changed = process_entity(ent);
 
@@ -656,6 +660,7 @@ static i32 onyx_compile() {
             entity_heap_insert_existing(&context.entities, ent);
     }
 
+    onyx_errors_print();
     u64 duration = bh_time_duration(start_time);
 
     if (context.options->verbose_output > 0) {
@@ -798,7 +803,6 @@ int main(int argc, char *argv[]) {
 
     switch (compiler_progress) {
         case ONYX_COMPILER_PROGRESS_ERROR:
-            onyx_errors_print();
             break;
 
         case ONYX_COMPILER_PROGRESS_FAILED_OUTPUT:
index 7ffd0912fa305a28c632a0f70bffef39ac5c2d8c..d5309853f5345e277871273cca99c018c06ea981 100644 (file)
@@ -2501,6 +2501,10 @@ static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* tok
             func_def->flags |= Ast_Flag_Proc_Is_Null;
         }
 
+        else if (parse_possible_directive(parser, "deprecated")) {
+            func_def->deprecated_warning = (AstStrLit *) parse_expression(parser, 0);
+        }
+
         else {
             OnyxToken* directive_token = expect_token(parser, '#');
             OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
index 7042740ffe1ac707896ca60d43e17d9d23d1aad9..81481d6bd424a998732d9191a785cb2202e9b3c2 100644 (file)
@@ -1133,6 +1133,10 @@ SymresStatus symres_function_header(AstFunction* func) {
 
     SYMRES(type, &func->return_type);
 
+    if (func->deprecated_warning) {
+        SYMRES(expression, (AstTyped **) &func->deprecated_warning);
+    }
+
     scope_leave();
 
     return Symres_Success;
@@ -1598,12 +1602,12 @@ static SymresStatus symres_polyquery(AstPolyQuery *query) {
         if (param->local->type_node != NULL) {
             resolved_a_symbol = 0;
 
+            onyx_errors_disable();
             param->local->flags |= Ast_Flag_Symbol_Invisible;
             symres_type(&param->local->type_node);
             param->local->flags &= ~Ast_Flag_Symbol_Invisible;
-
-            onyx_clear_errors();
-
+            onyx_errors_enable();
+            
             if (resolved_a_symbol) query->successful_symres = 1;
         }
     }
index 31c8aae4ce20586d2ca9d08f942afc17becf93b3..e894bd2331144a2fa58247c70cf7db4176271ead 100644 (file)
@@ -24,6 +24,7 @@ Reader :: struct {
     greedy : bool;
 
     is_empty :: reader_empty;
+    empty    :: reader_empty;
     read_all :: read_all;
     read_byte :: read_byte;
     unread_byte :: unread_byte;
index 578f32fa0267189b7a883c97d3e4a737ba3c1ee1..0b44023bd4d76ee321d8c6ff6dd5ceb07fb936af 100644 (file)
@@ -197,7 +197,11 @@ ends_with :: (s: str, suffix: str) -> bool {
     return true;
 }
 
-is_empty :: (s: str) => s.count == 0 || s.data == null;
+empty    :: (s: str) => s.count == 0 || s.data == null;
+
+is_empty :: (s: str) -> bool #deprecated "Use 'string.empty' instead." {
+    s.count == 0 || s.data == null;
+}
 
 index_of :: (s: str, c: u8) -> i32 {
     for s.count {
index 6ee3e2b6f262d99e6244b88c300f287a06c2702d..d786c13bed211169a844b775dfc6f8e6ad133fdf 100644 (file)
@@ -73,13 +73,13 @@ run_tests :: (packages: [] package_id = .[], log := true) -> bool {
         if failed_cases.count > 0 do printf("Failing test cases:\n");
 
         for failed_cases {
-            printf("[{}]\n", it.name if !string.is_empty(it.name) else "Unnamed test");
+            printf("[{}]\n", it.name if !string.empty(it.name) else "Unnamed test");
 
             for ^ it.assertions {
                 if it.passed do continue;
 
                 printf("    {} at {}:{}\n",
-                    it.name if !string.is_empty(it.name) else "Unnamed assertion",
+                    it.name if !string.empty(it.name) else "Unnamed assertion",
                     it.loc.file, it.loc.line);
             }
         }
index 3ea34de96bbfd784b77d33fcd2dee07a05d1865d..926040edcb6149a324e46af51e20b2158cfca775 100644 (file)
@@ -494,7 +494,7 @@ attempt_remove_native_library :: (package_folder: str) -> bool {
         result, error := parse_ini_file(^r, ^inner_config);
 
         if result != .Success do return false;
-        if string.is_empty(inner_config.native_library.library) do return false;
+        if string.empty(inner_config.native_library.library) do return false;
 
         os.remove_file(tprintf("{}/{}{}", config.config.lib_bin_directory, inner_config.native_library.library, native_library_suffix));
     }
@@ -528,7 +528,7 @@ run_native_library_installation :: (folder: str) -> bool {
         result, error := parse_ini_file(^r, ^inner_config);
 
         if result != .Success do return false;
-        if string.is_empty(inner_config.native_library.build_cmd) do return true;
+        if string.empty(inner_config.native_library.build_cmd) do return true;
 
         args := string.split(inner_config.native_library.build_cmd, #char " ", context.temp_allocator);
         cmd  := args[0];
@@ -873,7 +873,7 @@ Config :: struct {
 
 load_config_file :: () -> bool {
     file_data := os.get_contents(global_arguments.config_file);
-    if string.is_empty(file_data) {
+    if string.empty(file_data) {
         return false;
     }