Skip to content

Commit bffc321

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

7 files changed

Lines changed: 382 additions & 0 deletions

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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
Vocabularies::Known::JSON_Schema_Draft_6}) &&
25+
schema.is_object());
26+
27+
const auto *exclusive_minimum{schema.try_at("exclusiveMinimum")};
28+
ONLY_CONTINUE_IF(exclusive_minimum && exclusive_minimum->is_number());
29+
const auto *exclusive_maximum{schema.try_at("exclusiveMaximum")};
30+
ONLY_CONTINUE_IF(exclusive_maximum && exclusive_maximum->is_number() &&
31+
*exclusive_minimum >= *exclusive_maximum);
32+
33+
return APPLIES_TO_KEYWORDS("exclusiveMinimum", "exclusiveMaximum");
34+
}
35+
};

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_draft6_test.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,92 @@ TEST(AlterSchema_lint_draft6, exclusive_minimum_integer_to_minimum_4) {
12911291
EXPECT_EQ(document, expected);
12921292
}
12931293

1294+
TEST(AlterSchema_lint_draft6, incoherent_exclusive_limits_1) {
1295+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1296+
"$schema": "http://json-schema.org/draft-06/schema#",
1297+
"title": "Test",
1298+
"description": "A test schema",
1299+
"examples": [ "foo" ],
1300+
"exclusiveMinimum": 1,
1301+
"exclusiveMaximum": 5
1302+
})JSON");
1303+
1304+
LINT_WITHOUT_FIX(document, result, traces);
1305+
1306+
EXPECT_TRUE(result.first);
1307+
EXPECT_EQ(traces.size(), 0);
1308+
}
1309+
1310+
TEST(AlterSchema_lint_draft6, incoherent_exclusive_limits_2) {
1311+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1312+
"$schema": "http://json-schema.org/draft-06/schema#",
1313+
"title": "Test",
1314+
"description": "A test schema",
1315+
"examples": [ "foo" ],
1316+
"exclusiveMinimum": 1
1317+
})JSON");
1318+
1319+
LINT_WITHOUT_FIX(document, result, traces);
1320+
1321+
EXPECT_TRUE(result.first);
1322+
EXPECT_EQ(traces.size(), 0);
1323+
}
1324+
1325+
TEST(AlterSchema_lint_draft6, incoherent_exclusive_limits_3) {
1326+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1327+
"$schema": "http://json-schema.org/draft-06/schema#",
1328+
"title": "Test",
1329+
"description": "A test schema",
1330+
"examples": [ "foo" ],
1331+
"exclusiveMinimum": 3,
1332+
"exclusiveMaximum": 3
1333+
})JSON");
1334+
1335+
LINT_WITHOUT_FIX(document, result, traces);
1336+
1337+
EXPECT_FALSE(result.first);
1338+
EXPECT_EQ(traces.size(), 1);
1339+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
1340+
"`exclusiveMinimum` greater than or equal to "
1341+
"`exclusiveMaximum` makes the schema unsatisfiable",
1342+
false);
1343+
}
1344+
1345+
TEST(AlterSchema_lint_draft6, incoherent_exclusive_limits_4) {
1346+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1347+
"$schema": "http://json-schema.org/draft-06/schema#",
1348+
"title": "Test",
1349+
"description": "A test schema",
1350+
"examples": [ "foo" ],
1351+
"exclusiveMinimum": 5,
1352+
"exclusiveMaximum": 2
1353+
})JSON");
1354+
1355+
LINT_WITHOUT_FIX(document, result, traces);
1356+
1357+
EXPECT_FALSE(result.first);
1358+
EXPECT_EQ(traces.size(), 1);
1359+
EXPECT_LINT_TRACE(traces, 0, "", "incoherent_exclusive_limits",
1360+
"`exclusiveMinimum` greater than or equal to "
1361+
"`exclusiveMaximum` makes the schema unsatisfiable",
1362+
false);
1363+
}
1364+
1365+
TEST(AlterSchema_lint_draft6, incoherent_exclusive_limits_5) {
1366+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1367+
"$schema": "http://json-schema.org/draft-06/schema#",
1368+
"title": "Test",
1369+
"description": "A test schema",
1370+
"examples": [ "foo" ],
1371+
"exclusiveMaximum": 5
1372+
})JSON");
1373+
1374+
LINT_WITHOUT_FIX(document, result, traces);
1375+
1376+
EXPECT_TRUE(result.first);
1377+
EXPECT_EQ(traces.size(), 0);
1378+
}
1379+
12941380
TEST(AlterSchema_lint_draft6, equal_numeric_bounds_to_const_1) {
12951381
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
12961382
"$schema": "http://json-schema.org/draft-06/schema#",

0 commit comments

Comments
 (0)