exception/error-manager.spice
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | // Std imports | ||
| 4 | import "std/data/vector"; | ||
| 5 | |||
| 6 | // Own imports | ||
| 7 | import "@/ast/ast-nodes"; | ||
| 8 | import "@/exception/semantic-error"; | ||
| 9 | import "@/reader/code-loc"; | ||
| 10 | |||
| 11 | 530 | public type SoftError struct { | |
| 12 | CodeLoc codeLoc | ||
| 13 | String message | ||
| 14 | } | ||
| 15 | |||
| 16 | 265 | public p SoftError.ctor(const CodeLoc& codeLoc, const String& message) { | |
| 17 | 265 | this.codeLoc = codeLoc; | |
| 18 | 265 | this.message = message; | |
| 19 | 265 | } | |
| 20 | |||
| 21 | /** | ||
| 22 | * Collects the errors of a compilation. | ||
| 23 | * | ||
| 24 | * Soft errors are collected and reported together at the end of the type checker (see SourceFile.checkForSoftErrors). | ||
| 25 | * Hard errors abort the compilation. Spice has no exceptions, so a hard error is recorded here and the pipeline aborts with it | ||
| 26 | * after the current stage (see SourceFile.checkForHardError). Only the first hard error is kept, since it ends the | ||
| 27 | * compilation. Recording instead of panicking also keeps hard errors testable. | ||
| 28 | */ | ||
| 29 | 1821 | public type ErrorManager struct { | |
| 30 | public Vector<SoftError> softErrors | ||
| 31 | 1055 | public bool hasHardError = false | |
| 32 | public String hardErrorMessage | ||
| 33 | } | ||
| 34 | |||
| 35 | // Active error manager. Hard errors are raised from anywhere in the compiler, including places that have no access to the | ||
| 36 | // resource manager (e.g. the FunctionManager). | ||
| 37 | ErrorManager* ACTIVE_ERROR_MANAGER; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Make the given error manager the active one. throwSemanticError() records hard errors in it. | ||
| 41 | * | ||
| 42 | * @param errorManager Error manager | ||
| 43 | */ | ||
| 44 | 1055 | public p setActiveErrorManager(ErrorManager* errorManager) { | |
| 45 | 1055 | ACTIVE_ERROR_MANAGER = errorManager; | |
| 46 | 1055 | } | |
| 47 | |||
| 48 | /** | ||
| 49 | * Get the active error manager | ||
| 50 | * | ||
| 51 | * @return Active error manager | ||
| 52 | */ | ||
| 53 | 67 | public f<ErrorManager*> getActiveErrorManager() { | |
| 54 | 67 | assert ACTIVE_ERROR_MANAGER != nil<ErrorManager*>; | |
| 55 | 67 | return ACTIVE_ERROR_MANAGER; | |
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Record a hard error. Only the first hard error is kept, since it aborts the compilation. | ||
| 60 | * | ||
| 61 | * @param astNode AST node, where the error occurred | ||
| 62 | * @param errorType Type of the error | ||
| 63 | * @param message Error message suffix | ||
| 64 | * @param printErrorMessage Whether to append the code snippet of the node to the message | ||
| 65 | */ | ||
| 66 | 96 | public p ErrorManager.addHardError(const ASTNode* astNode, SemanticErrorType errorType, const String& message, bool printErrorMessage = true) { | |
| 67 | 96 | if this.hasHardError { | |
| 68 | 12 | return; | |
| 69 | } | ||
| 70 | 84 | const SemanticError semanticError = SemanticError(astNode, errorType, message.getRaw(), printErrorMessage); | |
| 71 | // Copy the message explicitly. Assigning the reference, returned by getMessage(), would alias the buffer of the | ||
| 72 | // SemanticError, which is freed at the end of this method (see https://github.com/spicelang/spice/issues/1460) | ||
| 73 | 84 | this.hardErrorMessage = String(semanticError.getMessage()); | |
| 74 | 84 | this.hasHardError = true; | |
| 75 | 84 | } | |
| 76 | |||
| 77 | /** | ||
| 78 | * Raise a hard error on the active error manager. The caller has to return afterwards, like after reporting a soft error. | ||
| 79 | * | ||
| 80 | * @param astNode AST node, where the error occurred | ||
| 81 | * @param errorType Type of the error | ||
| 82 | * @param message Error message suffix | ||
| 83 | * @param printErrorMessage Whether to append the code snippet of the node to the message | ||
| 84 | */ | ||
| 85 | 67 | public p throwSemanticError(const ASTNode* astNode, SemanticErrorType errorType, const String& message, bool printErrorMessage = true) { | |
| 86 | 67 | ErrorManager* errorManager = getActiveErrorManager(); | |
| 87 | 67 | errorManager.addHardError(astNode, errorType, message, printErrorMessage); | |
| 88 | 67 | } | |
| 89 | |||
| 90 | 275 | p ErrorManager.addSoftError(const CodeLoc& codeLoc, const String& message) { | |
| 91 | // Avoid duplicate errors | ||
| 92 | 543 | foreach (const SoftError& error : this.softErrors) { | |
| 93 | 278 | if error.codeLoc == codeLoc { | |
| 94 | 10 | return; | |
| 95 | } | ||
| 96 | 268 | } | |
| 97 | 265 | this.softErrors.pushBack(SoftError(codeLoc, message)); | |
| 98 | 265 | } | |
| 99 | |||
| 100 | 275 | public p ErrorManager.addSoftError(const ASTNode* astNode, SemanticErrorType errorType, const String& message) { | |
| 101 | // Build error message | ||
| 102 | 275 | SemanticError semanticError = SemanticError(astNode, errorType, message.getRaw()); | |
| 103 | // Add to soft errors list | ||
| 104 | 275 | this.addSoftError(astNode.codeLoc, semanticError.getMessage()); | |
| 105 | 275 | } | |
| 106 |