Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/extension/alterschema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
linter/definitions_to_defs.h
linter/unknown_keywords_prefix.h
linter/non_applicable_enum_validation_keywords.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 @@ -94,6 +94,7 @@ inline auto APPLIES_TO_POINTERS(std::vector<Pointer> &&keywords)
#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 @@ -130,6 +131,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
47 changes: 47 additions & 0 deletions src/extension/alterschema/linter/then_false.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 {
ONLY_CONTINUE_IF(
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() &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is_schema(schema.at("then")) is unnecessary if you are already checking its a boolean right afterwards

!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);
})));

if (schema.defines("else")) {
return APPLIES_TO_KEYWORDS("if", "then", "else");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return APPLIES_TO_KEYWORDS("if", "then", "else");
return APPLIES_TO_KEYWORDS("then", "if", "else");

Let's keep then as the first entry, as that's the main keyword we are discussing here

} else {
return APPLIES_TO_KEYWORDS("if", "then");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return APPLIES_TO_KEYWORDS("if", "then");
return APPLIES_TO_KEYWORDS("then", "if");

}
}

auto transform(JSON &schema, const Result &) 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 @@ -3275,3 +3275,179 @@ TEST(AlterSchema_lint_2019_09, unknown_keywords_prefix_11) {

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