diff --git a/DEPENDENCIES b/DEPENDENCIES index 96d04ac3..b9d1ebbd 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 31d3868e3f4217be3ed5de3b0ea31d521f1b873c +core https://github.com/sourcemeta/core e4d7ae9358710fc138d2afd3179db6d850e4190f bootstrap https://github.com/twbs/bootstrap 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e diff --git a/src/compiler/compiler.cc b/src/compiler/compiler.cc index beccea09..e8e60ca9 100644 --- a/src/compiler/compiler.cc +++ b/src/compiler/compiler.cc @@ -6,6 +6,8 @@ #include "encoding.h" +#include // assert + static auto transformer_callback_noop(const sourcemeta::core::Pointer &, const std::string_view, const std::string_view, @@ -25,8 +27,10 @@ auto canonicalize(sourcemeta::core::JSON &schema, sourcemeta::core::SchemaTransformer canonicalizer; sourcemeta::core::add(canonicalizer, sourcemeta::core::AlterSchemaMode::Canonicalizer); - canonicalizer.apply(schema, walker, make_resolver(resolver), - transformer_callback_noop, default_dialect); + [[maybe_unused]] const auto result = + canonicalizer.apply(schema, walker, make_resolver(resolver), + transformer_callback_noop, default_dialect); + assert(result.first); } auto make_encoding(sourcemeta::core::JSON &document, @@ -83,8 +87,10 @@ auto compile(sourcemeta::core::JSON &schema, // Numbers mapper.add(); - mapper.apply(schema, walker, make_resolver(resolver), - transformer_callback_noop, default_dialect); + [[maybe_unused]] const auto mapper_result = + mapper.apply(schema, walker, make_resolver(resolver), + transformer_callback_noop, default_dialect); + assert(mapper_result.first); // The "any" encoding is always the last resort const auto dialect{sourcemeta::core::dialect(schema)}; diff --git a/test/compiler/2020_12_canonicalizer_any_test.cc b/test/compiler/2020_12_canonicalizer_any_test.cc index 6eda9089..6c292677 100644 --- a/test/compiler/2020_12_canonicalizer_any_test.cc +++ b/test/compiler/2020_12_canonicalizer_any_test.cc @@ -61,7 +61,7 @@ TEST(JSONBinPack_Canonicalizer_Any_2020_12, duplicate_anyof_branches_1) { const auto expected = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ - { "type": "number", "multipleOf": 1 }, + { "type": "number" }, { "type": "string", "minLength": 0 } ] })JSON"); @@ -85,7 +85,7 @@ TEST(JSONBinPack_Canonicalizer_Any_2020_12, duplicate_anyof_branches_2) { const auto expected = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ - { "type": "number", "multipleOf": 1 }, + { "type": "number" }, { "type": "string", "minLength": 0 } ] })JSON"); @@ -116,7 +116,7 @@ TEST(JSONBinPack_Canonicalizer_Any_2020_12, duplicate_anyof_branches_3) { const auto expected = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ - { "type": "number", "multipleOf": 1 }, + { "type": "number" }, { "type": "string", "minLength": 0 } ] })JSON"); @@ -245,12 +245,7 @@ TEST(JSONBinPack_Canonicalizer_Any_2020_12, implicit_type_union_1) { "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] })JSON"); @@ -284,12 +279,7 @@ TEST(JSONBinPack_Canonicalizer_Any_2020_12, boolean_schema_1) { "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] })JSON"); diff --git a/test/compiler/2020_12_canonicalizer_number_test.cc b/test/compiler/2020_12_canonicalizer_number_test.cc index 2970eab8..6b1b8c2c 100644 --- a/test/compiler/2020_12_canonicalizer_number_test.cc +++ b/test/compiler/2020_12_canonicalizer_number_test.cc @@ -495,7 +495,6 @@ TEST(JSONBinPack_Canonicalizer_Number_2020_12, drop_non_numeric_keywords_1) { const auto expected = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "number", - "multipleOf": 1, "maximum": 4 })JSON"); diff --git a/test/compiler/2020_12_canonicalizer_object_test.cc b/test/compiler/2020_12_canonicalizer_object_test.cc index 2a4f60c8..8213cb87 100644 --- a/test/compiler/2020_12_canonicalizer_object_test.cc +++ b/test/compiler/2020_12_canonicalizer_object_test.cc @@ -29,8 +29,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "bar": { @@ -40,8 +39,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -75,8 +73,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "bar": { @@ -86,8 +83,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -121,8 +117,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "foo": { @@ -132,8 +127,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -231,8 +225,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_1) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "bar": { @@ -242,8 +235,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_1) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "baz": { @@ -253,8 +245,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_1) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -290,8 +281,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "baz": { @@ -301,8 +291,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "foo": { @@ -312,8 +301,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "qux": { @@ -323,8 +311,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, dependent_required_tautology_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -356,8 +343,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, duplicate_required_values_1) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } @@ -389,8 +375,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, duplicate_required_values_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "baz": { @@ -400,8 +385,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, duplicate_required_values_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] }, "foo": { @@ -411,8 +395,7 @@ TEST(JSONBinPack_Canonicalizer_Object_2020_12, duplicate_required_values_2) { { "type": "object", "minProperties": 0, "properties": {} }, { "type": "array", "minItems": 0 }, { "type": "string", "minLength": 0 }, - { "type": "number", "multipleOf": 1 }, - { "type": "integer", "multipleOf": 1 } + { "type": "number" } ] } } diff --git a/test/e2e/circleciblank/schema-less/canonical.json b/test/e2e/circleciblank/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/circleciblank/schema-less/canonical.json +++ b/test/e2e/circleciblank/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/circlecimatrix/schema-less/canonical.json b/test/e2e/circlecimatrix/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/circlecimatrix/schema-less/canonical.json +++ b/test/e2e/circlecimatrix/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/commitlint/schema-less/canonical.json b/test/e2e/commitlint/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/commitlint/schema-less/canonical.json +++ b/test/e2e/commitlint/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/commitlintbasic/schema-less/canonical.json b/test/e2e/commitlintbasic/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/commitlintbasic/schema-less/canonical.json +++ b/test/e2e/commitlintbasic/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/epr/schema-less/canonical.json b/test/e2e/epr/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/epr/schema-less/canonical.json +++ b/test/e2e/epr/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/eslintrc/schema-less/canonical.json b/test/e2e/eslintrc/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/eslintrc/schema-less/canonical.json +++ b/test/e2e/eslintrc/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/esmrc/schema-less/canonical.json b/test/e2e/esmrc/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/esmrc/schema-less/canonical.json +++ b/test/e2e/esmrc/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/geojson/schema-less/canonical.json b/test/e2e/geojson/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/geojson/schema-less/canonical.json +++ b/test/e2e/geojson/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/githubfundingblank/schema-less/canonical.json b/test/e2e/githubfundingblank/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/githubfundingblank/schema-less/canonical.json +++ b/test/e2e/githubfundingblank/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/githubworkflow/schema-less/canonical.json b/test/e2e/githubworkflow/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/githubworkflow/schema-less/canonical.json +++ b/test/e2e/githubworkflow/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/gruntcontribclean/schema-less/canonical.json b/test/e2e/gruntcontribclean/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/gruntcontribclean/schema-less/canonical.json +++ b/test/e2e/gruntcontribclean/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/imageoptimizerwebjob/schema-less/canonical.json b/test/e2e/imageoptimizerwebjob/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/imageoptimizerwebjob/schema-less/canonical.json +++ b/test/e2e/imageoptimizerwebjob/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/jsonereversesort/schema-less/canonical.json b/test/e2e/jsonereversesort/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/jsonereversesort/schema-less/canonical.json +++ b/test/e2e/jsonereversesort/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/jsonesort/schema-less/canonical.json b/test/e2e/jsonesort/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/jsonesort/schema-less/canonical.json +++ b/test/e2e/jsonesort/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/jsonfeed/schema-less/canonical.json b/test/e2e/jsonfeed/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/jsonfeed/schema-less/canonical.json +++ b/test/e2e/jsonfeed/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/jsonresume/schema-less/canonical.json b/test/e2e/jsonresume/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/jsonresume/schema-less/canonical.json +++ b/test/e2e/jsonresume/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/mixed-bounded-object/schema-less/canonical.json b/test/e2e/mixed-bounded-object/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/mixed-bounded-object/schema-less/canonical.json +++ b/test/e2e/mixed-bounded-object/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/netcoreproject/schema-less/canonical.json b/test/e2e/netcoreproject/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/netcoreproject/schema-less/canonical.json +++ b/test/e2e/netcoreproject/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/nightwatch/schema-less/canonical.json b/test/e2e/nightwatch/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/nightwatch/schema-less/canonical.json +++ b/test/e2e/nightwatch/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/openweathermap/schema-less/canonical.json b/test/e2e/openweathermap/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/openweathermap/schema-less/canonical.json +++ b/test/e2e/openweathermap/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/openweatherroadrisk/schema-less/canonical.json b/test/e2e/openweatherroadrisk/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/openweatherroadrisk/schema-less/canonical.json +++ b/test/e2e/openweatherroadrisk/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/ox-test/schema-less/canonical.json b/test/e2e/ox-test/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/ox-test/schema-less/canonical.json +++ b/test/e2e/ox-test/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/packagejson/schema-less/canonical.json b/test/e2e/packagejson/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/packagejson/schema-less/canonical.json +++ b/test/e2e/packagejson/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/packagejsonlintrc/schema-less/canonical.json b/test/e2e/packagejsonlintrc/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/packagejsonlintrc/schema-less/canonical.json +++ b/test/e2e/packagejsonlintrc/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/sapcloudsdkpipeline/schema-less/canonical.json b/test/e2e/sapcloudsdkpipeline/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/sapcloudsdkpipeline/schema-less/canonical.json +++ b/test/e2e/sapcloudsdkpipeline/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/travisnotifications/schema-less/canonical.json b/test/e2e/travisnotifications/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/travisnotifications/schema-less/canonical.json +++ b/test/e2e/travisnotifications/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/tslintbasic/schema-less/canonical.json b/test/e2e/tslintbasic/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/tslintbasic/schema-less/canonical.json +++ b/test/e2e/tslintbasic/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/tslintextend/schema-less/canonical.json b/test/e2e/tslintextend/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/tslintextend/schema-less/canonical.json +++ b/test/e2e/tslintextend/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/test/e2e/tslintmulti/schema-less/canonical.json b/test/e2e/tslintmulti/schema-less/canonical.json index 6fb94381..4aee8402 100644 --- a/test/e2e/tslintmulti/schema-less/canonical.json +++ b/test/e2e/tslintmulti/schema-less/canonical.json @@ -20,12 +20,7 @@ "minLength": 0 }, { - "type": "number", - "multipleOf": 1 - }, - { - "type": "integer", - "multipleOf": 1 + "type": "number" } ] } diff --git a/vendor/core/config.cmake.in b/vendor/core/config.cmake.in index 2db4b0aa..2a663c02 100644 --- a/vendor/core/config.cmake.in +++ b/vendor/core/config.cmake.in @@ -92,6 +92,8 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) find_dependency(mpdecimal CONFIG) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") + find_dependency(PCRE2 CONFIG) + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonschema.cmake") diff --git a/vendor/core/src/core/jsonschema/frame.cc b/vendor/core/src/core/jsonschema/frame.cc index 90c52550..9f9bf66c 100644 --- a/vendor/core/src/core/jsonschema/frame.cc +++ b/vendor/core/src/core/jsonschema/frame.cc @@ -5,9 +5,9 @@ #include // std::less #include // std::map #include // std::optional -#include // std::set #include // std::ostringstream #include // std::unordered_map +#include // std::unordered_set #include // std::pair, std::move #include // std::vector @@ -268,7 +268,9 @@ auto traverse_origin_instance_locations( const sourcemeta::core::SchemaFrame::Instances &instances, const sourcemeta::core::Pointer ¤t, const std::optional &accumulator, - sourcemeta::core::SchemaFrame::Instances::mapped_type &destination) + sourcemeta::core::SchemaFrame::Instances::mapped_type &destination, + std::unordered_set< + const sourcemeta::core::SchemaFrame::References::value_type *> &visited) -> void { if (accumulator.has_value() && std::ranges::find(destination, accumulator.value()) == @@ -277,23 +279,25 @@ auto traverse_origin_instance_locations( } for (const auto &reference : frame.references_to(current)) { - const auto subschema_pointer{reference.get().first.second.initial()}; - // Avoid recursing to itself, in the case of circular subschemas - if (subschema_pointer == current) { + if (visited.contains(&reference.get())) { continue; } + visited.insert(&reference.get()); + + const auto subschema_pointer{reference.get().first.second.initial()}; const auto match{instances.find(subschema_pointer)}; if (match != instances.cend()) { for (const auto &instance_location : match->second) { traverse_origin_instance_locations(frame, instances, subschema_pointer, - instance_location, destination); + instance_location, destination, + visited); } } else { // Even if the parent doesn't have instance locations yet, // recurse to find the origin of the reference chain traverse_origin_instance_locations(frame, instances, subschema_pointer, - std::nullopt, destination); + std::nullopt, destination, visited); } } } @@ -1060,9 +1064,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, continue; } + std::unordered_set visited; traverse_origin_instance_locations( *this, this->instances_, entry.second.pointer, std::nullopt, - this->instances_[entry.second.pointer]); + this->instances_[entry.second.pointer], visited); } // Second pass: inherit instance locations from parents (top-down). @@ -1087,9 +1092,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker, continue; } + std::unordered_set visited; traverse_origin_instance_locations( *this, this->instances_, entry.second.pointer, std::nullopt, - this->instances_[entry.second.pointer]); + this->instances_[entry.second.pointer], visited); } } } diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_transform.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_transform.h index 6b23e944..eb609061 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_transform.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_transform.h @@ -251,10 +251,11 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaTransformer { const SchemaTransformRule::Result &)>; /// Apply the bundle of rules to a schema - auto apply(JSON &schema, const SchemaWalker &walker, - const SchemaResolver &resolver, const Callback &callback, - const std::optional &default_dialect = std::nullopt, - const std::optional &default_id = std::nullopt) const + [[nodiscard]] auto + apply(JSON &schema, const SchemaWalker &walker, + const SchemaResolver &resolver, const Callback &callback, + const std::optional &default_dialect = std::nullopt, + const std::optional &default_id = std::nullopt) const -> std::pair; /// Report back the rules from the bundle that need to be applied to a schema diff --git a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h index b0c269f3..8f2845bc 100644 --- a/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h +++ b/vendor/core/src/core/jsonschema/include/sourcemeta/core/jsonschema_types.h @@ -161,6 +161,9 @@ struct SchemaWalkerResult { /// The keywords a given keyword depends on (if any) during the evaluation /// process std::unordered_set dependencies; + /// The keywords a given keyword depends on for evaluation ordering purposes + /// only (not semantic dependencies) + std::unordered_set order_dependencies; /// The JSON instance types that this keyword applies to (empty means all) JSON::TypeSet instances; @@ -175,9 +178,12 @@ struct SchemaWalkerResult { SchemaWalkerResult(SchemaKeywordType type_, std::optional vocabulary_, std::unordered_set dependencies_, + std::unordered_set order_dependencies_, JSON::TypeSet instances_) : type{type_}, vocabulary{std::move(vocabulary_)}, - dependencies{std::move(dependencies_)}, instances{instances_} {} + dependencies{std::move(dependencies_)}, + order_dependencies{std::move(order_dependencies_)}, + instances{instances_} {} }; /// @ingroup jsonschema diff --git a/vendor/core/src/core/jsonschema/jsonschema.cc b/vendor/core/src/core/jsonschema/jsonschema.cc index 20e30ad7..b7be25a5 100644 --- a/vendor/core/src/core/jsonschema/jsonschema.cc +++ b/vendor/core/src/core/jsonschema/jsonschema.cc @@ -474,14 +474,23 @@ auto sourcemeta::core::schema_keyword_priority( const sourcemeta::core::Vocabularies &vocabularies, const sourcemeta::core::SchemaWalker &walker) -> std::uint64_t { const auto &result{walker(keyword, vocabularies)}; - return std::accumulate( + const auto priority_from_dependencies{std::accumulate( result.dependencies.cbegin(), result.dependencies.cend(), static_cast(0), [&vocabularies, &walker](const auto accumulator, const auto &dependency) { return std::max( accumulator, schema_keyword_priority(dependency, vocabularies, walker) + 1); - }); + })}; + const auto priority_from_order_dependencies{std::accumulate( + result.order_dependencies.cbegin(), result.order_dependencies.cend(), + static_cast(0), + [&vocabularies, &walker](const auto accumulator, const auto &dependency) { + return std::max( + accumulator, + schema_keyword_priority(dependency, vocabularies, walker) + 1); + })}; + return std::max(priority_from_dependencies, priority_from_order_dependencies); } auto sourcemeta::core::wrap(const sourcemeta::core::JSON::String &identifier) diff --git a/vendor/core/src/core/jsonschema/known_walker.cc b/vendor/core/src/core/jsonschema/known_walker.cc index 56e199f6..88e19770 100644 --- a/vendor/core/src/core/jsonschema/known_walker.cc +++ b/vendor/core/src/core/jsonschema/known_walker.cc @@ -11,10 +11,10 @@ using KeywordHandler = const SchemaWalkerResult &(*)(const Vocabularies &vocabularies); static const SchemaWalkerResult UNKNOWN_RESULT{ - SchemaKeywordType::Unknown, std::nullopt, {}, {}}; + SchemaKeywordType::Unknown, std::nullopt, {}, {}, {}}; static const SchemaWalkerResult UNKNOWN_WITH_REF_RESULT{ - SchemaKeywordType::Unknown, std::nullopt, {"$ref"}, {}}; + SchemaKeywordType::Unknown, std::nullopt, {"$ref"}, {}, {}}; auto has_draft3_to_7(const Vocabularies &vocabularies) -> bool { return vocabularies.contains(Known::JSON_Schema_Draft_7) || @@ -30,14 +30,21 @@ auto has_draft3_to_7(const Vocabularies &vocabularies) -> bool { #define RETURN_WITH_DEPENDENCIES(_vocabulary, _types, _strategy, ...) \ { \ static const SchemaWalkerResult result{ \ - SchemaKeywordType::_strategy, _vocabulary, {__VA_ARGS__}, _types}; \ + SchemaKeywordType::_strategy, _vocabulary, {__VA_ARGS__}, {}, _types}; \ + return result; \ + } + +#define RETURN_WITH_ORDER_DEPENDENCIES(_vocabulary, _types, _strategy, ...) \ + { \ + static const SchemaWalkerResult result{ \ + SchemaKeywordType::_strategy, _vocabulary, {}, {__VA_ARGS__}, _types}; \ return result; \ } #define RETURN(_vocabulary, _types, _strategy) \ { \ static const SchemaWalkerResult result{ \ - SchemaKeywordType::_strategy, _vocabulary, {}, _types}; \ + SchemaKeywordType::_strategy, _vocabulary, {}, {}, _types}; \ return result; \ } @@ -47,6 +54,13 @@ auto has_draft3_to_7(const Vocabularies &vocabularies) -> bool { RETURN_WITH_DEPENDENCIES(_vocabulary, _types, _strategy, __VA_ARGS__) \ } +#define CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(_vocabulary, _types, \ + _strategy, ...) \ + if (vocabularies.contains(_vocabulary)) { \ + RETURN_WITH_ORDER_DEPENDENCIES(_vocabulary, _types, _strategy, \ + __VA_ARGS__) \ + } + #define CHECK_VOCABULARY(_vocabulary, _types, _strategy) \ if (vocabularies.contains(_vocabulary)) { \ RETURN(_vocabulary, _types, _strategy) \ @@ -340,7 +354,7 @@ auto handle_properties(const Vocabularies &vocabularies) -> const SchemaWalkerResult & { if (vocabularies.contains(Known::JSON_Schema_2020_12_Applicator)) { if (vocabularies.contains(Known::JSON_Schema_2020_12_Validation)) { - RETURN_WITH_DEPENDENCIES( + RETURN_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2020_12_Applicator, make_set({JSON::Type::Object}), ApplicatorMembersTraversePropertyStatic, "required") } @@ -350,7 +364,7 @@ auto handle_properties(const Vocabularies &vocabularies) } if (vocabularies.contains(Known::JSON_Schema_2019_09_Applicator)) { if (vocabularies.contains(Known::JSON_Schema_2019_09_Validation)) { - RETURN_WITH_DEPENDENCIES( + RETURN_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2019_09_Applicator, make_set({JSON::Type::Object}), ApplicatorMembersTraversePropertyStatic, "required") } @@ -358,24 +372,60 @@ auto handle_properties(const Vocabularies &vocabularies) make_set({JSON::Type::Object}), ApplicatorMembersTraversePropertyStatic) } - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_7, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_7_Hyper, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_6, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_6_Hyper, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_4, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") - CHECK_VOCABULARY_WITH_DEPENDENCIES( - Known::JSON_Schema_Draft_4_Hyper, make_set({JSON::Type::Object}), - ApplicatorMembersTraversePropertyStatic, "$ref", "required") + if (vocabularies.contains(Known::JSON_Schema_Draft_7)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_7, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } + if (vocabularies.contains(Known::JSON_Schema_Draft_7_Hyper)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_7_Hyper, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } + if (vocabularies.contains(Known::JSON_Schema_Draft_6)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_6, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } + if (vocabularies.contains(Known::JSON_Schema_Draft_6_Hyper)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_6_Hyper, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } + if (vocabularies.contains(Known::JSON_Schema_Draft_4)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_4, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } + if (vocabularies.contains(Known::JSON_Schema_Draft_4_Hyper)) { + static const SchemaWalkerResult result{ + SchemaKeywordType::ApplicatorMembersTraversePropertyStatic, + Known::JSON_Schema_Draft_4_Hyper, + {"$ref"}, + {"required"}, + make_set({JSON::Type::Object})}; + return result; + } CHECK_VOCABULARY_WITH_DEPENDENCIES( Known::JSON_Schema_Draft_3, make_set({JSON::Type::Object}), ApplicatorMembersTraversePropertyStatic, "$ref") @@ -718,30 +768,30 @@ auto handle_type(const Vocabularies &vocabularies) -> const SchemaWalkerResult & { if (vocabularies.contains(Known::JSON_Schema_2020_12_Validation)) { if (vocabularies.contains(Known::JSON_Schema_2020_12_Applicator)) { - RETURN_WITH_DEPENDENCIES(Known::JSON_Schema_2020_12_Validation, {}, - Assertion, "properties") + RETURN_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_2020_12_Validation, {}, + Assertion, "properties") } RETURN(Known::JSON_Schema_2020_12_Validation, {}, Assertion) } if (vocabularies.contains(Known::JSON_Schema_2019_09_Validation)) { if (vocabularies.contains(Known::JSON_Schema_2019_09_Applicator)) { - RETURN_WITH_DEPENDENCIES(Known::JSON_Schema_2019_09_Validation, {}, - Assertion, "properties") + RETURN_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_2019_09_Validation, {}, + Assertion, "properties") } RETURN(Known::JSON_Schema_2019_09_Validation, {}, Assertion) } - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_7, {}, Assertion, - ("properties")) - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_7_Hyper, {}, - Assertion, "properties") - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_6, {}, Assertion, - ("properties")) - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_6_Hyper, {}, - Assertion, "properties") - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_4, {}, Assertion, - ("properties")) - CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_4_Hyper, {}, - Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_7, {}, + Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_7_Hyper, {}, + Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_6, {}, + Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_6_Hyper, {}, + Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_4, {}, + Assertion, "properties") + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES(Known::JSON_Schema_Draft_4_Hyper, {}, + Assertion, "properties") CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_3, {}, ApplicatorElementsInPlaceSome, "$ref") CHECK_VOCABULARY_WITH_DEPENDENCIES(Known::JSON_Schema_Draft_3_Hyper, {}, @@ -834,10 +884,10 @@ auto handle_multipleOf(const Vocabularies &vocabularies) auto handle_maximum(const Vocabularies &vocabularies) -> const SchemaWalkerResult & { - CHECK_VOCABULARY_WITH_DEPENDENCIES( + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2020_12_Validation, make_set({JSON::Type::Integer, JSON::Type::Real}), Assertion, "type") - CHECK_VOCABULARY_WITH_DEPENDENCIES( + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2019_09_Validation, make_set({JSON::Type::Integer, JSON::Type::Real}), Assertion, "type") CHECK_VOCABULARY_WITH_DEPENDENCIES( @@ -881,10 +931,10 @@ auto handle_maximum(const Vocabularies &vocabularies) auto handle_minimum(const Vocabularies &vocabularies) -> const SchemaWalkerResult & { - CHECK_VOCABULARY_WITH_DEPENDENCIES( + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2020_12_Validation, make_set({JSON::Type::Integer, JSON::Type::Real}), Assertion, "type") - CHECK_VOCABULARY_WITH_DEPENDENCIES( + CHECK_VOCABULARY_WITH_ORDER_DEPENDENCIES( Known::JSON_Schema_2019_09_Validation, make_set({JSON::Type::Integer, JSON::Type::Real}), Assertion, "type") CHECK_VOCABULARY_WITH_DEPENDENCIES( diff --git a/vendor/core/src/extension/alterschema/CMakeLists.txt b/vendor/core/src/extension/alterschema/CMakeLists.txt index 666c41ec..11ca6a60 100644 --- a/vendor/core/src/extension/alterschema/CMakeLists.txt +++ b/vendor/core/src/extension/alterschema/CMakeLists.txt @@ -28,6 +28,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema common/dependent_required_tautology.h common/draft_official_dialect_without_empty_fragment.h common/draft_ref_siblings.h + common/drop_allof_empty_schemas.h common/duplicate_allof_branches.h common/duplicate_anyof_branches.h common/duplicate_enum_values.h @@ -78,6 +79,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema linter/properties_default.h linter/property_names_default.h linter/property_names_type_default.h + linter/simple_properties_identifiers.h linter/title_description_equal.h linter/title_trailing_period.h linter/title_trim.h @@ -95,3 +97,5 @@ endif() target_link_libraries(sourcemeta_core_alterschema PUBLIC sourcemeta::core::jsonschema) +target_link_libraries(sourcemeta_core_alterschema PRIVATE + sourcemeta::core::regex) diff --git a/vendor/core/src/extension/alterschema/alterschema.cc b/vendor/core/src/extension/alterschema/alterschema.cc index 6ec23f70..4e87f439 100644 --- a/vendor/core/src/extension/alterschema/alterschema.cc +++ b/vendor/core/src/extension/alterschema/alterschema.cc @@ -1,4 +1,5 @@ #include +#include // For built-in rules #include // std::sort, std::unique @@ -54,6 +55,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "common/dependent_required_tautology.h" #include "common/draft_official_dialect_without_empty_fragment.h" #include "common/draft_ref_siblings.h" +#include "common/drop_allof_empty_schemas.h" #include "common/duplicate_allof_branches.h" #include "common/duplicate_anyof_branches.h" #include "common/duplicate_enum_values.h" @@ -105,6 +107,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "linter/properties_default.h" #include "linter/property_names_default.h" #include "linter/property_names_type_default.h" +#include "linter/simple_properties_identifiers.h" #include "linter/title_description_equal.h" #include "linter/title_trailing_period.h" #include "linter/title_trim.h" @@ -214,11 +217,13 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void { bundle.add(); bundle.add(); bundle.add(); + bundle.add(); } bundle.add(); bundle.add(); bundle.add(); + bundle.add(); } } // namespace sourcemeta::core diff --git a/vendor/core/src/extension/alterschema/canonicalizer/multiple_of_implicit.h b/vendor/core/src/extension/alterschema/canonicalizer/multiple_of_implicit.h index d466165f..32a2c864 100644 --- a/vendor/core/src/extension/alterschema/canonicalizer/multiple_of_implicit.h +++ b/vendor/core/src/extension/alterschema/canonicalizer/multiple_of_implicit.h @@ -21,8 +21,8 @@ class MultipleOfImplicit final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_4}) && schema.is_object() && schema.defines("type") && schema.at("type").is_string() && - (schema.at("type").to_string() == "integer" || - schema.at("type").to_string() == "number") && + // Applying this to numbers would be a semantic problem + schema.at("type").to_string() == "integer" && !schema.defines("multipleOf")); return true; } diff --git a/vendor/core/src/extension/alterschema/canonicalizer/type_union_implicit.h b/vendor/core/src/extension/alterschema/canonicalizer/type_union_implicit.h index 8cc046f4..11fb779d 100644 --- a/vendor/core/src/extension/alterschema/canonicalizer/type_union_implicit.h +++ b/vendor/core/src/extension/alterschema/canonicalizer/type_union_implicit.h @@ -11,9 +11,10 @@ class TypeUnionImplicit final : public SchemaTransformRule { const sourcemeta::core::Vocabularies &vocabularies, const sourcemeta::core::SchemaFrame &, const sourcemeta::core::SchemaFrame::Location &, - const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaWalker &walker, const sourcemeta::core::SchemaResolver &) const -> sourcemeta::core::SchemaTransformRule::Result override { + using namespace sourcemeta::core; ONLY_CONTINUE_IF(schema.is_object()); ONLY_CONTINUE_IF(vocabularies.contains_any( {Vocabularies::Known::JSON_Schema_2020_12_Validation, @@ -26,66 +27,49 @@ class TypeUnionImplicit final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_1, Vocabularies::Known::JSON_Schema_Draft_0})); ONLY_CONTINUE_IF(!schema.defines("type")); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_2020_12_Core) || - !schema.defines_any({"$ref", "$dynamicRef"})); - ONLY_CONTINUE_IF(!vocabularies.contains( - Vocabularies::Known::JSON_Schema_2020_12_Applicator) || - !schema.defines_any({"anyOf", "oneOf", "allOf", "if", - "then", "else", "not"})); - ONLY_CONTINUE_IF(!vocabularies.contains( - Vocabularies::Known::JSON_Schema_2020_12_Validation) || - !schema.defines_any({"enum", "const"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_2019_09_Core) || - !schema.defines_any({"$ref", "$recursiveRef"})); - ONLY_CONTINUE_IF(!vocabularies.contains( - Vocabularies::Known::JSON_Schema_2019_09_Applicator) || - !schema.defines_any({"anyOf", "oneOf", "allOf", "if", - "then", "else", "not"})); - ONLY_CONTINUE_IF(!vocabularies.contains( - Vocabularies::Known::JSON_Schema_2019_09_Validation) || - !schema.defines_any({"enum", "const"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_7) || - !schema.defines_any({"$ref", "enum", "const", "anyOf", "oneOf", "allOf", - "if", "then", "else", "not"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_6) || - !schema.defines_any( - {"$ref", "enum", "const", "anyOf", "oneOf", "allOf", "not"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_4) || - !schema.defines_any( - {"$ref", "enum", "anyOf", "oneOf", "allOf", "not"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_3) || - !schema.defines_any({"$ref", "enum", "disallow", "extends"})) - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_2) || - !schema.defines_any({"enum", "disallow", "extends"})); - ONLY_CONTINUE_IF( - !vocabularies.contains(Vocabularies::Known::JSON_Schema_Draft_1) || - !schema.defines_any({"enum", "disallow", "extends"})); - ONLY_CONTINUE_IF(!vocabularies.contains( - Vocabularies::Known::JSON_Schema_Draft_0_Hyper) || - !schema.defines_any({"enum", "disallow", "extends"})); + ONLY_CONTINUE_IF(!schema.defines("enum")); + ONLY_CONTINUE_IF(!vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Validation, + Vocabularies::Known::JSON_Schema_2019_09_Validation, + Vocabularies::Known::JSON_Schema_Draft_7, + Vocabularies::Known::JSON_Schema_Draft_6}) || + !schema.defines("const")); + + for (const auto &entry : schema.as_object()) { + const auto &keyword_type{walker(entry.first, vocabularies).type}; + + // References point to other schemas that may have type constraints + ONLY_CONTINUE_IF(keyword_type != SchemaKeywordType::Reference); + + // Logical in-place applicators apply without affecting the instance + // location, meaning they impose constraints on the same instance. Adding + // an implicit type union alongside these would create redundant branches + // that need complex simplification + ONLY_CONTINUE_IF( + keyword_type != SchemaKeywordType::ApplicatorValueOrElementsInPlace && + keyword_type != SchemaKeywordType::ApplicatorMembersInPlaceSome && + keyword_type != SchemaKeywordType::ApplicatorElementsInPlace && + keyword_type != SchemaKeywordType::ApplicatorElementsInPlaceSome && + keyword_type != + SchemaKeywordType::ApplicatorElementsInPlaceSomeNegate && + keyword_type != SchemaKeywordType::ApplicatorValueInPlaceMaybe && + keyword_type != SchemaKeywordType::ApplicatorValueInPlaceNegate); + } + return true; } auto transform(JSON &schema, const Result &) const -> void override { auto types{sourcemeta::core::JSON::make_array()}; - // All possible JSON Schema types - // See - // https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.1 types.push_back(sourcemeta::core::JSON{"null"}); types.push_back(sourcemeta::core::JSON{"boolean"}); types.push_back(sourcemeta::core::JSON{"object"}); types.push_back(sourcemeta::core::JSON{"array"}); types.push_back(sourcemeta::core::JSON{"string"}); + + // Note we don't add `integer`, as its covered by `number` types.push_back(sourcemeta::core::JSON{"number"}); - types.push_back(sourcemeta::core::JSON{"integer"}); schema.assign("type", std::move(types)); } diff --git a/vendor/core/src/extension/alterschema/common/drop_allof_empty_schemas.h b/vendor/core/src/extension/alterschema/common/drop_allof_empty_schemas.h new file mode 100644 index 00000000..31abc14e --- /dev/null +++ b/vendor/core/src/extension/alterschema/common/drop_allof_empty_schemas.h @@ -0,0 +1,43 @@ +class DropAllOfEmptySchemas final : public SchemaTransformRule { +public: + DropAllOfEmptySchemas() + : SchemaTransformRule{"drop_allof_empty_schemas", + "Empty schemas in `allOf` are redundant and can be " + "removed"} {}; + + [[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(vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Applicator, + Vocabularies::Known::JSON_Schema_2019_09_Applicator, + Vocabularies::Known::JSON_Schema_Draft_7, + Vocabularies::Known::JSON_Schema_Draft_6, + Vocabularies::Known::JSON_Schema_Draft_4})); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines("allOf") && + schema.at("allOf").is_array() && + !schema.at("allOf").empty()); + ONLY_CONTINUE_IF( + std::ranges::any_of(schema.at("allOf").as_array(), is_empty_schema)); + return APPLIES_TO_KEYWORDS("allOf"); + } + + auto transform(JSON &schema, const Result &) const -> void override { + auto new_allof{JSON::make_array()}; + for (const auto &entry : schema.at("allOf").as_array()) { + if (!is_empty_schema(entry)) { + new_allof.push_back(entry); + } + } + + if (new_allof.empty()) { + schema.erase("allOf"); + } else { + // Re-assign instead of the deleting in place to invalid memory addresses + // and avoid confusing the transformer + schema.assign("allOf", std::move(new_allof)); + } + } +}; diff --git a/vendor/core/src/extension/alterschema/common/non_applicable_enum_validation_keywords.h b/vendor/core/src/extension/alterschema/common/non_applicable_enum_validation_keywords.h index 918ebf7e..6de098fe 100644 --- a/vendor/core/src/extension/alterschema/common/non_applicable_enum_validation_keywords.h +++ b/vendor/core/src/extension/alterschema/common/non_applicable_enum_validation_keywords.h @@ -27,7 +27,7 @@ class NonApplicableEnumValidationKeywords final : public SchemaTransformRule { Vocabularies::Known::JSON_Schema_Draft_1, Vocabularies::Known::JSON_Schema_Draft_1_Hyper}) && schema.is_object() && schema.defines("enum") && - schema.at("enum").is_array()); + schema.at("enum").is_array() && !schema.defines("type")); sourcemeta::core::JSON::TypeSet enum_types; for (const auto &value : schema.at("enum").as_array()) { diff --git a/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h b/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h index bfb77e0f..6f9b668e 100644 --- a/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h +++ b/vendor/core/src/extension/alterschema/common/unnecessary_allof_wrapper.h @@ -59,24 +59,24 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { for (const auto &keyword_entry : entry.as_object()) { const auto &keyword{keyword_entry.first}; + const auto &metadata{walker(keyword, vocabularies)}; + if (elevated.contains(keyword) || - dependency_blocked.contains(keyword) || (schema.defines(keyword) && schema.at(keyword) != keyword_entry.second)) { continue; } - const auto &metadata{walker(keyword, vocabularies)}; + if (dependency_blocked.contains(keyword)) { + continue; + } + if (metadata.instances.any() && parent_types.any() && (metadata.instances & parent_types).none()) { continue; } - // TODO: Fix the fact that the walker declares dependencies for - // evaluation order convenience, not semantic dependencies (i.e. many - // assertion keywords depend on `type`) - if (metadata.type != SchemaKeywordType::Assertion && - std::ranges::any_of( + if (std::ranges::any_of( metadata.dependencies, [&](const auto &dependency) { return !entry.defines(std::string{dependency}) && (schema.defines(std::string{dependency}) || @@ -105,16 +105,10 @@ class UnnecessaryAllOfWrapper final : public SchemaTransformRule { assert(location.size() == 3); const auto allof_index{location.at(1).to_index()}; const auto &keyword{location.at(2).to_property()}; - // TODO: Ideally here we could move instead of copying and then erasing schema.try_assign_before( keyword, schema.at("allOf").at(allof_index).at(keyword), "allOf"); schema.at("allOf").at(allof_index).erase(keyword); } - - schema.at("allOf").erase_if(is_empty_schema); - if (schema.at("allOf").empty()) { - schema.erase("allOf"); - } } private: diff --git a/vendor/core/src/extension/alterschema/linter/simple_properties_identifiers.h b/vendor/core/src/extension/alterschema/linter/simple_properties_identifiers.h new file mode 100644 index 00000000..e4e36519 --- /dev/null +++ b/vendor/core/src/extension/alterschema/linter/simple_properties_identifiers.h @@ -0,0 +1,65 @@ +class SimplePropertiesIdentifiers final : public SchemaTransformRule { +public: + SimplePropertiesIdentifiers() + // Inspired by + // https://json-structure.github.io/core/draft-vasters-json-structure-core.html#section-3.6 + : SchemaTransformRule{ + "simple_properties_identifiers", + "Set `properties` to identifier names that can be easily mapped to " + "programming languages (matching [A-Za-z_][A-Za-z0-9_]*)"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &root, + const sourcemeta::core::Vocabularies &vocabularies, + const sourcemeta::core::SchemaFrame &frame, + const sourcemeta::core::SchemaFrame::Location &location, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + ONLY_CONTINUE_IF(vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Applicator, + Vocabularies::Known::JSON_Schema_2019_09_Applicator, + Vocabularies::Known::JSON_Schema_Draft_7, + Vocabularies::Known::JSON_Schema_Draft_6, + Vocabularies::Known::JSON_Schema_Draft_4, + Vocabularies::Known::JSON_Schema_Draft_3, + Vocabularies::Known::JSON_Schema_Draft_2, + Vocabularies::Known::JSON_Schema_Draft_2_Hyper, + Vocabularies::Known::JSON_Schema_Draft_1, + Vocabularies::Known::JSON_Schema_Draft_1_Hyper})); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines("properties") && + schema.at("properties").is_object() && + !schema.at("properties").empty()); + + if (vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Core, + Vocabularies::Known::JSON_Schema_2019_09_Core})) { + // Skip meta-schemas with `$vocabulary` (2019-09+) + // We check the current schema resource (not root) to handle bundled + // schemas + const auto base_location{frame.traverse(location.base)}; + if (base_location.has_value()) { + const auto &resource{get(root, base_location->get().pointer)}; + ONLY_CONTINUE_IF(!resource.is_object() || + !resource.defines("$vocabulary")); + } + } else { + // Skip pre-vocabulary meta-schemas + ONLY_CONTINUE_IF(location.base != location.dialect && + (location.base + "#") != location.dialect); + } + + std::vector offenders; + for (const auto &entry : schema.at("properties").as_object()) { + static const Regex IDENTIFIER_PATTERN{ + to_regex("^[A-Za-z_][A-Za-z0-9_]*$").value()}; + if (!matches(IDENTIFIER_PATTERN, entry.first)) { + offenders.push_back(Pointer{"properties", entry.first}); + } + } + + ONLY_CONTINUE_IF(!offenders.empty()); + return APPLIES_TO_POINTERS(std::move(offenders)); + } +};