Skip to content

Commit d8625e0

Browse files
committed
lint: add incoherent_exclusive_limits rule
Signed-off-by: AcE <kintan0108@gmail.com>
1 parent bf77f60 commit d8625e0

11 files changed

Lines changed: 486 additions & 0 deletions

src/alterschema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema
9696
common/exclusive_minimum_number_and_minimum.h
9797
common/if_without_then_else.h
9898
common/ignored_metaschema.h
99+
common/incoherent_exclusive_limits.h
99100
common/max_contains_without_contains.h
100101
common/maximum_real_for_integer.h
101102
common/min_contains_without_contains.h

src/alterschema/alterschema.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ auto WALK_UP_IN_PLACE_APPLICATORS(const JSON &root, const SchemaFrame &frame,
202202
#include "common/flatten_nested_extends.h"
203203
#include "common/if_without_then_else.h"
204204
#include "common/ignored_metaschema.h"
205+
#include "common/incoherent_exclusive_limits.h"
205206
#include "common/max_contains_without_contains.h"
206207
#include "common/maximum_real_for_integer.h"
207208
#include "common/min_contains_without_contains.h"
@@ -339,6 +340,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
339340
if (mode == AlterSchemaMode::Canonicalizer) {
340341
bundle.add<ExclusiveMinimumBooleanIntegerFold>();
341342
bundle.add<ExclusiveMaximumBooleanIntegerFold>();
343+
bundle.add<IncoherentExclusiveLimits>();
342344
bundle.add<UnsatisfiableExclusiveEqualBounds>();
343345
bundle.add<MinimumCanEqualIntegerFold>();
344346
bundle.add<MaximumCanEqualIntegerFold>();
@@ -464,6 +466,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
464466
bundle.add<UnevaluatedItemsDefault>();
465467
bundle.add<UnevaluatedPropertiesDefault>();
466468
bundle.add<UnsatisfiableMaxContains>();
469+
bundle.add<IncoherentExclusiveLimits>();
467470
bundle.add<IncoherentMinMaxContains>();
468471
bundle.add<UnsatisfiableMinProperties>();
469472
bundle.add<EnumToConst>();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class IncoherentExclusiveLimits final : public SchemaTransformRule {
2+
public:
3+
using mutates = std::true_type;
4+
using reframe_after_transform = std::false_type;
5+
IncoherentExclusiveLimits()
6+
: SchemaTransformRule{
7+
"incoherent_exclusive_limits",
8+
"`exclusiveMinimum` greater than or equal to "
9+
"`exclusiveMaximum` makes the schema unsatisfiable"} {};
10+
11+
[[nodiscard]] auto
12+
condition(const sourcemeta::core::JSON &schema,
13+
const sourcemeta::core::JSON &, const Vocabularies &vocabularies,
14+
const SchemaFrame &, const SchemaFrame::Location &,
15+
const SchemaWalker &, const SchemaResolver &) const
16+
-> SchemaTransformRule::Result override {
17+
ONLY_CONTINUE_IF(schema.is_object());
18+
ONLY_CONTINUE_IF(vocabularies.contains_any(
19+
{Vocabularies::Known::JSON_Schema_2020_12_Validation,
20+
Vocabularies::Known::JSON_Schema_2019_09_Validation,
21+
Vocabularies::Known::JSON_Schema_Draft_7,
22+
Vocabularies::Known::JSON_Schema_Draft_6}));
23+
const auto *exclusive_minimum{schema.try_at("exclusiveMinimum")};
24+
ONLY_CONTINUE_IF(exclusive_minimum && exclusive_minimum->is_number());
25+
const auto *exclusive_maximum{schema.try_at("exclusiveMaximum")};
26+
ONLY_CONTINUE_IF(exclusive_maximum && exclusive_maximum->is_number() &&
27+
*exclusive_minimum >= *exclusive_maximum);
28+
return APPLIES_TO_KEYWORDS("exclusiveMinimum", "exclusiveMaximum");
29+
}
30+
31+
auto transform(JSON &schema, const Result &) const -> void override {
32+
schema.into(JSON{false});
33+
}
34+
};

test/alterschema/alterschema_canonicalize_2019_09_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,30 @@ TEST_F(Canonicalizer201909Test, exclusive_minimum_integer_to_minimum_3) {
323323
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
324324
}
325325

326+
TEST_F(Canonicalizer201909Test, incoherent_exclusive_limits_1) {
327+
auto document = sourcemeta::core::parse_json(R"JSON({
328+
"$schema": "https://json-schema.org/draft/2019-09/schema",
329+
"exclusiveMinimum": 3,
330+
"exclusiveMaximum": 3
331+
})JSON");
332+
333+
const auto expected = sourcemeta::core::parse_json("false");
334+
335+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
336+
}
337+
338+
TEST_F(Canonicalizer201909Test, incoherent_exclusive_limits_2) {
339+
auto document = sourcemeta::core::parse_json(R"JSON({
340+
"$schema": "https://json-schema.org/draft/2019-09/schema",
341+
"exclusiveMinimum": 5,
342+
"exclusiveMaximum": 2
343+
})JSON");
344+
345+
const auto expected = sourcemeta::core::parse_json("false");
346+
347+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
348+
}
349+
326350
TEST_F(Canonicalizer201909Test, exclusive_minimum_integer_to_minimum_5) {
327351
auto document = sourcemeta::core::parse_json(R"JSON({
328352
"$schema": "https://json-schema.org/draft/2019-09/schema",

test/alterschema/alterschema_canonicalize_2020_12_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,30 @@ TEST_F(Canonicalizer202012Test, exclusive_minimum_integer_to_minimum_3) {
665665
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
666666
}
667667

668+
TEST_F(Canonicalizer202012Test, incoherent_exclusive_limits_1) {
669+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
670+
"$schema": "https://json-schema.org/draft/2020-12/schema",
671+
"exclusiveMinimum": 3,
672+
"exclusiveMaximum": 3
673+
})JSON");
674+
675+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json("false");
676+
677+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
678+
}
679+
680+
TEST_F(Canonicalizer202012Test, incoherent_exclusive_limits_2) {
681+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
682+
"$schema": "https://json-schema.org/draft/2020-12/schema",
683+
"exclusiveMinimum": 5,
684+
"exclusiveMaximum": 2
685+
})JSON");
686+
687+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json("false");
688+
689+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
690+
}
691+
668692
TEST_F(Canonicalizer202012Test, exclusive_minimum_integer_to_minimum_5) {
669693
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
670694
"$schema": "https://json-schema.org/draft/2020-12/schema",

test/alterschema/alterschema_canonicalize_draft6_test.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,34 @@ TEST_F(CanonicalizerDraft6Test, integer_both_exclusive_bounds_fold) {
437437
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
438438
}
439439

440+
TEST_F(CanonicalizerDraft6Test, incoherent_exclusive_limits_1) {
441+
auto document = sourcemeta::core::parse_json(R"JSON({
442+
"$schema": "http://json-schema.org/draft-06/schema#",
443+
"exclusiveMinimum": 3,
444+
"exclusiveMaximum": 3
445+
})JSON");
446+
447+
const auto expected = sourcemeta::core::parse_json(R"JSON(
448+
false
449+
)JSON");
450+
451+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
452+
}
453+
454+
TEST_F(CanonicalizerDraft6Test, incoherent_exclusive_limits_2) {
455+
auto document = sourcemeta::core::parse_json(R"JSON({
456+
"$schema": "http://json-schema.org/draft-06/schema#",
457+
"exclusiveMinimum": 5,
458+
"exclusiveMaximum": 2
459+
})JSON");
460+
461+
const auto expected = sourcemeta::core::parse_json(R"JSON(
462+
false
463+
)JSON");
464+
465+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
466+
}
467+
440468
TEST_F(CanonicalizerDraft6Test, number_bare) {
441469
auto document = sourcemeta::core::parse_json(R"JSON({
442470
"$schema": "http://json-schema.org/draft-06/schema#",

test/alterschema/alterschema_canonicalize_draft7_test.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,34 @@ TEST_F(CanonicalizerDraft7Test, integer_both_exclusive_bounds_fold) {
437437
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
438438
}
439439

440+
TEST_F(CanonicalizerDraft7Test, incoherent_exclusive_limits_1) {
441+
auto document = sourcemeta::core::parse_json(R"JSON({
442+
"$schema": "http://json-schema.org/draft-07/schema#",
443+
"exclusiveMinimum": 3,
444+
"exclusiveMaximum": 3
445+
})JSON");
446+
447+
const auto expected = sourcemeta::core::parse_json(R"JSON(
448+
false
449+
)JSON");
450+
451+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
452+
}
453+
454+
TEST_F(CanonicalizerDraft7Test, incoherent_exclusive_limits_2) {
455+
auto document = sourcemeta::core::parse_json(R"JSON({
456+
"$schema": "http://json-schema.org/draft-07/schema#",
457+
"exclusiveMinimum": 5,
458+
"exclusiveMaximum": 2
459+
})JSON");
460+
461+
const auto expected = sourcemeta::core::parse_json(R"JSON(
462+
false
463+
)JSON");
464+
465+
CANONICALIZE_AND_VALIDATE(document, expected, *compiled_meta_);
466+
}
467+
440468
TEST_F(CanonicalizerDraft7Test, number_bare) {
441469
auto document = sourcemeta::core::parse_json(R"JSON({
442470
"$schema": "http://json-schema.org/draft-07/schema#",

test/alterschema/alterschema_lint_2019_09_test.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,92 @@ TEST(AlterSchema_lint_2019_09, incoherent_min_max_contains_9) {
24172417
true);
24182418
}
24192419

2420+
TEST(AlterSchema_lint_2019_09, incoherent_exclusive_limits_1) {
2421+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2422+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2423+
"title": "Test",
2424+
"description": "A test schema",
2425+
"examples": [ "foo" ],
2426+
"exclusiveMinimum": 1,
2427+
"exclusiveMaximum": 5
2428+
})JSON");
2429+
2430+
LINT_WITHOUT_FIX(document, result, traces);
2431+
2432+
EXPECT_TRUE(result.first);
2433+
EXPECT_EQ(traces.size(), 0);
2434+
}
2435+
2436+
TEST(AlterSchema_lint_2019_09, incoherent_exclusive_limits_2) {
2437+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2438+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2439+
"title": "Test",
2440+
"description": "A test schema",
2441+
"examples": [ "foo" ],
2442+
"exclusiveMinimum": 1
2443+
})JSON");
2444+
2445+
LINT_WITHOUT_FIX(document, result, traces);
2446+
2447+
EXPECT_TRUE(result.first);
2448+
EXPECT_EQ(traces.size(), 0);
2449+
}
2450+
2451+
TEST(AlterSchema_lint_2019_09, incoherent_exclusive_limits_3) {
2452+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2453+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2454+
"title": "Test",
2455+
"description": "A test schema",
2456+
"examples": [ "foo" ],
2457+
"exclusiveMinimum": 3,
2458+
"exclusiveMaximum": 3
2459+
})JSON");
2460+
2461+
LINT_WITHOUT_FIX(document, result, traces);
2462+
2463+
EXPECT_FALSE(result.first);
2464+
EXPECT_EQ(traces.size(), 1);
2465+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
2466+
"`exclusiveMinimum` greater than or equal to "
2467+
"`exclusiveMaximum` makes the schema unsatisfiable",
2468+
true);
2469+
}
2470+
2471+
TEST(AlterSchema_lint_2019_09, incoherent_exclusive_limits_4) {
2472+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2473+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2474+
"title": "Test",
2475+
"description": "A test schema",
2476+
"examples": [ "foo" ],
2477+
"exclusiveMinimum": 5,
2478+
"exclusiveMaximum": 2
2479+
})JSON");
2480+
2481+
LINT_WITHOUT_FIX(document, result, traces);
2482+
2483+
EXPECT_FALSE(result.first);
2484+
EXPECT_EQ(traces.size(), 1);
2485+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
2486+
"`exclusiveMinimum` greater than or equal to "
2487+
"`exclusiveMaximum` makes the schema unsatisfiable",
2488+
true);
2489+
}
2490+
2491+
TEST(AlterSchema_lint_2019_09, incoherent_exclusive_limits_5) {
2492+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2493+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2494+
"title": "Test",
2495+
"description": "A test schema",
2496+
"examples": [ "foo" ],
2497+
"exclusiveMaximum": 5
2498+
})JSON");
2499+
2500+
LINT_WITHOUT_FIX(document, result, traces);
2501+
2502+
EXPECT_TRUE(result.first);
2503+
EXPECT_EQ(traces.size(), 0);
2504+
}
2505+
24202506
TEST(AlterSchema_lint_2019_09, equal_numeric_bounds_to_const_1) {
24212507
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
24222508
"$schema": "https://json-schema.org/draft/2019-09/schema",

test/alterschema/alterschema_lint_2020_12_test.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,92 @@ TEST(AlterSchema_lint_2020_12, incoherent_min_max_contains_9) {
23242324
true);
23252325
}
23262326

