From 200fec45d6476a95d77daa89ebe85abaa76edc10 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 7 Nov 2024 17:44:49 -0400 Subject: [PATCH] Extend `--trace` to print out keyword location URIs (#194) Signed-off-by: Juan Cruz Viotti --- DEPENDENCIES | 2 +- src/utils.cc | 10 ++++++- test/metaschema/fail_trace.sh | 26 ++++++++++++++++++ test/metaschema/pass_trace.sh | 15 +++++++++++ test/validate/fail_trace.sh | 3 +++ test/validate/pass_trace.sh | 3 +++ .../src/compiler/compile_output_trace.cc | 27 ++++++++++++++++--- .../src/compiler/default_compiler_draft4.h | 11 +------- .../sourcemeta/blaze/compiler_output.h | 2 ++ vendor/blaze/src/evaluator/evaluator.cc | 7 ++++- 10 files changed, 89 insertions(+), 17 deletions(-) diff --git a/DEPENDENCIES b/DEPENDENCIES index d9c23c7..3ba7ace 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -4,4 +4,4 @@ jsontoolkit https://github.com/sourcemeta/jsontoolkit 7a398224cc2e76ea9ae8541a87 hydra https://github.com/sourcemeta/hydra a4a74f3cabd32f2f829f449d67339dac33f9910e alterschema https://github.com/sourcemeta/alterschema 92e370ce9c1f0582014b54d43e388ee012dfe13d jsonbinpack https://github.com/sourcemeta/jsonbinpack d777179441d3c703e1fda1187742541aa26836b5 -blaze https://github.com/sourcemeta/blaze a5b3c8e4d77a0b88e4a93f304ae75e711b30a2e6 +blaze https://github.com/sourcemeta/blaze 4db8309470369332d3d0658ade9402a37abe418e diff --git a/src/utils.cc b/src/utils.cc index 60a9ecc..69a7001 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -191,7 +191,9 @@ auto print(const sourcemeta::blaze::ErrorOutput &output, std::ostream &stream) auto print(const sourcemeta::blaze::TraceOutput &output, std::ostream &stream) -> void { - for (const auto &entry : output) { + for (auto iterator = output.cbegin(); iterator != output.cend(); iterator++) { + const auto &entry{*iterator}; + if (entry.evaluate_path.empty()) { continue; } @@ -217,6 +219,12 @@ auto print(const sourcemeta::blaze::TraceOutput &output, std::ostream &stream) stream << " at \""; sourcemeta::jsontoolkit::stringify(entry.instance_location, stream); stream << "\"\n"; + stream << " at keyword location \"" << entry.keyword_location << "\"\n"; + + // To make it easier to read + if (std::next(iterator) != output.cend()) { + stream << "\n"; + } } } diff --git a/test/metaschema/fail_trace.sh b/test/metaschema/fail_trace.sh index 463cc1c..b0f44a7 100755 --- a/test/metaschema/fail_trace.sh +++ b/test/metaschema/fail_trace.sh @@ -23,35 +23,61 @@ test "$CODE" = "1" || exit 1 cat << 'EOF' > "$TMP/expected-1.txt" -> (push) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + <- (pass) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + -> (push) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" + -> (push) "/properties/$schema/type" at "/$schema" + at keyword location "http://json-schema.org/draft-04/schema#/properties/$schema/type" + <- (pass) "/properties/$schema/type" at "/$schema" + at keyword location "http://json-schema.org/draft-04/schema#/properties/$schema/type" + -> (push) "/properties/minimum/type" at "/minimum" + at keyword location "http://json-schema.org/draft-04/schema#/properties/minimum/type" + <- (fail) "/properties/minimum/type" at "/minimum" + at keyword location "http://json-schema.org/draft-04/schema#/properties/minimum/type" + <- (fail) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" EOF cat << 'EOF' > "$TMP/expected-2.txt" -> (push) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + <- (pass) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + -> (push) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" + -> (push) "/properties/minimum/type" at "/minimum" + at keyword location "http://json-schema.org/draft-04/schema#/properties/minimum/type" + <- (fail) "/properties/minimum/type" at "/minimum" + at keyword location "http://json-schema.org/draft-04/schema#/properties/minimum/type" + <- (fail) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" EOF diff "$TMP/output.txt" "$TMP/expected-1.txt" || \ diff --git a/test/metaschema/pass_trace.sh b/test/metaschema/pass_trace.sh index 7bd0083..7e05e15 100755 --- a/test/metaschema/pass_trace.sh +++ b/test/metaschema/pass_trace.sh @@ -16,20 +16,35 @@ EOF cat << 'EOF' > "$TMP/expected.txt" -> (push) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + <- (pass) "/dependencies" at "" + at keyword location "http://json-schema.org/draft-04/schema#/dependencies" + -> (push) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" + -> (push) "/properties/$schema/type" at "/$schema" + at keyword location "http://json-schema.org/draft-04/schema#/properties/$schema/type" + <- (pass) "/properties/$schema/type" at "/$schema" + at keyword location "http://json-schema.org/draft-04/schema#/properties/$schema/type" + <- (pass) "/properties" at "" + at keyword location "http://json-schema.org/draft-04/schema#/properties" + -> (push) "/type" at "" + at keyword location "http://json-schema.org/draft-04/schema#/type" + <- (pass) "/type" at "" + at keyword location "http://json-schema.org/draft-04/schema#/type" EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/validate/fail_trace.sh b/test/validate/fail_trace.sh index b2aa83c..77ce824 100755 --- a/test/validate/fail_trace.sh +++ b/test/validate/fail_trace.sh @@ -29,8 +29,11 @@ test "$CODE" = "1" || exit 1 cat << EOF > "$TMP/expected.txt" -> (push) "/properties/foo/type" at "/foo" + at keyword location "#/properties/foo/type" + <- (fail) "/properties/foo/type" at "/foo" + at keyword location "#/properties/foo/type" EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/validate/pass_trace.sh b/test/validate/pass_trace.sh index 7c85cf0..9fdd182 100755 --- a/test/validate/pass_trace.sh +++ b/test/validate/pass_trace.sh @@ -27,8 +27,11 @@ EOF cat << EOF > "$TMP/expected.txt" -> (push) "/properties/foo/type" at "/foo" + at keyword location "#/properties/foo/type" + <- (pass) "/properties/foo/type" at "/foo" + at keyword location "#/properties/foo/type" EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/vendor/blaze/src/compiler/compile_output_trace.cc b/vendor/blaze/src/compiler/compile_output_trace.cc index 23b67ab..435b81a 100644 --- a/vendor/blaze/src/compiler/compile_output_trace.cc +++ b/vendor/blaze/src/compiler/compile_output_trace.cc @@ -25,10 +25,17 @@ static auto step_name(const sourcemeta::blaze::Template::value_type &step) }, step); } +#elif defined(_MSC_VER) +static auto step_name(const sourcemeta::blaze::Template::value_type &step) + -> std::string { + return std::visit( + [](const auto &value) { return std::string{typeid(value).name()}; }, + step); +} #else static auto step_name(const sourcemeta::blaze::Template::value_type &) -> std::string { - // TODO: Properly implement for GCC and MSVC + // TODO: Properly implement for GCC return "????"; } #endif @@ -56,7 +63,13 @@ auto TraceOutput::operator()( const sourcemeta::jsontoolkit::WeakPointer &evaluate_path, const sourcemeta::jsontoolkit::WeakPointer &instance_location, const sourcemeta::jsontoolkit::JSON &) -> void { + +#if defined(_MSC_VER) + const std::string step_prefix{"struct sourcemeta::blaze::"}; +#else const std::string step_prefix{"sourcemeta::blaze::"}; +#endif + const auto full_step_name{step_name(step)}; const auto short_step_name{full_step_name.starts_with(step_prefix) ? full_step_name.substr(step_prefix.size()) @@ -64,15 +77,21 @@ auto TraceOutput::operator()( auto effective_evaluate_path{evaluate_path.resolve_from(this->base_)}; + auto keyword_location{std::visit( + [](const auto &value) { return value.keyword_location; }, step)}; + if (type == EvaluationType::Pre) { this->output.push_back({EntryType::Push, short_step_name, instance_location, - std::move(effective_evaluate_path)}); + std::move(effective_evaluate_path), + std::move(keyword_location)}); } else if (result) { this->output.push_back({EntryType::Pass, short_step_name, instance_location, - std::move(effective_evaluate_path)}); + std::move(effective_evaluate_path), + std::move(keyword_location)}); } else { this->output.push_back({EntryType::Fail, short_step_name, instance_location, - std::move(effective_evaluate_path)}); + std::move(effective_evaluate_path), + std::move(keyword_location)}); } } diff --git a/vendor/blaze/src/compiler/default_compiler_draft4.h b/vendor/blaze/src/compiler/default_compiler_draft4.h index 9937b0d..85182fd 100644 --- a/vendor/blaze/src/compiler/default_compiler_draft4.h +++ b/vendor/blaze/src/compiler/default_compiler_draft4.h @@ -758,16 +758,7 @@ auto compiler_draft4_applicator_properties_with_options( } } - if (imports_validation_vocabulary && - schema_context.schema.defines("type") && - schema_context.schema.at("type").is_string() && - schema_context.schema.at("type").to_string() == "object" && - required.contains(name)) { - // We can avoid the container too and just inline these steps - for (auto &&substep : substeps) { - children.push_back(std::move(substep)); - } - } else if (!substeps.empty()) { + if (!substeps.empty()) { children.push_back(make( context, schema_context, effective_dynamic_context, ValueString{name}, std::move(substeps))); diff --git a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_output.h b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_output.h index 9dcfa8a..f53e0e9 100644 --- a/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_output.h +++ b/vendor/blaze/src/compiler/include/sourcemeta/blaze/compiler_output.h @@ -149,6 +149,7 @@ class SOURCEMETA_BLAZE_COMPILER_EXPORT ErrorOutput { // } /// /// std::cerr << entry.name << "\n"; +/// std::cerr << entry.keyword_location << "\n"; /// sourcemeta::jsontoolkit::stringify(entry.instance_location, std::cerr); /// std::cerr << "\n"; /// sourcemeta::jsontoolkit::stringify(entry.evaluate_path, std::cerr); @@ -172,6 +173,7 @@ class SOURCEMETA_BLAZE_COMPILER_EXPORT TraceOutput { const std::string name; const sourcemeta::jsontoolkit::WeakPointer instance_location; const sourcemeta::jsontoolkit::WeakPointer evaluate_path; + const std::string keyword_location; }; auto operator()(const EvaluationType type, const bool result, diff --git a/vendor/blaze/src/evaluator/evaluator.cc b/vendor/blaze/src/evaluator/evaluator.cc index 4491989..d11b5a1 100644 --- a/vendor/blaze/src/evaluator/evaluator.cc +++ b/vendor/blaze/src/evaluator/evaluator.cc @@ -767,9 +767,14 @@ auto evaluate_step(const sourcemeta::blaze::Template::value_type &step, case IS_STEP(ControlGroupWhenDefines): { EVALUATE_BEGIN_PASS_THROUGH(control, ControlGroupWhenDefines); - const auto &target{context.resolve_target()}; assert(!control.children.empty()); + // TODO: This is needed for nested `properties`, but can have a + // performance impact. Maybe we can be smarter about when we + // do this traversal? + const auto &target{ + get(context.resolve_target(), control.relative_instance_location)}; + if (target.is_object() && target.defines(control.value)) { for (const auto &child : control.children) { if (!evaluate_step(child, callback, context)) {