/*! @file src/Error/Error.cpp @brief 错误报告实现 @author PuqiAR (im@puqiar.top) @date 2026-02-14 */ #include #include #include namespace Fig { void ColoredPrint(const char *color, const char *msg, std::ostream &ost = CoreIO::GetStdErr()) { ost << color << msg << TerminalColors::Reset; } void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr()) { ost << color << msg << TerminalColors::Reset; } void ColoredPrint(const char *color, const String &msg, std::ostream &ost = CoreIO::GetStdErr()) { ost << color << msg << TerminalColors::Reset; } std::string MultipleStr(const char *c, size_t n) { std::string buf; for (size_t i = 0; i < n; ++i) { buf += c; } return buf; } const char *ErrorTypeToString(ErrorType type) { using enum ErrorType; switch (type) { case UnusedSymbol: return "UnusedSymbol"; case UnnecessarySemicolon: return "UnnecessarySemicolon"; case MayBeNull: return "MaybeNull"; case UnterminatedString: return "UnterminatedString"; case UnterminatedComments: return "UnterminatedComments"; case InvalidNumberLiteral: return "InvalidNumberLiteral"; case InvalidCharacter: return "InvalidCharacter"; case InvalidSymbol: return "InvalidSymbol"; case ExpectedExpression: return "ExpectedExpression"; case SyntaxError: return "SyntaxError"; case RedeclarationError: return "RedeclarationError"; case UseUndeclaredIdentifier: return "UseUndeclaredIdentifier"; case NotAnLvalue: return "NotAnLvalue"; case TypeError: return "TypeError"; case TooManyLocals: return "TooManyLocals"; case TooManyConstants: return "TooManyConstants"; case RegisterOverflow: return "RegisterOverflow"; case InternalError: return "InternalError"; // default: return "Some one forgot to add case to `ErrorTypeToString`"; } return "UnknownError"; } void PrintSystemInfos() { std::ostream &err = CoreIO::GetStdErr(); std::stringstream build_info; build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '[' << Core::COMPILER << ']' << '\n' << " Build Time: " << Core::COMPILE_TIME; const std::string &build_info_str = build_info.str(); err << MultipleStr("─", build_info_str.size()) << '\n'; err << build_info_str << '\n'; err << MultipleStr("─", build_info_str.size()) << '\n'; } void PrintErrorInfo(const Error &error, const SourceManager &srcManager) { static constexpr const char *MinorColor = "\033[38;2;138;227;198m"; static constexpr const char *MediumColor = "\033[38;2;255;199;95m"; static constexpr const char *CriticalColor = "\033[38;2;255;107;107m"; namespace TC = TerminalColors; std::ostream &err = CoreIO::GetStdErr(); uint8_t level = ErrorLevel(error.type); // const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical")); const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor)); err << "🔥 " << level_color //<< '(' << level_name << ')' << 'E' << static_cast(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type) << TC::Reset << '\n'; const SourceLocation &location = error.location; err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset << '\n'; err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n'; // 尝试打印上3行 下2行 int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2; while (!srcManager.HasLine(line_end)) { --line_end; } while (!srcManager.HasLine(line_start)) { ++line_start; } const auto &getLineNumWidth = [](size_t l) { unsigned int cnt = 0; while (l != 0) { l = l / 10; cnt++; } return cnt; }; unsigned int max_line_number_width = getLineNumWidth(line_end); for (size_t i = line_start; i <= line_end; ++i) { unsigned int offset = 2 + 2 + 1; // ' └─ ' if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; } else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; } else { err << MultipleStr(" ", offset); } unsigned int cur_line_number_width = getLineNumWidth(i); err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset; err << " │ " << srcManager.GetLine(i) << '\n'; if (i == location.sp.line) { unsigned int error_col_offset = offset + 1 + max_line_number_width + 2; err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen << MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n'; err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color << error.message << TC::Reset << "\n\n"; } } err << "\n"; err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " (" << error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n"; err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset; err << '\n'; } void ReportError(const Error &error, const SourceManager &srcManager) { assert(srcManager.read && "ReportError: srcManager doesn't read source"); // assert(srcManager.HasLine(error.location.sp.line)); PrintSystemInfos(); PrintErrorInfo(error, srcManager); } void ReportErrors(const std::vector &errors, const SourceManager &srcManager) { std::ostream &ost = CoreIO::GetStdErr(); PrintSystemInfos(); for (const auto &err : errors) { PrintErrorInfo(err, srcManager); ost << '\n'; } } }; // namespace Fig