diff --git a/CHANGELOG.md b/CHANGELOG.md index 28bc4138..12ccef76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `:?s` to format as an escaped quoted string, - `:s` to format as a quoted string - `format` can use format specifiers for integers: `b`, `#b`, `B`, `#B`, `c`, `d`, `o`, `x`, `#x`, `X`, and `#X` if the argument is an integer +- display a warning to `stderr` when using a deprecated function/value (checks for `@deprecated` inside the attached comment of functions / values) ### Changed diff --git a/include/Ark/Compiler/AST/Optimizer.hpp b/include/Ark/Compiler/AST/Optimizer.hpp index b76a2e41..ab9f9cef 100644 --- a/include/Ark/Compiler/AST/Optimizer.hpp +++ b/include/Ark/Compiler/AST/Optimizer.hpp @@ -40,14 +40,14 @@ namespace Ark::internal * * @param ast */ - void process(const Node& ast) override; + void process(const Node& ast); /** * @brief Returns the modified AST * * @return const Node& */ - [[nodiscard]] const Node& ast() const noexcept override; + [[nodiscard]] const Node& ast() const noexcept; private: Node m_ast; diff --git a/include/Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp b/include/Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp index 82131cc3..be3943fd 100644 --- a/include/Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp +++ b/include/Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp @@ -17,12 +17,13 @@ #include #include #include +#include #include #include namespace Ark::internal { - class ARK_API IRCompiler final + class ARK_API IRCompiler final : public Pass { public: /** @@ -56,7 +57,6 @@ namespace Ark::internal [[nodiscard]] const bytecode_t& bytecode() const noexcept; private: - Logger m_logger; bytecode_t m_bytecode; std::vector m_ir; std::vector m_filenames; diff --git a/include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp b/include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp index dd293970..4bfd41e5 100644 --- a/include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp +++ b/include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,7 @@ namespace Ark::internal std::size_t offset; }; - class ARK_API IROptimizer final + class ARK_API IROptimizer final : public Pass { public: /** @@ -87,7 +88,6 @@ namespace Ark::internal std::vector m_ruleset; - Logger m_logger; std::vector m_ir; std::vector m_symbols; std::vector m_values; diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index 11d96085..a4b5c5f8 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ namespace Ark::internal * @brief The ArkScript AST to IR compiler * */ - class ARK_API ASTLowerer final + class ARK_API ASTLowerer final : public Pass { public: /** @@ -108,8 +109,6 @@ namespace Ark::internal IR::label_t m_current_label = 0; std::stack m_opened_vars; ///< stack of vars we are currently declaring - Logger m_logger; - enum class ErrorKind { InvalidNodeMacro, @@ -224,7 +223,7 @@ namespace Ark::internal * @param message * @param node */ - static void warning(const std::string& message, const Node& node); + void warning(const std::string& message, const Node& node); /** * @brief Throw a nice error message diff --git a/include/Ark/Compiler/Macros/Processor.hpp b/include/Ark/Compiler/Macros/Processor.hpp index 44155b2d..9d065a29 100644 --- a/include/Ark/Compiler/Macros/Processor.hpp +++ b/include/Ark/Compiler/Macros/Processor.hpp @@ -43,14 +43,14 @@ namespace Ark::internal * * @param ast */ - void process(const Node& ast) override; + void process(const Node& ast); /** * @brief Return the modified AST * * @return Node& */ - [[nodiscard]] const Node& ast() const noexcept override; + [[nodiscard]] const Node& ast() const noexcept; friend class MacroExecutor; diff --git a/include/Ark/Compiler/NameResolution/NameResolutionPass.hpp b/include/Ark/Compiler/NameResolution/NameResolutionPass.hpp index 22fd6fc7..cc122d66 100644 --- a/include/Ark/Compiler/NameResolution/NameResolutionPass.hpp +++ b/include/Ark/Compiler/NameResolution/NameResolutionPass.hpp @@ -17,6 +17,7 @@ #include #include +#include #include namespace Ark::internal @@ -34,13 +35,13 @@ namespace Ark::internal * @brief Start visiting the given AST, checking for mutability violation and unbound variables * @param ast AST to analyze */ - void process(const Node& ast) override; + void process(const Node& ast); /** * @brief Unused overload that return the input AST (untouched as this pass only generates errors) * @return const Node& ast */ - [[nodiscard]] const Node& ast() const noexcept override; + [[nodiscard]] const Node& ast() const noexcept; /** * @brief Register a symbol as defined, so that later we can throw errors on undefined symbols diff --git a/include/Ark/Compiler/Package/ImportSolver.hpp b/include/Ark/Compiler/Package/ImportSolver.hpp index a273decb..5ad076f5 100644 --- a/include/Ark/Compiler/Package/ImportSolver.hpp +++ b/include/Ark/Compiler/Package/ImportSolver.hpp @@ -43,9 +43,9 @@ namespace Ark::internal */ ImportSolver& setup(const std::filesystem::path& root, const std::vector& origin_imports); - void process(const Node& origin_ast) override; + void process(const Node& origin_ast); - [[nodiscard]] const Node& ast() const noexcept override; + [[nodiscard]] const Node& ast() const noexcept; private: struct ImportWithSource diff --git a/include/Ark/Compiler/Pass.hpp b/include/Ark/Compiler/Pass.hpp index 733c0f0c..b1320465 100644 --- a/include/Ark/Compiler/Pass.hpp +++ b/include/Ark/Compiler/Pass.hpp @@ -1,7 +1,7 @@ /** * @file Pass.hpp * @author Lexy Plateau (lexplt.dev@gmail.com) - * @brief Interface for a compiler pass (take in an AST, output an AST) + * @brief Interface for a compiler pass * @date 2024-07-21 * * @copyright Copyright (c) 2024-2026 @@ -11,9 +11,10 @@ #define ARK_COMPILER_PASS_HPP #include -#include #include +#include + namespace Ark::internal { /** @@ -33,17 +34,11 @@ namespace Ark::internal virtual ~Pass() = default; /** - * @brief Start processing the given AST - * @param ast - */ - virtual void process(const Node& ast) = 0; - - /** - * @brief Output of the compiler pass + * @brief Set a custom output stream for the logger * - * @return const Node& the modified AST + * @param os output stream */ - [[nodiscard]] virtual const Node& ast() const noexcept = 0; + void configureLogger(std::ostream& os); protected: Logger m_logger; diff --git a/include/Ark/Compiler/Welder.hpp b/include/Ark/Compiler/Welder.hpp index e3f23b86..49339e41 100644 --- a/include/Ark/Compiler/Welder.hpp +++ b/include/Ark/Compiler/Welder.hpp @@ -100,6 +100,12 @@ namespace Ark */ bool saveBytecodeToFile(const std::string& filename); + /** + * @brief Redirect the logs to a given stream + * @param os output stream + */ + void redirectLogsTo(std::ostream& os); + [[nodiscard]] const internal::Node& ast() const noexcept; [[nodiscard]] std::string textualIR() const noexcept; [[nodiscard]] const bytecode_t& bytecode() const noexcept; diff --git a/include/Ark/Error/Diagnostics.hpp b/include/Ark/Error/Diagnostics.hpp index 8606eb66..4ea8e518 100644 --- a/include/Ark/Error/Diagnostics.hpp +++ b/include/Ark/Error/Diagnostics.hpp @@ -60,9 +60,10 @@ namespace Ark::Diagnostics * * @param message error message to be included in the context * @param node AST node with the error + * @param colorize toggle context colors (default: true) * @return std::string */ - std::string makeContextWithNode(const std::string& message, const internal::Node& node); + std::string makeContextWithNode(const std::string& message, const internal::Node& node, bool colorize = true); ARK_API void generateWithCode(const CodeError& e, const std::string& code, std::ostream& os = std::cerr, bool colorize = true); diff --git a/include/Ark/State.hpp b/include/Ark/State.hpp index 3b832352..da8c4934 100644 --- a/include/Ark/State.hpp +++ b/include/Ark/State.hpp @@ -69,20 +69,22 @@ namespace Ark * * @param file_path path to an ArkScript code file * @param features compiler features to enable/disable + * @param stream optional output stream for the logger * @return true on success * @return false on failure */ - bool doFile(const std::string& file_path, uint16_t features = DefaultFeatures); + bool doFile(const std::string& file_path, uint16_t features = DefaultFeatures, std::ostream* stream = nullptr); /** * @brief Compile a string (representing ArkScript code) and store resulting bytecode in m_bytecode * * @param code the ArkScript code * @param features compiler features to enable/disable + * @param stream optional output stream for the logger * @return true on success * @return false on failure */ - bool doString(const std::string& code, uint16_t features = DefaultFeatures); + bool doString(const std::string& code, uint16_t features = DefaultFeatures, std::ostream* stream = nullptr); /** * @brief Register a function in the virtual machine @@ -146,10 +148,11 @@ namespace Ark * * @param file the path of file code to compile * @param output set path of .arkc file + * @param stream output stream for the logger * @return true on success * @return false on failure and raise an exception */ - [[nodiscard]] bool compile(const std::string& file, const std::string& output); + [[nodiscard]] bool compile(const std::string& file, const std::string& output, std::ostream* stream = nullptr); static void throwStateError(const std::string& message) { diff --git a/include/Ark/Utils/Logger.hpp b/include/Ark/Utils/Logger.hpp index b80f1678..bf4e1cd6 100644 --- a/include/Ark/Utils/Logger.hpp +++ b/include/Ark/Utils/Logger.hpp @@ -4,24 +4,27 @@ * @brief Internal logger * @date 2024-08-30 * - * @copyright Copyright (c) 2024-2026) + * @copyright Copyright (c) 2024-2026 */ #ifndef ARK_LOGGER_HPP #define ARK_LOGGER_HPP #include #include +#include +#include #include #include #include #include #include -#include + +#include namespace Ark::internal { - class Logger + class ARK_API Logger { public: struct MessageAndLocation @@ -67,6 +70,22 @@ namespace Ark::internal fmt::vformat(fmt, fmt::make_format_args(args...))); } + /** + * @brief Write a warn level log using fmtlib + * @tparam Args + * @param fmt format string + * @param args + */ + template + void warn(const char* fmt, Args&&... args) + { + fmt::println( + m_stream == nullptr ? std::cerr : *m_stream, + "{}: {}", + fmt::styled("Warning", colorize() ? fmt::fg(fmt::color::dark_orange) : fmt::text_style()), + fmt::vformat(fmt, fmt::make_format_args(args...))); + } + /** * @brief Write a debug level log using fmtlib * @tparam Args @@ -120,12 +139,33 @@ namespace Ark::internal fmt::vformat(fmt, fmt::make_format_args(args...))); } + /** + * @brief Set a custom output stream to use for warnings. This will disable colors. + * + * @param os output stream + */ + void configureOutputStream(std::ostream* os) + { + m_stream = os; + } + + /** + * @brief Check if logs can be colorized + * + * @return true if logs can be colorized + */ + inline bool colorize() const noexcept + { + return m_stream == nullptr; + } + private: unsigned m_debug; std::string m_name; fmt::color m_pass_color; std::unordered_map> m_trace_starts; std::vector m_active_traces; + std::ostream* m_stream; }; } diff --git a/include/CLI/Formatter.hpp b/include/CLI/Formatter.hpp index abdf3fdc..94d1935f 100644 --- a/include/CLI/Formatter.hpp +++ b/include/CLI/Formatter.hpp @@ -42,6 +42,7 @@ class Formatter final Ark::internal::Parser m_parser; std::string m_output; bool m_updated; ///< True if the original code now difer from the formatted one + Ark::internal::Logger m_logger; void processAst(const Ark::internal::Node& ast); @@ -110,7 +111,7 @@ class Formatter final * @param indent indentation level * @return std::string */ - static std::string prefix(const std::size_t indent) + [[nodiscard]] static std::string prefix(const std::size_t indent) { return std::string(indent * FormatterConfig::SpacePerIndent, ' '); } @@ -122,21 +123,21 @@ class Formatter final * @param after_newline when false, do not add prefix * @return */ - std::string format(const Ark::internal::Node& node, std::size_t indent, bool after_newline); + [[nodiscard]] std::string format(const Ark::internal::Node& node, std::size_t indent, bool after_newline); - std::string formatComment(const std::string& comment, std::size_t indent) const; + [[nodiscard]] std::string formatComment(const std::string& comment, std::size_t indent) const; - std::string formatBlock(const Ark::internal::Node& node, std::size_t indent, bool after_newline); + [[nodiscard]] std::string formatBlock(const Ark::internal::Node& node, std::size_t indent, bool after_newline); - std::string formatFunction(const Ark::internal::Node& node, std::size_t indent); - std::string formatVariable(const Ark::internal::Node& node, std::size_t indent); - std::string formatCondition(const Ark::internal::Node& node, std::size_t indent, bool is_macro = false); - std::string formatLoop(const Ark::internal::Node& node, std::size_t indent); - std::string formatBegin(const Ark::internal::Node& node, std::size_t indent, bool after_newline); - std::string formatImport(const Ark::internal::Node& node, std::size_t indent); - std::string formatDel(const Ark::internal::Node& node, std::size_t indent); - std::string formatCall(const Ark::internal::Node& node, std::size_t indent); - std::string formatMacro(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatFunction(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatVariable(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatCondition(const Ark::internal::Node& node, std::size_t indent, bool is_macro = false); + [[nodiscard]] std::string formatLoop(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatBegin(const Ark::internal::Node& node, std::size_t indent, bool after_newline); + [[nodiscard]] std::string formatImport(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatDel(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatCall(const Ark::internal::Node& node, std::size_t indent); + [[nodiscard]] std::string formatMacro(const Ark::internal::Node& node, std::size_t indent); }; #endif // ARK_FORMATTER_HPP diff --git a/src/arkreactor/Compiler/AST/Optimizer.cpp b/src/arkreactor/Compiler/AST/Optimizer.cpp index 263dfe7c..17d5fec6 100644 --- a/src/arkreactor/Compiler/AST/Optimizer.cpp +++ b/src/arkreactor/Compiler/AST/Optimizer.cpp @@ -1,5 +1,7 @@ #include +#include + namespace Ark::internal { Optimizer::Optimizer(const unsigned debug) noexcept : @@ -110,6 +112,30 @@ namespace Ark::internal // erase the node by turning it to an Unused node child = Node(NodeType::Unused); } + else if (child.comment().find("@deprecated") != std::string::npos) + { + std::string advice; + const std::vector vec = Utils::splitString(child.comment(), '\n'); + if (auto result = std::ranges::find_if(vec, [](const std::string& line) { + return line.find("@deprecated") != std::string::npos; + }); + result != vec.end()) + { + advice = result->substr(result->find("@deprecated") + 11); + Utils::trimWhitespace(advice); + } + + m_logger.warn( + "Using a deprecated {} `{}'.{}", + child.constList()[2].nodeType() == NodeType::List && + !child.constList()[2].constList().empty() && + child.constList()[2].constList()[0].nodeType() == NodeType::Keyword && + child.constList()[2].constList()[0].keyword() == Keyword::Fun + ? "function" + : "value", + name, + advice.empty() ? "" : " " + advice); + } } } else if (child.nodeType() == NodeType::Namespace) diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp index f6dbb578..1e04dd1c 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp @@ -18,7 +18,7 @@ namespace Ark::internal using namespace literals; IRCompiler::IRCompiler(const unsigned debug) : - m_logger("IRCompiler", debug) + Pass("IRCompiler", debug) {} void IRCompiler::process(const std::vector& pages, const std::vector& symbols, const std::vector& values) diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp index eb492248..a94fc0ef 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp @@ -20,7 +20,7 @@ namespace Ark::internal } IROptimizer::IROptimizer(const unsigned debug) : - m_logger("IROptimizer", debug) + Pass("IROptimizer", debug) { m_ruleset = { Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST }, diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index e5072baa..d595ed83 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -16,7 +15,7 @@ namespace Ark::internal using namespace literals; ASTLowerer::ASTLowerer(const unsigned debug) : - m_logger("ASTLowerer", debug) + Pass("ASTLowerer", debug) {} void ASTLowerer::addToTables(const std::vector& symbols, const std::vector& constants) @@ -160,7 +159,7 @@ namespace Ark::internal void ASTLowerer::warning(const std::string& message, const Node& node) { - fmt::println("{} {}", fmt::styled("Warning", fmt::fg(fmt::color::dark_orange)), Diagnostics::makeContextWithNode(message, node)); + m_logger.warn("{}", Diagnostics::makeContextWithNode(message, node, m_logger.colorize())); } void ASTLowerer::buildAndThrowError(const std::string& message, const Node& node) diff --git a/src/arkreactor/Compiler/Pass.cpp b/src/arkreactor/Compiler/Pass.cpp index e1961952..bd311794 100644 --- a/src/arkreactor/Compiler/Pass.cpp +++ b/src/arkreactor/Compiler/Pass.cpp @@ -7,4 +7,8 @@ namespace Ark::internal m_logger(std::move(name), debug_level) {} + void Pass::configureLogger(std::ostream& os) + { + m_logger.configureOutputStream(&os); + } } diff --git a/src/arkreactor/Compiler/Welder.cpp b/src/arkreactor/Compiler/Welder.cpp index 23182aab..41612d60 100644 --- a/src/arkreactor/Compiler/Welder.cpp +++ b/src/arkreactor/Compiler/Welder.cpp @@ -135,6 +135,18 @@ namespace Ark return true; } + void Welder::redirectLogsTo(std::ostream& os) + { + m_import_solver.configureLogger(os); + m_macro_processor.configureLogger(os); + m_ast_optimizer.configureLogger(os); + m_name_resolver.configureLogger(os); + m_lowerer.configureLogger(os); + m_ir_optimizer.configureLogger(os); + m_ir_compiler.configureLogger(os); + m_logger.configureOutputStream(&os); + } + const internal::Node& Welder::ast() const noexcept { return m_computed_ast; diff --git a/src/arkreactor/Error/Diagnostics.cpp b/src/arkreactor/Error/Diagnostics.cpp index 621dc85e..a4989518 100644 --- a/src/arkreactor/Error/Diagnostics.cpp +++ b/src/arkreactor/Error/Diagnostics.cpp @@ -210,14 +210,14 @@ namespace Ark::Diagnostics fmt::print(os, " {}\n", text); } - std::string makeContextWithNode(const std::string& message, const internal::Node& node) + std::string makeContextWithNode(const std::string& message, const internal::Node& node, const bool colorize) { std::stringstream ss; helper( ss, message, - true, + colorize, node.filename(), node.position()); diff --git a/src/arkreactor/State.cpp b/src/arkreactor/State.cpp index b85468c9..5bf3fcad 100644 --- a/src/arkreactor/State.cpp +++ b/src/arkreactor/State.cpp @@ -62,9 +62,12 @@ namespace Ark } } - bool State::compile(const std::string& file, const std::string& output) + bool State::compile(const std::string& file, const std::string& output, std::ostream* stream) { Welder welder(m_debug_level, m_libenv, m_features); + if (stream != nullptr) + welder.redirectLogsTo(*stream); + for (const auto& key : m_bound | std::views::keys) welder.registerSymbol(key); @@ -82,7 +85,7 @@ namespace Ark return true; } - bool State::doFile(const std::string& file_path, const uint16_t features) + bool State::doFile(const std::string& file_path, const uint16_t features, std::ostream* stream) { m_features = features; @@ -116,7 +119,7 @@ namespace Ark } } - if (compile(file_path, bytecode_path)) + if (compile(file_path, bytecode_path, stream)) return true; } else if (feed(bytecode)) // it's a bytecode file @@ -124,11 +127,14 @@ namespace Ark return false; } - bool State::doString(const std::string& code, const uint16_t features) + bool State::doString(const std::string& code, const uint16_t features, std::ostream* stream) { m_features = features; Welder welder(m_debug_level, m_libenv, m_features); + if (stream != nullptr) + welder.redirectLogsTo(*stream); + for (const auto& p : m_bound) welder.registerSymbol(p.first); diff --git a/src/arkreactor/Utils/Logger.cpp b/src/arkreactor/Utils/Logger.cpp index e13dd398..b743d21e 100644 --- a/src/arkreactor/Utils/Logger.cpp +++ b/src/arkreactor/Utils/Logger.cpp @@ -27,7 +27,7 @@ namespace Ark::internal }; Logger::Logger(std::string name, const unsigned debug_level) : - m_debug(debug_level), m_name(std::move(name)) + m_debug(debug_level), m_name(std::move(name)), m_stream(nullptr) { m_pass_color = colors[std::hash {}(m_name) % colors.size()]; } diff --git a/src/arkscript/Formatter.cpp b/src/arkscript/Formatter.cpp index 4b863803..fc745956 100644 --- a/src/arkscript/Formatter.cpp +++ b/src/arkscript/Formatter.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -15,11 +14,11 @@ using namespace Ark::internal; using namespace Ark::literals; Formatter::Formatter(const bool dry_run) : - m_dry_run(dry_run), m_parser(/* debug= */ 0, ParserMode::Raw), m_updated(false) + m_dry_run(dry_run), m_parser(/* debug= */ 0, ParserMode::Raw), m_updated(false), m_logger("formatter", 0) {} Formatter::Formatter(std::string filename, const bool dry_run) : - m_filename(std::move(filename)), m_dry_run(dry_run), m_parser(/* debug= */ 0, ParserMode::Raw), m_updated(false) + m_filename(std::move(filename)), m_dry_run(dry_run), m_parser(/* debug= */ 0, ParserMode::Raw), m_updated(false), m_logger("formatter", 0) {} void Formatter::run() @@ -95,12 +94,11 @@ void Formatter::warnIfCommentsWereRemoved(const std::string& original_code, cons if (before_count != after_count) { - fmt::println( - "{}: one or more comments from the original source code seem to have been {} by mistake while formatting {}", - fmt::styled("Warning", fmt::fg(fmt::color::dark_orange)), + m_logger.warn( + "one or more comments from the original source code seem to have been {} by mistake while formatting {}", before_count > after_count ? "removed" : "duplicated", filename != ARK_NO_NAME_FILE ? filename : "file"); - fmt::println("Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark"); + m_logger.warn("Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark"); } } diff --git a/src/arkscript/main.cpp b/src/arkscript/main.cpp index 936faa10..8bda5d7d 100644 --- a/src/arkscript/main.cpp +++ b/src/arkscript/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -189,7 +190,7 @@ int main(int argc, char** argv) else if (Utils::fileExists("./lib") && Utils::fileExists("./lib/std/Prelude.ark")) lib_paths.emplace_back("lib"); else if (debug > 0) - fmt::println("{}: Couldn't read ARKSCRIPT_PATH environment variable", fmt::styled("Warning", fmt::fg(fmt::color::dark_orange))); + fmt::println(std::cerr, "{}: Couldn't read ARKSCRIPT_PATH environment variable", fmt::styled("Warning", fmt::fg(fmt::color::dark_orange))); } switch (selected) diff --git a/tests/unittests/Suites/DiagnosticsSuite.cpp b/tests/unittests/Suites/DiagnosticsSuite.cpp index ae0d6392..59b19875 100644 --- a/tests/unittests/Suites/DiagnosticsSuite.cpp +++ b/tests/unittests/Suites/DiagnosticsSuite.cpp @@ -85,4 +85,29 @@ ut::suite<"Diagnostics"> diagnostics_suite = [] { } }; }); + + iterTestFiles( + "DiagnosticsSuite/warnings", + [](TestData&& data) { + Ark::State state({ lib_path }); + std::stringstream stream; + + should("compile without error warnings/" + data.stem) = [&] { + expect(mut(state).doFile(data.path, features | Ark::FeatureASTOptimizer, &stream)); + }; + + should("run " + data.stem) = [&] { + expect(nothrow([&] { + Ark::VM vm(state); + vm.run(/* fail_with_exception= */ true); + })); + }; + + should("have generated warnings in " + data.stem) = [&] { + std::string warns = sanitizeOutput(stream.str()); + expectOrDiff(data.expected, warns); + if (shouldWriteNewDiffsTofile() && data.expected != warns) + updateExpectedFile(data, warns); + }; + }); }; diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.ark b/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.ark new file mode 100644 index 00000000..5fd58035 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.ark @@ -0,0 +1,3 @@ +# @deprecated Use '+' +(let foo (fun (a b) (+ a b))) +(print (foo 1 2)) diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.expected b/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.expected new file mode 100644 index 00000000..9f35112b --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/deprecated.expected @@ -0,0 +1 @@ +Warning: Using a deprecated function `foo'. Use '+' diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.ark b/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.ark new file mode 100644 index 00000000..98a5d3de --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.ark @@ -0,0 +1,3 @@ +(let a 5) +(while (!= a 5) (append [] a)) +(print a) diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.expected b/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.expected new file mode 100644 index 00000000..764edc7e --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.expected @@ -0,0 +1,7 @@ +Warning: In file tests/unittests/resources/DiagnosticsSuite/warnings/ignoring_return_value.ark:2 + 1 | (let a 5) + 2 | (while (!= a 5) (append [] a)) + | ^~~~~~~~~~~~~ + 3 | (print a) + 4 | + Ignoring return value of function diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.ark b/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.ark new file mode 100644 index 00000000..0e67c6ee --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.ark @@ -0,0 +1,3 @@ +(let a 5) +(while (!= a 5) a) +(print a) diff --git a/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.expected b/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.expected new file mode 100644 index 00000000..7b143784 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.expected @@ -0,0 +1,7 @@ +Warning: In file tests/unittests/resources/DiagnosticsSuite/warnings/statement_no_effect.ark:2 + 1 | (let a 5) + 2 | (while (!= a 5) a) + | ^ + 3 | (print a) + 4 | + Statement has no effect