Skip to content

Commit

Permalink
[WIP] Add redundancy rules for keyword that won't apply to given types
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Sep 10, 2024
1 parent bb285c6 commit 1f23b44
Show file tree
Hide file tree
Showing 54 changed files with 2,122 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/linter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
130 changes: 130 additions & 0 deletions src/linter/linter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@ auto contains_any(const T &container, const T &values) -> bool {
[&values](const auto &element) { return values.contains(element); });
}

template <typename T> 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 <typename T> 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<std::string> &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
Expand All @@ -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"
Expand Down Expand Up @@ -79,6 +159,56 @@ auto add(Bundle &bundle, const LinterCategory category) -> void {
bundle.add<ContentMediaTypeWithoutEncoding>();
bundle.add<ContentSchemaDefault>();
bundle.add<ContentSchemaWithoutMediaType>();
bundle.add<DropNonArrayKeywordsApplicator_2019_09>();
bundle.add<DropNonArrayKeywordsApplicator_2020_12>();
bundle.add<DropNonArrayKeywordsContent_2019_09>();
bundle.add<DropNonArrayKeywordsContent_2020_12>();
bundle.add<DropNonArrayKeywordsFormat_2019_09>();
bundle.add<DropNonArrayKeywordsFormat_2020_12>();
bundle.add<DropNonArrayKeywordsUnevaluated_2020_12>();
bundle.add<DropNonArrayKeywordsValidation_2019_09>();
bundle.add<DropNonArrayKeywordsValidation_2020_12>();
bundle.add<DropNonBooleanKeywordsApplicator_2019_09>();
bundle.add<DropNonBooleanKeywordsApplicator_2020_12>();
bundle.add<DropNonBooleanKeywordsContent_2019_09>();
bundle.add<DropNonBooleanKeywordsContent_2020_12>();
bundle.add<DropNonBooleanKeywordsFormat_2019_09>();
bundle.add<DropNonBooleanKeywordsFormat_2020_12>();
bundle.add<DropNonBooleanKeywordsUnevaluated_2020_12>();
bundle.add<DropNonBooleanKeywordsValidation_2019_09>();
bundle.add<DropNonBooleanKeywordsValidation_2020_12>();
bundle.add<DropNonNullKeywordsApplicator_2019_09>();
bundle.add<DropNonNullKeywordsApplicator_2020_12>();
bundle.add<DropNonNullKeywordsContent_2019_09>();
bundle.add<DropNonNullKeywordsContent_2020_12>();
bundle.add<DropNonNullKeywordsFormat_2019_09>();
bundle.add<DropNonNullKeywordsFormat_2020_12>();
bundle.add<DropNonNullKeywordsUnevaluated_2020_12>();
bundle.add<DropNonNullKeywordsValidation_2019_09>();
bundle.add<DropNonNullKeywordsValidation_2020_12>();
bundle.add<DropNonNumericKeywordsApplicator_2019_09>();
bundle.add<DropNonNumericKeywordsApplicator_2020_12>();
bundle.add<DropNonNumericKeywordsContent_2019_09>();
bundle.add<DropNonNumericKeywordsContent_2020_12>();
bundle.add<DropNonNumericKeywordsFormat_2019_09>();
bundle.add<DropNonNumericKeywordsFormat_2020_12>();
bundle.add<DropNonNumericKeywordsUnevaluated_2020_12>();
bundle.add<DropNonNumericKeywordsValidation_2019_09>();
bundle.add<DropNonNumericKeywordsValidation_2020_12>();
bundle.add<DropNonObjectKeywordsApplicator_2019_09>();
bundle.add<DropNonObjectKeywordsApplicator_2020_12>();
bundle.add<DropNonObjectKeywordsContent_2019_09>();
bundle.add<DropNonObjectKeywordsContent_2020_12>();
bundle.add<DropNonObjectKeywordsFormat_2019_09>();
bundle.add<DropNonObjectKeywordsFormat_2020_12>();
bundle.add<DropNonObjectKeywordsUnevaluated_2020_12>();
bundle.add<DropNonObjectKeywordsValidation_2019_09>();
bundle.add<DropNonObjectKeywordsValidation_2020_12>();
bundle.add<DropNonStringKeywordsApplicator_2019_09>();
bundle.add<DropNonStringKeywordsApplicator_2020_12>();
bundle.add<DropNonStringKeywordsUnevaluated_2020_12>();
bundle.add<DropNonStringKeywordsValidation_2019_09>();
bundle.add<DropNonStringKeywordsValidation_2020_12>();
bundle.add<DuplicateAllOfBranches>();
bundle.add<DuplicateAnyOfBranches>();
bundle.add<ElseWithoutIf>();
Expand Down
30 changes: 30 additions & 0 deletions src/linter/redundant/drop_non_array_keywords_applicator_2019_09.h
Original file line number Diff line number Diff line change
@@ -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<std::string> &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<std::string> BLACKLIST{
"properties", "patternProperties", "additionalProperties",
"dependentSchemas", "propertyNames", "unevaluatedProperties"};
};
30 changes: 30 additions & 0 deletions src/linter/redundant/drop_non_array_keywords_applicator_2020_12.h
Original file line number Diff line number Diff line change
@@ -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<std::string> &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<std::string> BLACKLIST{"properties", "patternProperties",
"additionalProperties",
"dependentSchemas", "propertyNames"};
};
29 changes: 29 additions & 0 deletions src/linter/redundant/drop_non_array_keywords_content_2019_09.h
Original file line number Diff line number Diff line change
@@ -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<std::string> &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<std::string> BLACKLIST{"contentEncoding", "contentMediaType",
"contentSchema"};
};
29 changes: 29 additions & 0 deletions src/linter/redundant/drop_non_array_keywords_content_2020_12.h
Original file line number Diff line number Diff line change
@@ -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<std::string> &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<std::string> BLACKLIST{"contentEncoding", "contentMediaType",
"contentSchema"};
};
28 changes: 28 additions & 0 deletions src/linter/redundant/drop_non_array_keywords_format_2019_09.h
Original file line number Diff line number Diff line change
@@ -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<std::string> &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<std::string> BLACKLIST{"format"};
};
Loading

0 comments on commit 1f23b44

Please sign in to comment.