Skip to content

Commit efb534c

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

6 files changed

Lines changed: 295 additions & 0 deletions

File tree

src/alterschema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema
136136
linter/enum_to_const.h
137137
linter/equal_numeric_bounds_to_const.h
138138
linter/forbid_empty_enum.h
139+
linter/incoherent_exclusive_limits.h
139140
linter/incoherent_min_max_contains.h
140141
linter/invalid_external_ref.h
141142
linter/items_array_default.h

src/alterschema/alterschema.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ auto WALK_UP_IN_PLACE_APPLICATORS(const JSON &root, const SchemaFrame &frame,
247247
#include "linter/enum_to_const.h"
248248
#include "linter/equal_numeric_bounds_to_const.h"
249249
#include "linter/forbid_empty_enum.h"
250+
#include "linter/incoherent_exclusive_limits.h"
250251
#include "linter/incoherent_min_max_contains.h"
251252
#include "linter/invalid_external_ref.h"
252253
#include "linter/items_array_default.h"
@@ -464,6 +465,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
464465
bundle.add<UnevaluatedItemsDefault>();
465466
bundle.add<UnevaluatedPropertiesDefault>();
466467
bundle.add<UnsatisfiableMaxContains>();
468+
bundle.add<IncoherentExclusiveLimits>();
467469
bundle.add<IncoherentMinMaxContains>();
468470
bundle.add<UnsatisfiableMinProperties>();
469471
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::false_type;
4+
using reframe_after_transform = std::false_type;
5+
IncoherentExclusiveLimits()
6+
: SchemaTransformRule{
7+
"incoherent_exclusive_limits",
8+
"`exclusiveMinimum` greater than or equal to `exclusiveMaximum` "
9+
"makes the schema unsatisfiable"} {};
10+
11+
[[nodiscard]] auto
12+
condition(const sourcemeta::core::JSON &schema,
13+
const sourcemeta::core::JSON &,
14+
const sourcemeta::core::Vocabularies &vocabularies,
15+
const sourcemeta::core::SchemaFrame &,
16+
const sourcemeta::core::SchemaFrame::Location &,
17+
const sourcemeta::core::SchemaWalker &,
18+
const sourcemeta::core::SchemaResolver &) const
19+
-> SchemaTransformRule::Result override {
20+
ONLY_CONTINUE_IF(vocabularies.contains_any(
21+
{Vocabularies::Known::JSON_Schema_2020_12_Validation,
22+
Vocabularies::Known::JSON_Schema_2019_09_Validation,
23+
Vocabularies::Known::JSON_Schema_Draft_7}) &&
24+
schema.is_object());
25+
26+
const auto *exclusive_minimum{schema.try_at("exclusiveMinimum")};
27+
ONLY_CONTINUE_IF(exclusive_minimum && exclusive_minimum->is_number());
28+
const auto *exclusive_maximum{schema.try_at("exclusiveMaximum")};
29+
ONLY_CONTINUE_IF(exclusive_maximum && exclusive_maximum->is_number() &&
30+
*exclusive_minimum >= *exclusive_maximum);
31+
32+
return APPLIES_TO_KEYWORDS("exclusiveMinimum", "exclusiveMaximum");
33+
}
34+
};

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+
false);
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+
false);
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+
false);
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+
false);
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",

test/alterschema/alterschema_lint_draft7_test.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3041,6 +3041,92 @@ TEST(AlterSchema_lint_draft7, conflicting_readonly_writeonly_4) {
30413041
false);
30423042
}
30433043

3044+
TEST(AlterSchema_lint_draft7, incoherent_exclusive_limits_1) {
3045+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
3046+
"$schema": "http://json-schema.org/draft-07/schema#",
3047+
"title": "Test",
3048+
"description": "A test schema",
3049+
"examples": [ "foo" ],
3050+
"exclusiveMinimum": 1,
3051+
"exclusiveMaximum": 5
3052+
})JSON");
3053+
3054+
LINT_WITHOUT_FIX(document, result, traces);
3055+
3056+
EXPECT_TRUE(result.first);
3057+
EXPECT_EQ(traces.size(), 0);
3058+
}
3059+
3060+
TEST(AlterSchema_lint_draft7, incoherent_exclusive_limits_2) {
3061+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
3062+
"$schema": "http://json-schema.org/draft-07/schema#",
3063+
"title": "Test",
3064+
"description": "A test schema",
3065+
"examples": [ "foo" ],
3066+
"exclusiveMinimum": 1
3067+
})JSON");
3068+
3069+
LINT_WITHOUT_FIX(document, result, traces);
3070+
3071+
EXPECT_TRUE(result.first);
3072+
EXPECT_EQ(traces.size(), 0);
3073+
}
3074+
3075+
TEST(AlterSchema_lint_draft7, incoherent_exclusive_limits_3) {
3076+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
3077+
"$schema": "http://json-schema.org/draft-07/schema#",
3078+
"title": "Test",
3079+
"description": "A test schema",
3080+
"examples": [ "foo" ],
3081+
"exclusiveMinimum": 3,
3082+
"exclusiveMaximum": 3
3083+
})JSON");
3084+
3085+
LINT_WITHOUT_FIX(document, result, traces);
3086+
3087+
EXPECT_FALSE(result.first);
3088+
EXPECT_EQ(traces.size(), 1);
3089+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
3090+
"`exclusiveMinimum` greater than or equal to "
3091+
"`exclusiveMaximum` makes the schema unsatisfiable",
3092+
false);
3093+
}
3094+
3095+
TEST(AlterSchema_lint_draft7, incoherent_exclusive_limits_4) {
3096+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
3097+
"$schema": "http://json-schema.org/draft-07/schema#",
3098+
"title": "Test",
3099+
"description": "A test schema",
3100+
"examples": [ "foo" ],
3101+
"exclusiveMinimum": 5,
3102+
"exclusiveMaximum": 2
3103+
})JSON");
3104+
3105+
LINT_WITHOUT_FIX(document, result, traces);
3106+
3107+
EXPECT_FALSE(result.first);
3108+
EXPECT_EQ(traces.size(), 1);
3109+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
3110+
"`exclusiveMinimum` greater than or equal to "
3111+
"`exclusiveMaximum` makes the schema unsatisfiable",
3112+
false);
3113+
}
3114+
3115+
TEST(AlterSchema_lint_draft7, incoherent_exclusive_limits_5) {
3116+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
3117+
"$schema": "http://json-schema.org/draft-07/schema#",
3118+
"title": "Test",
3119+
"description": "A test schema",
3120+
"examples": [ "foo" ],
3121+
"exclusiveMaximum": 5
3122+
})JSON");
3123+
3124+
LINT_WITHOUT_FIX(document, result, traces);
3125+
3126+
EXPECT_TRUE(result.first);
3127+
EXPECT_EQ(traces.size(), 0);
3128+
}
3129+
30443130
TEST(AlterSchema_lint_draft7, duplicate_examples_1) {
30453131
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
30463132
"$schema": "http://json-schema.org/draft-07/schema#",

0 commit comments

Comments
 (0)