diff --git a/docs/fake/rollnw/script/__init__.py b/docs/fake/rollnw/script/__init__.py index d4744d00e..2e598fe41 100644 --- a/docs/fake/rollnw/script/__init__.py +++ b/docs/fake/rollnw/script/__init__.py @@ -132,9 +132,6 @@ class LspContext: def __init__(self, command_script: str = "nwscript"): pass - def diagnostics(self) -> List[Diagnostic]: - return [] - class Nss: """Implementation of nwscript""" @@ -147,6 +144,9 @@ def ast(self): """Gets the parsed script""" pass + def diagnostics(self) -> List[Diagnostic]: + return [] + def errors(self): """Gets number of errors encountered while parsing""" pass diff --git a/lib/nw/script/Context.cpp b/lib/nw/script/Context.cpp index 90689619d..c13948a40 100644 --- a/lib/nw/script/Context.cpp +++ b/lib/nw/script/Context.cpp @@ -176,6 +176,14 @@ bool Context::is_type_convertible(size_t lhs, size_t rhs) void Context::lexical_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { if (script) { + Diagnostic result; + result.type = DiagnosticType::lexical; + result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; + result.script = script ? script->name() : ""; + result.message = std::string(msg); + result.location = loc; + script->add_diagnostic(std::move(result)); + if (is_warning) { script->increment_warnings(); } else { @@ -196,6 +204,14 @@ void Context::lexical_diagnostic(Nss* script, std::string_view msg, bool is_warn void Context::parse_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { if (script) { + Diagnostic result; + result.type = DiagnosticType::parse; + result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; + result.script = script ? script->name() : ""; + result.message = std::string(msg); + result.location = loc; + script->add_diagnostic(std::move(result)); + if (is_warning) { script->increment_warnings(); } else { @@ -216,6 +232,14 @@ void Context::parse_diagnostic(Nss* script, std::string_view msg, bool is_warnin void Context::semantic_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { if (script) { + Diagnostic result; + result.type = DiagnosticType::semantic; + result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; + result.script = script ? script->name() : ""; + result.message = std::string(msg); + result.location = loc; + script->add_diagnostic(std::move(result)); + if (is_warning) { script->increment_warnings(); } else { diff --git a/lib/nw/script/Diagnostic.hpp b/lib/nw/script/Diagnostic.hpp new file mode 100644 index 000000000..5bc031a3a --- /dev/null +++ b/lib/nw/script/Diagnostic.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Token.hpp" + +#include + +namespace nw::script { + +enum struct DiagnosticType { + lexical, + parse, + semantic, +}; + +enum struct DiagnosticLevel { + warning, + error, +}; + +struct Diagnostic { + DiagnosticType type; + DiagnosticLevel level; + std::string script; + std::string message; + SourceLocation location; +}; + +} // namespace nw::script diff --git a/lib/nw/script/LspContext.cpp b/lib/nw/script/LspContext.cpp index 88de11b15..7503cf285 100644 --- a/lib/nw/script/LspContext.cpp +++ b/lib/nw/script/LspContext.cpp @@ -2,6 +2,8 @@ #include "Nss.hpp" +#include "../log.hpp" + namespace nw::script { LspContext::LspContext(std::string command_script) @@ -9,42 +11,61 @@ LspContext::LspContext(std::string command_script) { } -const std::vector& LspContext::diagnostics() const -{ - return diagnostics_; -} - void LspContext::lexical_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { + CHECK_F(!!script, ""); + Diagnostic result; result.type = DiagnosticType::lexical; result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; result.script = script ? script->name() : ""; result.message = std::string(msg); result.location = loc; - diagnostics_.push_back(std::move(result)); + script->add_diagnostic(std::move(result)); + + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } } void LspContext::parse_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { + CHECK_F(!!script, ""); + Diagnostic result; result.type = DiagnosticType::parse; result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; result.script = script ? script->name() : ""; result.message = std::string(msg); result.location = loc; - diagnostics_.push_back(std::move(result)); + script->add_diagnostic(std::move(result)); + + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } } void LspContext::semantic_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { + CHECK_F(!!script, ""); + Diagnostic result; result.type = DiagnosticType::semantic; result.level = is_warning ? DiagnosticLevel::warning : DiagnosticLevel::error; result.script = script ? script->name() : ""; result.message = std::string(msg); result.location = loc; - diagnostics_.push_back(std::move(result)); + script->add_diagnostic(std::move(result)); + + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } } } // namespace nw::script diff --git a/lib/nw/script/LspContext.hpp b/lib/nw/script/LspContext.hpp index 08dcf3996..807570ea4 100644 --- a/lib/nw/script/LspContext.hpp +++ b/lib/nw/script/LspContext.hpp @@ -4,33 +4,10 @@ namespace nw::script { -enum struct DiagnosticType { - lexical, - parse, - semantic, -}; - -enum struct DiagnosticLevel { - warning, - error, -}; - -struct Diagnostic { - DiagnosticType type; - DiagnosticLevel level; - std::string script; - std::string message; - SourceLocation location; -}; - struct LspContext : public Context { LspContext(std::string command_script = "nwscript"); virtual ~LspContext() = default; - std::vector diagnostics_; - - const std::vector& diagnostics() const; - virtual void lexical_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) override; virtual void parse_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) override; virtual void semantic_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) override; diff --git a/lib/nw/script/Nss.cpp b/lib/nw/script/Nss.cpp index a53b431b0..828273c5f 100644 --- a/lib/nw/script/Nss.cpp +++ b/lib/nw/script/Nss.cpp @@ -33,6 +33,11 @@ Nss::Nss(ResourceData data, Context* ctx) CHECK_F(!!ctx_, "[script] invalid script context"); } +void Nss::add_diagnostic(Diagnostic diagnostic) +{ + diagnostics_.push_back(std::move(diagnostic)); +} + std::vector Nss::complete(const std::string& needle) { std::vector result; @@ -59,6 +64,11 @@ std::set Nss::dependencies() const return result; } +const std::vector& Nss::diagnostics() const noexcept +{ + return diagnostics_; +} + Declaration* Nss::locate_export(const std::string& name, bool is_type) { auto sym = symbol_table_.find(name); diff --git a/lib/nw/script/Nss.hpp b/lib/nw/script/Nss.hpp index 3917979fc..ce75629da 100644 --- a/lib/nw/script/Nss.hpp +++ b/lib/nw/script/Nss.hpp @@ -5,6 +5,7 @@ #include "../resources/ResourceData.hpp" #include "Context.hpp" +#include "Diagnostic.hpp" #include #include @@ -28,6 +29,9 @@ struct Nss { explicit Nss(std::string_view script, Context* ctx); explicit Nss(ResourceData data, Context* ctx); + /// Add diagnostic to script + void add_diagnostic(Diagnostic diagnostic); + /// Generates a list of potential completions from exports /// @note This is just a baby step. std::vector complete(const std::string& needle); @@ -38,6 +42,9 @@ struct Nss { /// Returns all transitive dependencies std::set dependencies() const; + /// Gets script diagnostics + const std::vector& diagnostics() const noexcept; + /// Returns how many errors were found during parsing size_t errors() const noexcept { return errors_; } @@ -89,6 +96,7 @@ struct Nss { NssParser parser_; Ast ast_; immer::map symbol_table_; + std::vector diagnostics_; size_t errors_ = 0; size_t warnings_ = 0; bool resolved_ = false; diff --git a/rollnw-py/wrapper_script.cpp b/rollnw-py/wrapper_script.cpp index f6b05fe33..b481bb926 100644 --- a/rollnw-py/wrapper_script.cpp +++ b/rollnw-py/wrapper_script.cpp @@ -42,8 +42,7 @@ void init_script(py::module& nw) py::class_(nw, "LspContext") .def(py::init<>()) - .def(py::init()) - .def("diagnostics", &nws::LspContext::diagnostics); + .def(py::init()); py::enum_(nw, "NssTokenType") .value("INVALID", nws::NssTokenType::INVALID) @@ -164,6 +163,7 @@ void init_script(py::module& nw) }, py::return_value_policy::reference_internal) .def("dependencies", &nws::Nss::dependencies) + .def("diagnostics", &nws::Nss::diagnostics) .def("errors", &nws::Nss::errors) .def("locate_export", &nws::Nss::locate_export, py::return_value_policy::reference_internal) .def("parse", &nws::Nss::parse) diff --git a/rollnw-stubs/script.pyi b/rollnw-stubs/script.pyi index 0c8783b5e..b119f2547 100644 --- a/rollnw-stubs/script.pyi +++ b/rollnw-stubs/script.pyi @@ -30,7 +30,6 @@ class Diagnostic: class LspContext(Context): def __init__(self, command_script: str = "nwscript") -> None: ... - def diagnostics(self) -> List[Diagnostic]: ... class AssignExpression(Expression): @@ -215,6 +214,7 @@ class Nss: def __init__(self, filename: Path, ctx: Context) -> None: ... def ast(self) -> Ast: ... def dependencies(self) -> Set[str]: ... + def diagnostics(self) -> List[Diagnostic]: ... def errors(self) -> int: ... def parse(self, *args, **kwargs) -> None: ... def process_includes(self, parent: Nss) -> Any: ... diff --git a/tests/script_nss.cpp b/tests/script_nss.cpp index 2edca55d9..3b28e053b 100644 --- a/tests/script_nss.cpp +++ b/tests/script_nss.cpp @@ -1018,7 +1018,7 @@ TEST(Nss, LspContext) EXPECT_NO_THROW(nss3.parse()); EXPECT_NO_THROW(nss3.resolve()); - EXPECT_EQ(ctx->diagnostics().size(), 1); - EXPECT_EQ(ctx->diagnostics()[0].type, nw::script::DiagnosticType::semantic); - EXPECT_EQ(ctx->diagnostics()[0].level, nw::script::DiagnosticLevel::error); + EXPECT_EQ(nss3.diagnostics().size(), 1); + EXPECT_EQ(nss3.diagnostics()[0].type, nw::script::DiagnosticType::semantic); + EXPECT_EQ(nss3.diagnostics()[0].level, nw::script::DiagnosticLevel::error); } diff --git a/tests/test_script.py b/tests/test_script.py index aa86f0ab7..5d0eb1953 100644 --- a/tests/test_script.py +++ b/tests/test_script.py @@ -35,7 +35,7 @@ def test_function_decl2(): except: pass nss.resolve() - assert len(ctx.diagnostics()) == 1 + assert len(nss.diagnostics()) == 1 def test_var_decl():