Skip to content

Linter: create rule to to add syntactical sugar when then is set to false #1918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions src/extension/alterschema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
linter/property_names_default.h
linter/draft_ref_siblings.h
linter/definitions_to_defs.h
linter/then_false.h

# Strict
strict/required_properties_in_properties.h)
Expand Down
2 changes: 2 additions & 0 deletions src/extension/alterschema/alterschema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ contains_any(const Vocabularies &container,
#include "linter/property_names_type_default.h"
#include "linter/single_type_array.h"
#include "linter/then_empty.h"
#include "linter/then_false.h"
#include "linter/then_without_if.h"
#include "linter/unevaluated_items_default.h"
#include "linter/unevaluated_properties_default.h"
Expand Down Expand Up @@ -108,6 +109,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
bundle.add<NotFalse>();
bundle.add<ThenEmpty>();
bundle.add<ElseEmpty>();
bundle.add<ThenFalse>();
bundle.add<ThenWithoutIf>();
bundle.add<DependenciesPropertyTautology>();
bundle.add<DependentRequiredTautology>();
Expand Down
42 changes: 42 additions & 0 deletions src/extension/alterschema/linter/then_false.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class ThenFalse final : public SchemaTransformRule {
public:
ThenFalse()
: SchemaTransformRule{
"then_false",
"`Setting the `then` keyword to the false schema is a convoluted "
"way of negating the `if` subschema, which you can more cleanly "
"represent using the `not` keyword"} {};

[[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_boolean() && !schema.at("then").to_boolean() &&
is_schema(schema.at("if")) &&
!(schema.at("if").is_boolean() && schema.at("if").to_boolean()) &&
(!schema.defines("else") || (schema.at("else").is_boolean() &&
schema.at("else").to_boolean())) &&
(!schema.at("if").is_object() ||
std::all_of(schema.at("if").as_object().begin(),
schema.at("if").as_object().end(),
[&schema](const auto &entry) {
return !schema.defines(entry.first);
}));
}

auto transform(JSON &schema) const -> void override {
auto if_schema = schema.at("if");
schema.erase("if");
schema.erase("then");
schema.erase("else");
schema.assign("not", std::move(if_schema));
}
};
176 changes: 176 additions & 0 deletions test/alterschema/alterschema_lint_2019_09_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2831,3 +2831,179 @@ TEST(AlterSchema_lint_2019_09, required_properties_in_properties_5) {

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_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": false
})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",
"not": {
"properties": {
"flag": {
"const": true
}
}
}
})JSON");

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_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": false,
"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/2019-09/schema",
"not": {
"properties": {
"flag": {
"const": true
}
}
}
})JSON");

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_3) {
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"if": true,
"then": false
})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": true,
"then": false
})JSON");

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_4) {
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"if": false,
"then": false
})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_false_5) {
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"name": { "type": "string" }
},
"if": {
"properties": {
"name": { "type": "number" }
}
},
"then": false
})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",
"type": "object",
"properties": {
"name": { "type": "string" }
},
"if": {
"properties": {
"name": { "type": "number" }
}
},
"then": false
})JSON");

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_6) {
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": false
})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",
"not": {
"properties": {
"flag": { "const": true }
}
}
})JSON");

EXPECT_EQ(document, expected);
}

TEST(AlterSchema_lint_2019_09, then_false_7) {
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"if": {
"type": "object"
},
"then": false,
"else": false
})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": {
"type": "object"
},
"then": false,
"else": false
})JSON");

EXPECT_EQ(document, expected);
}
Loading
Loading