diff --git a/src/extension/alterschema/CMakeLists.txt b/src/extension/alterschema/CMakeLists.txt index 995e528cd..29e9cb974 100644 --- a/src/extension/alterschema/CMakeLists.txt +++ b/src/extension/alterschema/CMakeLists.txt @@ -60,6 +60,8 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema linter/max_contains_without_contains.h linter/min_contains_without_contains.h linter/modern_official_dialect_with_empty_fragment.h + linter/then_empty.h + linter/else_empty.h linter/then_without_if.h) if(SOURCEMETA_CORE_INSTALL) diff --git a/src/extension/alterschema/alterschema.cc b/src/extension/alterschema/alterschema.cc index 46933d4ef..6eb8e771f 100644 --- a/src/extension/alterschema/alterschema.cc +++ b/src/extension/alterschema/alterschema.cc @@ -50,6 +50,7 @@ contains_any(const Vocabularies &container, #include "linter/duplicate_anyof_branches.h" #include "linter/duplicate_enum_values.h" #include "linter/duplicate_required_values.h" +#include "linter/else_empty.h" #include "linter/else_without_if.h" #include "linter/enum_to_const.h" #include "linter/enum_with_type.h" @@ -69,6 +70,7 @@ contains_any(const Vocabularies &container, #include "linter/pattern_properties_default.h" #include "linter/properties_default.h" #include "linter/single_type_array.h" +#include "linter/then_empty.h" #include "linter/then_without_if.h" #include "linter/unevaluated_items_default.h" #include "linter/unevaluated_properties_default.h" @@ -98,6 +100,8 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) bundle.add(); bundle.add(); bundle.add(); + bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/src/extension/alterschema/linter/else_empty.h b/src/extension/alterschema/linter/else_empty.h new file mode 100644 index 000000000..03f808700 --- /dev/null +++ b/src/extension/alterschema/linter/else_empty.h @@ -0,0 +1,26 @@ +class ElseEmpty final : public SchemaTransformRule { +public: + ElseEmpty() + : SchemaTransformRule{"else_empty", + "Setting the `else` keyword to the empty schema " + "does not add any further constraint"} {}; + + [[nodiscard]] auto + condition(const JSON &schema, const JSON &, const Vocabularies &vocabularies, + const SchemaFrame &, const SchemaFrame::Location &, + const SchemaWalker &, const SchemaResolver &) const + -> SchemaTransformRule::Result override { + return contains_any( + vocabularies, + {"https://json-schema.org/draft/2020-12/vocab/applicator", + "https://json-schema.org/draft/2019-09/vocab/applicator", + "http://json-schema.org/draft-07/schema#"}) && + schema.is_object() && schema.defines("if") && + schema.defines("else") && is_schema(schema.at("else")) && + ((schema.at("else").is_object() && schema.at("else").empty()) || + (schema.at("else").is_boolean() && schema.at("else").to_boolean() && + !(schema.at("if").is_boolean() && schema.at("if").to_boolean()))); + } + + auto transform(JSON &schema) const -> void override { schema.erase("else"); } +}; diff --git a/src/extension/alterschema/linter/then_empty.h b/src/extension/alterschema/linter/then_empty.h new file mode 100644 index 000000000..2cae575d5 --- /dev/null +++ b/src/extension/alterschema/linter/then_empty.h @@ -0,0 +1,26 @@ +class ThenEmpty final : public SchemaTransformRule { +public: + ThenEmpty() + : SchemaTransformRule{"then_empty", + "Setting the `then` keyword to the empty schema " + "does not add any further constraint"} {}; + + [[nodiscard]] auto + condition(const JSON &schema, const JSON &, const Vocabularies &vocabularies, + const SchemaFrame &, const SchemaFrame::Location &, + const SchemaWalker &, const SchemaResolver &) const + -> SchemaTransformRule::Result override { + return contains_any( + vocabularies, + {"https://json-schema.org/draft/2020-12/vocab/applicator", + "https://json-schema.org/draft/2019-09/vocab/applicator", + "http://json-schema.org/draft-07/schema#"}) && + schema.is_object() && schema.defines("if") && + schema.defines("then") && is_schema(schema.at("then")) && + ((schema.at("then").is_object() && schema.at("then").empty()) || + (schema.at("then").is_boolean() && schema.at("then").to_boolean() && + !(schema.at("if").is_boolean() && schema.at("if").to_boolean()))); + } + + auto transform(JSON &schema) const -> void override { schema.erase("then"); } +}; diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index f8b26ac7c..6c45d58c8 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -2032,3 +2032,105 @@ TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_empty_fragment_3) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_2019_09, then_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, else_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, then_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {}, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, else_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + } + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index cde0a7955..32dd65b4d 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -2207,3 +2207,164 @@ TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_empty_fragment_3) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_2020_12, then_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, else_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, then_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {}, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, else_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, else_empty_3) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "else": true + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, then_empty_3) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": true + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2020_12, then_empty_4) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "then": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} \ No newline at end of file diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index d86917544..a189693b0 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -1442,3 +1442,160 @@ TEST(AlterSchema_lint_draft7, additional_items_with_schema_items_array_items) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_draft7, then_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, else_empty_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, then_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": {}, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, else_empty_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + }, + "else": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, then_empty_3) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + }, + "else": { + "type": "number" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "if": { + "properties": { + "flag": { + "const": true + } + } + }, + "then": { + "type": "string" + }, + "else": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, then_empty_4) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "then": {} + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +}