From 1f23b44147038332eaa2aa06c67f9eba524b1b70 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 9 Sep 2024 17:29:47 -0400 Subject: [PATCH] [WIP] Add redundancy rules for keyword that won't apply to given types Signed-off-by: Juan Cruz Viotti --- src/linter/CMakeLists.txt | 50 +++++ src/linter/linter.cc | 130 ++++++++++++ ...op_non_array_keywords_applicator_2019_09.h | 30 +++ ...op_non_array_keywords_applicator_2020_12.h | 30 +++ .../drop_non_array_keywords_content_2019_09.h | 29 +++ .../drop_non_array_keywords_content_2020_12.h | 29 +++ .../drop_non_array_keywords_format_2019_09.h | 28 +++ .../drop_non_array_keywords_format_2020_12.h | 30 +++ ...p_non_array_keywords_unevaluated_2020_12.h | 28 +++ ...op_non_array_keywords_validation_2019_09.h | 29 +++ ...op_non_array_keywords_validation_2020_12.h | 29 +++ ..._non_boolean_keywords_applicator_2019_09.h | 39 ++++ ..._non_boolean_keywords_applicator_2020_12.h | 37 ++++ ...rop_non_boolean_keywords_content_2019_09.h | 31 +++ ...rop_non_boolean_keywords_content_2020_12.h | 31 +++ ...drop_non_boolean_keywords_format_2019_09.h | 30 +++ ...drop_non_boolean_keywords_format_2020_12.h | 32 +++ ...non_boolean_keywords_unevaluated_2020_12.h | 31 +++ ..._non_boolean_keywords_validation_2019_09.h | 35 ++++ ..._non_boolean_keywords_validation_2020_12.h | 35 ++++ ...rop_non_null_keywords_applicator_2019_09.h | 39 ++++ ...rop_non_null_keywords_applicator_2020_12.h | 37 ++++ .../drop_non_null_keywords_content_2019_09.h | 31 +++ .../drop_non_null_keywords_content_2020_12.h | 31 +++ .../drop_non_null_keywords_format_2019_09.h | 30 +++ .../drop_non_null_keywords_format_2020_12.h | 32 +++ ...op_non_null_keywords_unevaluated_2020_12.h | 31 +++ ...rop_non_null_keywords_validation_2019_09.h | 35 ++++ ...rop_non_null_keywords_validation_2020_12.h | 35 ++++ ..._non_numeric_keywords_applicator_2019_09.h | 38 ++++ ..._non_numeric_keywords_applicator_2020_12.h | 36 ++++ ...rop_non_numeric_keywords_content_2019_09.h | 30 +++ ...rop_non_numeric_keywords_content_2020_12.h | 30 +++ ...drop_non_numeric_keywords_format_2019_09.h | 29 +++ ...drop_non_numeric_keywords_format_2020_12.h | 31 +++ ...non_numeric_keywords_unevaluated_2020_12.h | 30 +++ ..._non_numeric_keywords_validation_2019_09.h | 30 +++ ..._non_numeric_keywords_validation_2020_12.h | 30 +++ ...p_non_object_keywords_applicator_2019_09.h | 29 +++ ...p_non_object_keywords_applicator_2020_12.h | 28 +++ ...drop_non_object_keywords_content_2019_09.h | 29 +++ ...drop_non_object_keywords_content_2020_12.h | 29 +++ .../drop_non_object_keywords_format_2019_09.h | 28 +++ .../drop_non_object_keywords_format_2020_12.h | 30 +++ ..._non_object_keywords_unevaluated_2020_12.h | 28 +++ ...p_non_object_keywords_validation_2019_09.h | 30 +++ ...p_non_object_keywords_validation_2020_12.h | 30 +++ ...p_non_string_keywords_applicator_2019_09.h | 37 ++++ ...p_non_string_keywords_applicator_2020_12.h | 35 ++++ ..._non_string_keywords_unevaluated_2020_12.h | 29 +++ ...p_non_string_keywords_validation_2019_09.h | 30 +++ ...p_non_string_keywords_validation_2020_12.h | 30 +++ test/linter/2019_09_test.cc | 186 ++++++++++++++++++ test/linter/2020_12_test.cc | 186 ++++++++++++++++++ 54 files changed, 2122 insertions(+) create mode 100644 src/linter/redundant/drop_non_array_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_array_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_array_keywords_content_2019_09.h create mode 100644 src/linter/redundant/drop_non_array_keywords_content_2020_12.h create mode 100644 src/linter/redundant/drop_non_array_keywords_format_2019_09.h create mode 100644 src/linter/redundant/drop_non_array_keywords_format_2020_12.h create mode 100644 src/linter/redundant/drop_non_array_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_array_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_array_keywords_validation_2020_12.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_content_2019_09.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_content_2020_12.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_format_2019_09.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_format_2020_12.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_boolean_keywords_validation_2020_12.h create mode 100644 src/linter/redundant/drop_non_null_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_null_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_null_keywords_content_2019_09.h create mode 100644 src/linter/redundant/drop_non_null_keywords_content_2020_12.h create mode 100644 src/linter/redundant/drop_non_null_keywords_format_2019_09.h create mode 100644 src/linter/redundant/drop_non_null_keywords_format_2020_12.h create mode 100644 src/linter/redundant/drop_non_null_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_null_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_null_keywords_validation_2020_12.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_content_2019_09.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_content_2020_12.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_format_2019_09.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_format_2020_12.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_numeric_keywords_validation_2020_12.h create mode 100644 src/linter/redundant/drop_non_object_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_object_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_object_keywords_content_2019_09.h create mode 100644 src/linter/redundant/drop_non_object_keywords_content_2020_12.h create mode 100644 src/linter/redundant/drop_non_object_keywords_format_2019_09.h create mode 100644 src/linter/redundant/drop_non_object_keywords_format_2020_12.h create mode 100644 src/linter/redundant/drop_non_object_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_object_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_object_keywords_validation_2020_12.h create mode 100644 src/linter/redundant/drop_non_string_keywords_applicator_2019_09.h create mode 100644 src/linter/redundant/drop_non_string_keywords_applicator_2020_12.h create mode 100644 src/linter/redundant/drop_non_string_keywords_unevaluated_2020_12.h create mode 100644 src/linter/redundant/drop_non_string_keywords_validation_2019_09.h create mode 100644 src/linter/redundant/drop_non_string_keywords_validation_2020_12.h diff --git a/src/linter/CMakeLists.txt b/src/linter/CMakeLists.txt index 0770afe..1a23437 100644 --- a/src/linter/CMakeLists.txt +++ b/src/linter/CMakeLists.txt @@ -24,6 +24,56 @@ noa_library(NAMESPACE sourcemeta PROJECT alterschema NAME linter redundant/content_media_type_without_encoding.h redundant/content_schema_default.h redundant/content_schema_without_media_type.h + redundant/drop_non_array_keywords_applicator_2019_09.h + redundant/drop_non_array_keywords_applicator_2020_12.h + redundant/drop_non_array_keywords_content_2019_09.h + redundant/drop_non_array_keywords_content_2020_12.h + redundant/drop_non_array_keywords_format_2019_09.h + redundant/drop_non_array_keywords_format_2020_12.h + redundant/drop_non_array_keywords_unevaluated_2020_12.h + redundant/drop_non_array_keywords_validation_2019_09.h + redundant/drop_non_array_keywords_validation_2020_12.h + redundant/drop_non_boolean_keywords_applicator_2019_09.h + redundant/drop_non_boolean_keywords_applicator_2020_12.h + redundant/drop_non_boolean_keywords_content_2019_09.h + redundant/drop_non_boolean_keywords_content_2020_12.h + redundant/drop_non_boolean_keywords_format_2019_09.h + redundant/drop_non_boolean_keywords_format_2020_12.h + redundant/drop_non_boolean_keywords_unevaluated_2020_12.h + redundant/drop_non_boolean_keywords_validation_2019_09.h + redundant/drop_non_boolean_keywords_validation_2020_12.h + redundant/drop_non_null_keywords_applicator_2019_09.h + redundant/drop_non_null_keywords_applicator_2020_12.h + redundant/drop_non_null_keywords_content_2019_09.h + redundant/drop_non_null_keywords_content_2020_12.h + redundant/drop_non_null_keywords_format_2019_09.h + redundant/drop_non_null_keywords_format_2020_12.h + redundant/drop_non_null_keywords_unevaluated_2020_12.h + redundant/drop_non_null_keywords_validation_2019_09.h + redundant/drop_non_null_keywords_validation_2020_12.h + redundant/drop_non_numeric_keywords_applicator_2019_09.h + redundant/drop_non_numeric_keywords_applicator_2020_12.h + redundant/drop_non_numeric_keywords_content_2019_09.h + redundant/drop_non_numeric_keywords_content_2020_12.h + redundant/drop_non_numeric_keywords_format_2019_09.h + redundant/drop_non_numeric_keywords_format_2020_12.h + redundant/drop_non_numeric_keywords_unevaluated_2020_12.h + redundant/drop_non_numeric_keywords_validation_2019_09.h + redundant/drop_non_numeric_keywords_validation_2020_12.h + redundant/drop_non_object_keywords_applicator_2019_09.h + redundant/drop_non_object_keywords_applicator_2020_12.h + redundant/drop_non_object_keywords_content_2019_09.h + redundant/drop_non_object_keywords_content_2020_12.h + redundant/drop_non_object_keywords_format_2019_09.h + redundant/drop_non_object_keywords_format_2020_12.h + redundant/drop_non_object_keywords_unevaluated_2020_12.h + redundant/drop_non_object_keywords_validation_2019_09.h + redundant/drop_non_object_keywords_validation_2020_12.h + redundant/drop_non_string_keywords_applicator_2019_09.h + redundant/drop_non_string_keywords_applicator_2020_12.h + redundant/drop_non_string_keywords_unevaluated_2020_12.h + redundant/drop_non_string_keywords_validation_2019_09.h + redundant/drop_non_string_keywords_validation_2020_12.h redundant/duplicate_allof_branches.h redundant/duplicate_anyof_branches.h redundant/else_without_if.h diff --git a/src/linter/linter.cc b/src/linter/linter.cc index 6ff2e4a..99e9cbd 100644 --- a/src/linter/linter.cc +++ b/src/linter/linter.cc @@ -14,6 +14,36 @@ auto contains_any(const T &container, const T &values) -> bool { [&values](const auto &element) { return values.contains(element); }); } +template auto every_item_is_null(const T &container) -> bool { + return std::all_of(std::cbegin(container), std::cend(container), + [](const auto &element) { return element.is_null(); }); +} + +template auto every_item_is_boolean(const T &container) -> bool { + return std::all_of(std::cbegin(container), std::cend(container), + [](const auto &element) { return element.is_boolean(); }); +} + +// We don't have to check for "type: null" as that type +// is collapsed to an enum by other canonicalizer rules. +auto is_null_schema(const sourcemeta::jsontoolkit::JSON &schema, + const std::set &vocabularies) -> bool { + return contains_any(vocabularies, + {"https://json-schema.org/draft/2020-12/vocab/validation", + "https://json-schema.org/draft/2019-09/vocab/validation", + "http://json-schema.org/draft-07/schema#", + "http://json-schema.org/draft-06/schema#", + "http://json-schema.org/draft-04/schema#", + "http://json-schema.org/draft-03/schema#", + "http://json-schema.org/draft-02/hyper-schema#", + "http://json-schema.org/draft-01/hyper-schema#"}) && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))); +} + // Modernize #include "modernize/enum_to_const.h" // AntiPattern @@ -34,6 +64,56 @@ auto contains_any(const T &container, const T &values) -> bool { #include "redundant/content_media_type_without_encoding.h" #include "redundant/content_schema_default.h" #include "redundant/content_schema_without_media_type.h" +#include "redundant/drop_non_array_keywords_applicator_2019_09.h" +#include "redundant/drop_non_array_keywords_applicator_2020_12.h" +#include "redundant/drop_non_array_keywords_content_2019_09.h" +#include "redundant/drop_non_array_keywords_content_2020_12.h" +#include "redundant/drop_non_array_keywords_format_2019_09.h" +#include "redundant/drop_non_array_keywords_format_2020_12.h" +#include "redundant/drop_non_array_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_array_keywords_validation_2019_09.h" +#include "redundant/drop_non_array_keywords_validation_2020_12.h" +#include "redundant/drop_non_boolean_keywords_applicator_2019_09.h" +#include "redundant/drop_non_boolean_keywords_applicator_2020_12.h" +#include "redundant/drop_non_boolean_keywords_content_2019_09.h" +#include "redundant/drop_non_boolean_keywords_content_2020_12.h" +#include "redundant/drop_non_boolean_keywords_format_2019_09.h" +#include "redundant/drop_non_boolean_keywords_format_2020_12.h" +#include "redundant/drop_non_boolean_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_boolean_keywords_validation_2019_09.h" +#include "redundant/drop_non_boolean_keywords_validation_2020_12.h" +#include "redundant/drop_non_null_keywords_applicator_2019_09.h" +#include "redundant/drop_non_null_keywords_applicator_2020_12.h" +#include "redundant/drop_non_null_keywords_content_2019_09.h" +#include "redundant/drop_non_null_keywords_content_2020_12.h" +#include "redundant/drop_non_null_keywords_format_2019_09.h" +#include "redundant/drop_non_null_keywords_format_2020_12.h" +#include "redundant/drop_non_null_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_null_keywords_validation_2019_09.h" +#include "redundant/drop_non_null_keywords_validation_2020_12.h" +#include "redundant/drop_non_numeric_keywords_applicator_2019_09.h" +#include "redundant/drop_non_numeric_keywords_applicator_2020_12.h" +#include "redundant/drop_non_numeric_keywords_content_2019_09.h" +#include "redundant/drop_non_numeric_keywords_content_2020_12.h" +#include "redundant/drop_non_numeric_keywords_format_2019_09.h" +#include "redundant/drop_non_numeric_keywords_format_2020_12.h" +#include "redundant/drop_non_numeric_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_numeric_keywords_validation_2019_09.h" +#include "redundant/drop_non_numeric_keywords_validation_2020_12.h" +#include "redundant/drop_non_object_keywords_applicator_2019_09.h" +#include "redundant/drop_non_object_keywords_applicator_2020_12.h" +#include "redundant/drop_non_object_keywords_content_2019_09.h" +#include "redundant/drop_non_object_keywords_content_2020_12.h" +#include "redundant/drop_non_object_keywords_format_2019_09.h" +#include "redundant/drop_non_object_keywords_format_2020_12.h" +#include "redundant/drop_non_object_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_object_keywords_validation_2019_09.h" +#include "redundant/drop_non_object_keywords_validation_2020_12.h" +#include "redundant/drop_non_string_keywords_applicator_2019_09.h" +#include "redundant/drop_non_string_keywords_applicator_2020_12.h" +#include "redundant/drop_non_string_keywords_unevaluated_2020_12.h" +#include "redundant/drop_non_string_keywords_validation_2019_09.h" +#include "redundant/drop_non_string_keywords_validation_2020_12.h" #include "redundant/duplicate_allof_branches.h" #include "redundant/duplicate_anyof_branches.h" #include "redundant/else_without_if.h" @@ -79,6 +159,56 @@ auto add(Bundle &bundle, const LinterCategory category) -> void { bundle.add(); bundle.add(); bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/src/linter/redundant/drop_non_array_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_array_keywords_applicator_2019_09.h new file mode 100644 index 0000000..b243195 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_applicator_2019_09.h @@ -0,0 +1,30 @@ +class DropNonArrayKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonArrayKeywordsApplicator_2019_09() + : Rule{"drop_non_array_keywords_applicator_2019_09", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "properties", "patternProperties", "additionalProperties", + "dependentSchemas", "propertyNames", "unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_array_keywords_applicator_2020_12.h new file mode 100644 index 0000000..05b0802 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_applicator_2020_12.h @@ -0,0 +1,30 @@ +class DropNonArrayKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonArrayKeywordsApplicator_2020_12() + : Rule{"drop_non_array_keywords_applicator_2020_12", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", "patternProperties", + "additionalProperties", + "dependentSchemas", "propertyNames"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_content_2019_09.h b/src/linter/redundant/drop_non_array_keywords_content_2019_09.h new file mode 100644 index 0000000..75fc7a2 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_content_2019_09.h @@ -0,0 +1,29 @@ +class DropNonArrayKeywordsContent_2019_09 final : public Rule { +public: + DropNonArrayKeywordsContent_2019_09() + : Rule{"drop_non_array_keywords_content_2019_09", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_content_2020_12.h b/src/linter/redundant/drop_non_array_keywords_content_2020_12.h new file mode 100644 index 0000000..a2f8263 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_content_2020_12.h @@ -0,0 +1,29 @@ +class DropNonArrayKeywordsContent_2020_12 final : public Rule { +public: + DropNonArrayKeywordsContent_2020_12() + : Rule{"drop_non_array_keywords_content_2020_12", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_format_2019_09.h b/src/linter/redundant/drop_non_array_keywords_format_2019_09.h new file mode 100644 index 0000000..92d34bb --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_format_2019_09.h @@ -0,0 +1,28 @@ +class DropNonArrayKeywordsFormat_2019_09 final : public Rule { +public: + DropNonArrayKeywordsFormat_2019_09() + : Rule{"drop_non_array_keywords_format_2019_09", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/format") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_format_2020_12.h b/src/linter/redundant/drop_non_array_keywords_format_2020_12.h new file mode 100644 index 0000000..4aea095 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_format_2020_12.h @@ -0,0 +1,30 @@ +class DropNonArrayKeywordsFormat_2020_12 final : public Rule { +public: + DropNonArrayKeywordsFormat_2020_12() + : Rule{"drop_non_array_keywords_format_2020_12", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + (vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-annotation") || + vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-assertion")) && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_array_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..e3ae17a --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_unevaluated_2020_12.h @@ -0,0 +1,28 @@ +class DropNonArrayKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonArrayKeywordsUnevaluated_2020_12() + : Rule{"drop_non_array_keywords_unevaluated_2020_12", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_array_keywords_validation_2019_09.h new file mode 100644 index 0000000..dcdcab1 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_validation_2019_09.h @@ -0,0 +1,29 @@ +class DropNonArrayKeywordsValidation_2019_09 final : public Rule { +public: + DropNonArrayKeywordsValidation_2019_09() + : Rule{"drop_non_array_keywords_validation_2019_09", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required"}; +}; diff --git a/src/linter/redundant/drop_non_array_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_array_keywords_validation_2020_12.h new file mode 100644 index 0000000..58ecce5 --- /dev/null +++ b/src/linter/redundant/drop_non_array_keywords_validation_2020_12.h @@ -0,0 +1,29 @@ +class DropNonArrayKeywordsValidation_2020_12 final : public Rule { +public: + DropNonArrayKeywordsValidation_2020_12() + : Rule{"drop_non_array_keywords_validation_2020_12", + "Keywords that don't apply to arrays will never match if the " + "instance is guaranteed to be an array"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "array" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_boolean_keywords_applicator_2019_09.h new file mode 100644 index 0000000..d26cb0f --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_applicator_2019_09.h @@ -0,0 +1,39 @@ +class DropNonBooleanKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonBooleanKeywordsApplicator_2019_09() + : Rule{"drop_non_boolean_keywords_applicator_2019_09", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "additionalItems", + "contains", + "items", + "unevaluatedProperties", + "unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_boolean_keywords_applicator_2020_12.h new file mode 100644 index 0000000..ae03d39 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_applicator_2020_12.h @@ -0,0 +1,37 @@ +class DropNonBooleanKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonBooleanKeywordsApplicator_2020_12() + : Rule{"drop_non_boolean_keywords_applicator_2020_12", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "prefixItems", + "contains", + "items"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_content_2019_09.h b/src/linter/redundant/drop_non_boolean_keywords_content_2019_09.h new file mode 100644 index 0000000..33fe367 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_content_2019_09.h @@ -0,0 +1,31 @@ +class DropNonBooleanKeywordsContent_2019_09 final : public Rule { +public: + DropNonBooleanKeywordsContent_2019_09() + : Rule{"drop_non_boolean_keywords_content_2019_09", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_content_2020_12.h b/src/linter/redundant/drop_non_boolean_keywords_content_2020_12.h new file mode 100644 index 0000000..bf1b98f --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_content_2020_12.h @@ -0,0 +1,31 @@ +class DropNonBooleanKeywordsContent_2020_12 final : public Rule { +public: + DropNonBooleanKeywordsContent_2020_12() + : Rule{"drop_non_boolean_keywords_content_2020_12", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_format_2019_09.h b/src/linter/redundant/drop_non_boolean_keywords_format_2019_09.h new file mode 100644 index 0000000..3e1d681 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_format_2019_09.h @@ -0,0 +1,30 @@ +class DropNonBooleanKeywordsFormat_2019_09 final : public Rule { +public: + DropNonBooleanKeywordsFormat_2019_09() + : Rule{"drop_non_boolean_keywords_format_2019_09", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/format") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_format_2020_12.h b/src/linter/redundant/drop_non_boolean_keywords_format_2020_12.h new file mode 100644 index 0000000..fc93814 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_format_2020_12.h @@ -0,0 +1,32 @@ +class DropNonBooleanKeywordsFormat_2020_12 final : public Rule { +public: + DropNonBooleanKeywordsFormat_2020_12() + : Rule{"drop_non_boolean_keywords_format_2020_12", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + (vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-annotation") || + vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-assertion")) && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_boolean_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..a2e2b3a --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_unevaluated_2020_12.h @@ -0,0 +1,31 @@ +class DropNonBooleanKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonBooleanKeywordsUnevaluated_2020_12() + : Rule{"drop_non_boolean_keywords_unevaluated_2020_12", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedItems", + "unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_boolean_keywords_validation_2019_09.h new file mode 100644 index 0000000..072b8e8 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_validation_2019_09.h @@ -0,0 +1,35 @@ +class DropNonBooleanKeywordsValidation_2019_09 final : public Rule { +public: + DropNonBooleanKeywordsValidation_2019_09() + : Rule{"drop_non_boolean_keywords_validation_2019_09", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_boolean_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_boolean_keywords_validation_2020_12.h new file mode 100644 index 0000000..e3d5b81 --- /dev/null +++ b/src/linter/redundant/drop_non_boolean_keywords_validation_2020_12.h @@ -0,0 +1,35 @@ +class DropNonBooleanKeywordsValidation_2020_12 final : public Rule { +public: + DropNonBooleanKeywordsValidation_2020_12() + : Rule{"drop_non_boolean_keywords_validation_2020_12", + "Keywords that don't apply to booleans will never match if the " + "instance is guaranteed to be a boolean"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "boolean") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_boolean(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_null_keywords_applicator_2019_09.h new file mode 100644 index 0000000..b6279f4 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_applicator_2019_09.h @@ -0,0 +1,39 @@ +class DropNonNullKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonNullKeywordsApplicator_2019_09() + : Rule{"drop_non_null_keywords_applicator_2019_09", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "additionalItems", + "contains", + "items", + "unevaluatedProperties", + "unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_null_keywords_applicator_2020_12.h new file mode 100644 index 0000000..f6958e0 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_applicator_2020_12.h @@ -0,0 +1,37 @@ +class DropNonNullKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonNullKeywordsApplicator_2020_12() + : Rule{"drop_non_null_keywords_applicator_2020_12", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "prefixItems", + "contains", + "items"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_content_2019_09.h b/src/linter/redundant/drop_non_null_keywords_content_2019_09.h new file mode 100644 index 0000000..9343c03 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_content_2019_09.h @@ -0,0 +1,31 @@ +class DropNonNullKeywordsContent_2019_09 final : public Rule { +public: + DropNonNullKeywordsContent_2019_09() + : Rule{"drop_non_null_keywords_content_2019_09", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_content_2020_12.h b/src/linter/redundant/drop_non_null_keywords_content_2020_12.h new file mode 100644 index 0000000..61538b1 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_content_2020_12.h @@ -0,0 +1,31 @@ +class DropNonNullKeywordsContent_2020_12 final : public Rule { +public: + DropNonNullKeywordsContent_2020_12() + : Rule{"drop_non_null_keywords_content_2020_12", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_format_2019_09.h b/src/linter/redundant/drop_non_null_keywords_format_2019_09.h new file mode 100644 index 0000000..28592fc --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_format_2019_09.h @@ -0,0 +1,30 @@ +class DropNonNullKeywordsFormat_2019_09 final : public Rule { +public: + DropNonNullKeywordsFormat_2019_09() + : Rule{"drop_non_null_keywords_format_2019_09", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/format") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_format_2020_12.h b/src/linter/redundant/drop_non_null_keywords_format_2020_12.h new file mode 100644 index 0000000..de26f8d --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_format_2020_12.h @@ -0,0 +1,32 @@ +class DropNonNullKeywordsFormat_2020_12 final : public Rule { +public: + DropNonNullKeywordsFormat_2020_12() + : Rule{"drop_non_null_keywords_format_2020_12", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + (vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-annotation") || + vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-assertion")) && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_null_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..de42538 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_unevaluated_2020_12.h @@ -0,0 +1,31 @@ +class DropNonNullKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonNullKeywordsUnevaluated_2020_12() + : Rule{"drop_non_null_keywords_unevaluated_2020_12", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedItems", + "unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_null_keywords_validation_2019_09.h new file mode 100644 index 0000000..b35331f --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_validation_2019_09.h @@ -0,0 +1,35 @@ +class DropNonNullKeywordsValidation_2019_09 final : public Rule { +public: + DropNonNullKeywordsValidation_2019_09() + : Rule{"drop_non_null_keywords_validation_2019_09", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_null_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_null_keywords_validation_2020_12.h new file mode 100644 index 0000000..be528a4 --- /dev/null +++ b/src/linter/redundant/drop_non_null_keywords_validation_2020_12.h @@ -0,0 +1,35 @@ +class DropNonNullKeywordsValidation_2020_12 final : public Rule { +public: + DropNonNullKeywordsValidation_2020_12() + : Rule{"drop_non_null_keywords_validation_2020_12", + "Keywords that don't apply to null values will never match if the " + "instance is guaranteed to be null"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && + ((schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "null") || + (schema.defines("enum") && schema.at("enum").is_array() && + every_item_is_null(schema.at("enum").as_array()))) && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "dependentRequired", "minProperties", "maxProperties", "required", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_numeric_keywords_applicator_2019_09.h new file mode 100644 index 0000000..6827d61 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_applicator_2019_09.h @@ -0,0 +1,38 @@ +class DropNonNumericKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonNumericKeywordsApplicator_2019_09() + : Rule{"drop_non_numeric_keywords_applicator_2019_09", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "additionalItems", + "contains", + "items", + "unevaluatedProperties", + "unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_numeric_keywords_applicator_2020_12.h new file mode 100644 index 0000000..e475d11 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_applicator_2020_12.h @@ -0,0 +1,36 @@ +class DropNonNumericKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonNumericKeywordsApplicator_2020_12() + : Rule{"drop_non_numeric_keywords_applicator_2020_12", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "prefixItems", + "contains", + "items"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_content_2019_09.h b/src/linter/redundant/drop_non_numeric_keywords_content_2019_09.h new file mode 100644 index 0000000..db6bac2 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_content_2019_09.h @@ -0,0 +1,30 @@ +class DropNonNumericKeywordsContent_2019_09 final : public Rule { +public: + DropNonNumericKeywordsContent_2019_09() + : Rule{"drop_non_numeric_keywords_content_2019_09", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_content_2020_12.h b/src/linter/redundant/drop_non_numeric_keywords_content_2020_12.h new file mode 100644 index 0000000..40f0194 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_content_2020_12.h @@ -0,0 +1,30 @@ +class DropNonNumericKeywordsContent_2020_12 final : public Rule { +public: + DropNonNumericKeywordsContent_2020_12() + : Rule{"drop_non_numeric_keywords_content_2020_12", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_format_2019_09.h b/src/linter/redundant/drop_non_numeric_keywords_format_2019_09.h new file mode 100644 index 0000000..1dc8145 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_format_2019_09.h @@ -0,0 +1,29 @@ +class DropNonNumericKeywordsFormat_2019_09 final : public Rule { +public: + DropNonNumericKeywordsFormat_2019_09() + : Rule{"drop_non_numeric_keywords_format_2019_09", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/format") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_format_2020_12.h b/src/linter/redundant/drop_non_numeric_keywords_format_2020_12.h new file mode 100644 index 0000000..1fbe343 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_format_2020_12.h @@ -0,0 +1,31 @@ +class DropNonNumericKeywordsFormat_2020_12 final : public Rule { +public: + DropNonNumericKeywordsFormat_2020_12() + : Rule{"drop_non_numeric_keywords_format_2020_12", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + (vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-annotation") || + vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-assertion")) && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_numeric_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..919ff86 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_unevaluated_2020_12.h @@ -0,0 +1,30 @@ +class DropNonNumericKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonNumericKeywordsUnevaluated_2020_12() + : Rule{"drop_non_numeric_keywords_unevaluated_2020_12", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedItems", + "unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_numeric_keywords_validation_2019_09.h new file mode 100644 index 0000000..7d6f186 --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_validation_2019_09.h @@ -0,0 +1,30 @@ +class DropNonNumericKeywordsValidation_2019_09 final : public Rule { +public: + DropNonNumericKeywordsValidation_2019_09() + : Rule{"drop_non_numeric_keywords_validation_2019_09", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "dependentRequired", + "minProperties", "maxProperties", "required", "minItems", + "maxItems", "minContains", "maxContains", "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_numeric_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_numeric_keywords_validation_2020_12.h new file mode 100644 index 0000000..c74e72b --- /dev/null +++ b/src/linter/redundant/drop_non_numeric_keywords_validation_2020_12.h @@ -0,0 +1,30 @@ +class DropNonNumericKeywordsValidation_2020_12 final : public Rule { +public: + DropNonNumericKeywordsValidation_2020_12() + : Rule{"drop_non_numeric_keywords_validation_2020_12", + "Keywords that don't apply to numbers will never match if the " + "instance is guaranteed to be a number"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + (schema.at("type").to_string() == "integer" || + schema.at("type").to_string() == "number") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "dependentRequired", + "minProperties", "maxProperties", "required", "minItems", + "maxItems", "minContains", "maxContains", "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_object_keywords_applicator_2019_09.h new file mode 100644 index 0000000..59e255e --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_applicator_2019_09.h @@ -0,0 +1,29 @@ +class DropNonObjectKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonObjectKeywordsApplicator_2019_09() + : Rule{"drop_non_object_keywords_applicator_2019_09", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"additionalItems", "contains", "items", + "unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_object_keywords_applicator_2020_12.h new file mode 100644 index 0000000..212d009 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_applicator_2020_12.h @@ -0,0 +1,28 @@ +class DropNonObjectKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonObjectKeywordsApplicator_2020_12() + : Rule{"drop_non_object_keywords_applicator_2020_12", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"prefixItems", "contains", "items"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_content_2019_09.h b/src/linter/redundant/drop_non_object_keywords_content_2019_09.h new file mode 100644 index 0000000..eb73b34 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_content_2019_09.h @@ -0,0 +1,29 @@ +class DropNonObjectKeywordsContent_2019_09 final : public Rule { +public: + DropNonObjectKeywordsContent_2019_09() + : Rule{"drop_non_object_keywords_content_2019_09", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_content_2020_12.h b/src/linter/redundant/drop_non_object_keywords_content_2020_12.h new file mode 100644 index 0000000..70d2485 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_content_2020_12.h @@ -0,0 +1,29 @@ +class DropNonObjectKeywordsContent_2020_12 final : public Rule { +public: + DropNonObjectKeywordsContent_2020_12() + : Rule{"drop_non_object_keywords_content_2020_12", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/content") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"contentEncoding", "contentMediaType", + "contentSchema"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_format_2019_09.h b/src/linter/redundant/drop_non_object_keywords_format_2019_09.h new file mode 100644 index 0000000..3ac6973 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_format_2019_09.h @@ -0,0 +1,28 @@ +class DropNonObjectKeywordsFormat_2019_09 final : public Rule { +public: + DropNonObjectKeywordsFormat_2019_09() + : Rule{"drop_non_object_keywords_format_2019_09", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/format") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_format_2020_12.h b/src/linter/redundant/drop_non_object_keywords_format_2020_12.h new file mode 100644 index 0000000..8ab84ed --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_format_2020_12.h @@ -0,0 +1,30 @@ +class DropNonObjectKeywordsFormat_2020_12 final : public Rule { +public: + DropNonObjectKeywordsFormat_2020_12() + : Rule{"drop_non_object_keywords_format_2020_12", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + (vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-annotation") || + vocabularies.contains("https://json-schema.org/draft/2020-12/vocab/" + "format-assertion")) && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"format"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_object_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..a486125 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_unevaluated_2020_12.h @@ -0,0 +1,28 @@ +class DropNonObjectKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonObjectKeywordsUnevaluated_2020_12() + : Rule{"drop_non_object_keywords_unevaluated_2020_12", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_object_keywords_validation_2019_09.h new file mode 100644 index 0000000..d41cff0 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_validation_2019_09.h @@ -0,0 +1,30 @@ +class DropNonObjectKeywordsValidation_2019_09 final : public Rule { +public: + DropNonObjectKeywordsValidation_2019_09() + : Rule{"drop_non_object_keywords_validation_2019_09", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_object_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_object_keywords_validation_2020_12.h new file mode 100644 index 0000000..90dc496 --- /dev/null +++ b/src/linter/redundant/drop_non_object_keywords_validation_2020_12.h @@ -0,0 +1,30 @@ +class DropNonObjectKeywordsValidation_2020_12 final : public Rule { +public: + DropNonObjectKeywordsValidation_2020_12() + : Rule{"drop_non_object_keywords_validation_2020_12", + "Keywords that don't apply to objects will never match if the " + "instance is guaranteed to be an object"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "object" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "minLength", "maxLength", "pattern", "maximum", + "exclusiveMinimum", "multipleOf", "exclusiveMaximum", "minimum", + "minItems", "maxItems", "minContains", "maxContains", + "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_string_keywords_applicator_2019_09.h b/src/linter/redundant/drop_non_string_keywords_applicator_2019_09.h new file mode 100644 index 0000000..7133783 --- /dev/null +++ b/src/linter/redundant/drop_non_string_keywords_applicator_2019_09.h @@ -0,0 +1,37 @@ +class DropNonStringKeywordsApplicator_2019_09 final : public Rule { +public: + DropNonStringKeywordsApplicator_2019_09() + : Rule{"drop_non_string_keywords_applicator_2019_09", + "Keywords that don't apply to strings will never match if the " + "instance is guaranteed to be a string"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "string" && + vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "additionalItems", + "contains", + "items", + "unevaluatedProperties", + "unevaluatedItems"}; +}; diff --git a/src/linter/redundant/drop_non_string_keywords_applicator_2020_12.h b/src/linter/redundant/drop_non_string_keywords_applicator_2020_12.h new file mode 100644 index 0000000..0f6c4e7 --- /dev/null +++ b/src/linter/redundant/drop_non_string_keywords_applicator_2020_12.h @@ -0,0 +1,35 @@ +class DropNonStringKeywordsApplicator_2020_12 final : public Rule { +public: + DropNonStringKeywordsApplicator_2020_12() + : Rule{"drop_non_string_keywords_applicator_2020_12", + "Keywords that don't apply to strings will never match if the " + "instance is guaranteed to be a string"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "string" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/applicator") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"properties", + "patternProperties", + "additionalProperties", + "dependentSchemas", + "propertyNames", + "prefixItems", + "contains", + "items"}; +}; diff --git a/src/linter/redundant/drop_non_string_keywords_unevaluated_2020_12.h b/src/linter/redundant/drop_non_string_keywords_unevaluated_2020_12.h new file mode 100644 index 0000000..2d7495f --- /dev/null +++ b/src/linter/redundant/drop_non_string_keywords_unevaluated_2020_12.h @@ -0,0 +1,29 @@ +class DropNonStringKeywordsUnevaluated_2020_12 final : public Rule { +public: + DropNonStringKeywordsUnevaluated_2020_12() + : Rule{"drop_non_string_keywords_unevaluated_2020_12", + "Keywords that don't apply to strings will never match if the " + "instance is guaranteed to be a string"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "string" && + vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/unevaluated") && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{"unevaluatedItems", + "unevaluatedProperties"}; +}; diff --git a/src/linter/redundant/drop_non_string_keywords_validation_2019_09.h b/src/linter/redundant/drop_non_string_keywords_validation_2019_09.h new file mode 100644 index 0000000..0d69e75 --- /dev/null +++ b/src/linter/redundant/drop_non_string_keywords_validation_2019_09.h @@ -0,0 +1,30 @@ +class DropNonStringKeywordsValidation_2019_09 final : public Rule { +public: + DropNonStringKeywordsValidation_2019_09() + : Rule{"drop_non_string_keywords_validation_2019_09", + "Keywords that don't apply to strings will never match if the " + "instance is guaranteed to be a string"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "string" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "maximum", "exclusiveMinimum", "multipleOf", "exclusiveMaximum", + "minimum", "dependentRequired", "minProperties", "maxProperties", + "required", "minItems", "maxItems", "minContains", + "maxContains", "uniqueItems"}; +}; diff --git a/src/linter/redundant/drop_non_string_keywords_validation_2020_12.h b/src/linter/redundant/drop_non_string_keywords_validation_2020_12.h new file mode 100644 index 0000000..1caa459 --- /dev/null +++ b/src/linter/redundant/drop_non_string_keywords_validation_2020_12.h @@ -0,0 +1,30 @@ +class DropNonStringKeywordsValidation_2020_12 final : public Rule { +public: + DropNonStringKeywordsValidation_2020_12() + : Rule{"drop_non_string_keywords_validation_2020_12", + "Keywords that don't apply to strings will never match if the " + "instance is guaranteed to be a string"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &, + const std::set &vocabularies, + const sourcemeta::jsontoolkit::Pointer &) const -> bool override { + return vocabularies.contains( + "https://json-schema.org/draft/2020-12/vocab/validation") && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() == "string" && + schema.defines_any(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + + auto transform(Transformer &transformer) const -> void override { + transformer.erase_keys(this->BLACKLIST.cbegin(), this->BLACKLIST.cend()); + } + +private: + const std::set BLACKLIST{ + "maximum", "exclusiveMinimum", "multipleOf", "exclusiveMaximum", + "minimum", "dependentRequired", "minProperties", "maxProperties", + "required", "minItems", "maxItems", "minContains", + "maxContains", "uniqueItems"}; +}; diff --git a/test/linter/2019_09_test.cc b/test/linter/2019_09_test.cc index 0e47a19..e04ecf1 100644 --- a/test/linter/2019_09_test.cc +++ b/test/linter/2019_09_test.cc @@ -932,3 +932,189 @@ TEST(Lint_2019_09, min_properties_covered_by_required_3) { EXPECT_EQ(document, expected); } + +TEST(Lint_2019_09, drop_non_array_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "array", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "array", + "minItems": 1 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_boolean_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "boolean", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "boolean" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_null_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "null", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "null" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_numeric_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "number", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "number", + "multipleOf": 2 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_numeric_keywords_2) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "integer", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "integer", + "multipleOf": 2 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_object_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "object", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "object", + "unevaluatedProperties": false, + "maxProperties": 3 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2019_09, drop_non_string_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "string", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "type": "string", + "contentEncoding": "base64", + "format": "uri" + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/linter/2020_12_test.cc b/test/linter/2020_12_test.cc index 1023018..b6e3590 100644 --- a/test/linter/2020_12_test.cc +++ b/test/linter/2020_12_test.cc @@ -915,3 +915,189 @@ TEST(Lint_2020_12, min_properties_covered_by_required_3) { EXPECT_EQ(document, expected); } + +TEST(Lint_2020_12, drop_non_array_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "array", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "array", + "minItems": 1 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_boolean_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "boolean", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "boolean" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_null_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "null", + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "null" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_numeric_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "number", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "number", + "multipleOf": 2 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_numeric_keywords_2) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "integer", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "integer", + "multipleOf": 2 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_object_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "object", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "object", + "unevaluatedProperties": false, + "maxProperties": 3 + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(Lint_2020_12, drop_non_string_keywords_1) { + sourcemeta::jsontoolkit::JSON document = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "string", + "multipleOf": 2, + "unevaluatedProperties": false, + "contentEncoding": "base64", + "maxProperties": 3, + "format": "uri", + "minItems": 1 + })JSON"); + + LINT_AND_FIX(document); + + const sourcemeta::jsontoolkit::JSON expected = + sourcemeta::jsontoolkit::parse(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "type": "string", + "contentEncoding": "base64", + "format": "uri" + })JSON"); + + EXPECT_EQ(document, expected); +}