2327+
TEST(AlterSchema_lint_2020_12, incoherent_exclusive_limits_1) {
2328+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2329+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2330+
"title": "Test",
2331+
"description": "A test schema",
2332+
"examples": [ "foo" ],
2333+
"exclusiveMinimum": 1,
2334+
"exclusiveMaximum": 5
2335+
})JSON");
2336+
2337+
LINT_WITHOUT_FIX(document, result, traces);
2338+
2339+
EXPECT_TRUE(result.first);
2340+
EXPECT_EQ(traces.size(), 0);
2341+
}
2342+
2343+
TEST(AlterSchema_lint_2020_12, incoherent_exclusive_limits_2) {
2344+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2345+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2346+
"title": "Test",
2347+
"description": "A test schema",
2348+
"examples": [ "foo" ],
2349+
"exclusiveMinimum": 1
2350+
})JSON");
2351+
2352+
LINT_WITHOUT_FIX(document, result, traces);
2353+
2354+
EXPECT_TRUE(result.first);
2355+
EXPECT_EQ(traces.size(), 0);
2356+
}
2357+
2358+
TEST(AlterSchema_lint_2020_12, incoherent_exclusive_limits_3) {
2359+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2360+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2361+
"title": "Test",
2362+
"description": "A test schema",
2363+
"examples": [ "foo" ],
2364+
"exclusiveMinimum": 3,
2365+
"exclusiveMaximum": 3
2366+
})JSON");
2367+
2368+
LINT_WITHOUT_FIX(document, result, traces);
2369+
2370+
EXPECT_FALSE(result.first);
2371+
EXPECT_EQ(traces.size(), 1);
2372+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
2373+
"`exclusiveMinimum` greater than or equal to "
2374+
"`exclusiveMaximum` makes the schema unsatisfiable",
2375+
true);
2376+
}
2377+
2378+
TEST(AlterSchema_lint_2020_12, incoherent_exclusive_limits_4) {
2379+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2380+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2381+
"title": "Test",
2382+
"description": "A test schema",
2383+
"examples": [ "foo" ],
2384+
"exclusiveMinimum": 5,
2385+
"exclusiveMaximum": 2
2386+
})JSON");
2387+
2388+
LINT_WITHOUT_FIX(document, result, traces);
2389+
2390+
EXPECT_FALSE(result.first);
2391+
EXPECT_EQ(traces.size(), 1);
2392+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
2393+
"`exclusiveMinimum` greater than or equal to "
2394+
"`exclusiveMaximum` makes the schema unsatisfiable",
2395+
true);
2396+
}
2397+
2398+
TEST(AlterSchema_lint_2020_12, incoherent_exclusive_limits_5) {
2399+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2400+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2401+
"title": "Test",
2402+
"description": "A test schema",
2403+
"examples": [ "foo" ],
2404+
"exclusiveMaximum": 5
2405+
})JSON");
2406+
2407+
LINT_WITHOUT_FIX(document, result, traces);
2408+
2409+
EXPECT_TRUE(result.first);
2410+
EXPECT_EQ(traces.size(), 0);
2411+
}
2412+
23272413
TEST(AlterSchema_lint_2020_12, equal_numeric_bounds_to_const_1) {
23282414
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
23292415
"$schema": "https://json-schema.org/draft/2020-12/schema",

0 commit comments

Comments
 (0)