From 3a0bba296ea49c71ef349164e690e4d177c700b9 Mon Sep 17 00:00:00 2001 From: Vaibhav mittal Date: Fri, 10 Apr 2026 13:40:54 +0000 Subject: [PATCH 1/3] feat(alterschema): add format_type_mismatch rule - port rule from core to Blaze alterschema module - align with Blaze architecture and conventions - add full test coverage across supported scenarios Signed-off-by: Vaibhav mittal --- src/alterschema/CMakeLists.txt | 227 +++++++++--------- src/alterschema/alterschema.cc | 2 + src/alterschema/linter/format_type_mismatch.h | 33 +++ .../alterschema_lint_2019_09_test.cc | 196 +++++++++++++++ .../alterschema_lint_2020_12_test.cc | 196 +++++++++++++++ .../alterschema_lint_draft3_test.cc | 191 +++++++++++++++ .../alterschema_lint_draft4_test.cc | 191 +++++++++++++++ .../alterschema_lint_draft6_test.cc | 200 +++++++++++++++ .../alterschema_lint_draft7_test.cc | 200 +++++++++++++++ 9 files changed, 1323 insertions(+), 113 deletions(-) create mode 100644 src/alterschema/linter/format_type_mismatch.h diff --git a/src/alterschema/CMakeLists.txt b/src/alterschema/CMakeLists.txt index 34376c7d4..6fccee440 100644 --- a/src/alterschema/CMakeLists.txt +++ b/src/alterschema/CMakeLists.txt @@ -1,125 +1,126 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema - FOLDER "Blaze/AlterSchema" - PRIVATE_HEADERS error.h - SOURCES alterschema.cc schema_rule.cc - # Canonicalizer - canonicalizer/const_as_enum.h - canonicalizer/exclusive_maximum_integer_to_maximum.h - canonicalizer/exclusive_minimum_integer_to_minimum.h - canonicalizer/items_implicit.h - canonicalizer/max_contains_covered_by_max_items.h - canonicalizer/min_items_given_min_contains.h - canonicalizer/min_items_implicit.h - canonicalizer/min_length_implicit.h - canonicalizer/min_properties_covered_by_required.h - canonicalizer/min_properties_implicit.h - canonicalizer/multiple_of_implicit.h - canonicalizer/properties_implicit.h - canonicalizer/type_array_to_any_of.h - canonicalizer/type_boolean_as_enum.h - canonicalizer/type_null_as_enum.h - canonicalizer/type_union_implicit.h + FOLDER "Blaze/AlterSchema" + PRIVATE_HEADERS error.h + SOURCES alterschema.cc schema_rule.cc + # Canonicalizer + canonicalizer/const_as_enum.h + canonicalizer/exclusive_maximum_integer_to_maximum.h + canonicalizer/exclusive_minimum_integer_to_minimum.h + canonicalizer/items_implicit.h + canonicalizer/max_contains_covered_by_max_items.h + canonicalizer/min_items_given_min_contains.h + canonicalizer/min_items_implicit.h + canonicalizer/min_length_implicit.h + canonicalizer/min_properties_covered_by_required.h + canonicalizer/min_properties_implicit.h + canonicalizer/multiple_of_implicit.h + canonicalizer/properties_implicit.h + canonicalizer/type_array_to_any_of.h + canonicalizer/type_boolean_as_enum.h + canonicalizer/type_null_as_enum.h + canonicalizer/type_union_implicit.h - # Common - common/allof_false_simplify.h - common/anyof_false_simplify.h - common/anyof_remove_false_schemas.h - common/anyof_true_simplify.h - common/const_in_enum.h - common/const_with_type.h - common/orphan_definitions.h - common/content_media_type_without_encoding.h - common/content_schema_without_media_type.h - common/dependencies_property_tautology.h - common/dependent_required_tautology.h - common/draft_official_dialect_with_https.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 - common/duplicate_required_values.h - common/empty_object_as_true.h - common/else_empty.h - common/else_without_if.h - common/enum_with_type.h - common/equal_numeric_bounds_to_enum.h - common/exclusive_maximum_number_and_maximum.h - common/exclusive_minimum_number_and_minimum.h - common/if_without_then_else.h - common/ignored_metaschema.h - common/max_contains_without_contains.h - common/maximum_real_for_integer.h - common/min_contains_without_contains.h - common/minimum_real_for_integer.h - common/modern_official_dialect_with_empty_fragment.h - common/modern_official_dialect_with_http.h - common/non_applicable_additional_items.h - common/non_applicable_enum_validation_keywords.h - common/non_applicable_type_specific_keywords.h - common/not_false.h - common/oneof_false_simplify.h - common/oneof_to_anyof_disjoint_types.h - common/required_properties_in_properties.h - common/single_type_array.h - common/then_empty.h - common/then_without_if.h - common/unknown_keywords_prefix.h - common/unknown_local_ref.h - common/unsatisfiable_drop_validation.h - common/unnecessary_allof_ref_wrapper_draft.h - common/unnecessary_allof_ref_wrapper_modern.h - common/unnecessary_allof_wrapper.h - common/unsatisfiable_in_place_applicator_type.h + # Common + common/allof_false_simplify.h + common/anyof_false_simplify.h + common/anyof_remove_false_schemas.h + common/anyof_true_simplify.h + common/const_in_enum.h + common/const_with_type.h + common/orphan_definitions.h + common/content_media_type_without_encoding.h + common/content_schema_without_media_type.h + common/dependencies_property_tautology.h + common/dependent_required_tautology.h + common/draft_official_dialect_with_https.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 + common/duplicate_required_values.h + common/empty_object_as_true.h + common/else_empty.h + common/else_without_if.h + common/enum_with_type.h + common/equal_numeric_bounds_to_enum.h + common/exclusive_maximum_number_and_maximum.h + common/exclusive_minimum_number_and_minimum.h + common/if_without_then_else.h + common/ignored_metaschema.h + common/max_contains_without_contains.h + common/maximum_real_for_integer.h + common/min_contains_without_contains.h + common/minimum_real_for_integer.h + common/modern_official_dialect_with_empty_fragment.h + common/modern_official_dialect_with_http.h + common/non_applicable_additional_items.h + common/non_applicable_enum_validation_keywords.h + common/non_applicable_type_specific_keywords.h + common/not_false.h + common/oneof_false_simplify.h + common/oneof_to_anyof_disjoint_types.h + common/required_properties_in_properties.h + common/single_type_array.h + common/then_empty.h + common/then_without_if.h + common/unknown_keywords_prefix.h + common/unknown_local_ref.h + common/unsatisfiable_drop_validation.h + common/unnecessary_allof_ref_wrapper_draft.h + common/unnecessary_allof_ref_wrapper_modern.h + common/unnecessary_allof_wrapper.h + common/unsatisfiable_in_place_applicator_type.h - # Linter - linter/comment_trim.h - linter/const_not_in_enum.h - linter/content_schema_default.h - linter/definitions_to_defs.h - linter/dependencies_default.h - linter/dependent_required_default.h - linter/description_trailing_period.h - linter/description_trim.h - linter/duplicate_examples.h - linter/enum_to_const.h - linter/equal_numeric_bounds_to_const.h - linter/forbid_empty_enum.h - linter/incoherent_min_max_contains.h - linter/invalid_external_ref.h - linter/items_array_default.h - linter/items_schema_default.h - linter/multiple_of_default.h - linter/pattern_properties_default.h - 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 - linter/top_level_description.h - linter/top_level_examples.h - linter/top_level_title.h - linter/unevaluated_items_default.h - linter/unevaluated_properties_default.h - linter/unsatisfiable_max_contains.h - linter/unsatisfiable_min_properties.h - linter/valid_default.h - linter/valid_examples.h) + # Linter + linter/comment_trim.h + linter/const_not_in_enum.h + linter/content_schema_default.h + linter/definitions_to_defs.h + linter/dependencies_default.h + linter/dependent_required_default.h + linter/description_trailing_period.h + linter/description_trim.h + linter/duplicate_examples.h + linter/enum_to_const.h + linter/equal_numeric_bounds_to_const.h + linter/forbid_empty_enum.h + linter/format_type_mismatch.h + linter/incoherent_min_max_contains.h + linter/invalid_external_ref.h + linter/items_array_default.h + linter/items_schema_default.h + linter/multiple_of_default.h + linter/pattern_properties_default.h + 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 + linter/top_level_description.h + linter/top_level_examples.h + linter/top_level_title.h + linter/unevaluated_items_default.h + linter/unevaluated_properties_default.h + linter/unsatisfiable_max_contains.h + linter/unsatisfiable_min_properties.h + linter/valid_default.h + linter/valid_examples.h) if(BLAZE_INSTALL) - sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME alterschema) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME alterschema) endif() target_link_libraries(sourcemeta_blaze_alterschema PUBLIC - sourcemeta::core::jsonschema) + sourcemeta::core::jsonschema) target_link_libraries(sourcemeta_blaze_alterschema PUBLIC - sourcemeta::blaze::compiler) + sourcemeta::blaze::compiler) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::blaze::evaluator) + sourcemeta::blaze::evaluator) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::blaze::output) + sourcemeta::blaze::output) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::core::regex) + sourcemeta::core::regex) diff --git a/src/alterschema/alterschema.cc b/src/alterschema/alterschema.cc index 2cd0f3bf0..205d37a10 100644 --- a/src/alterschema/alterschema.cc +++ b/src/alterschema/alterschema.cc @@ -124,6 +124,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "linter/enum_to_const.h" #include "linter/equal_numeric_bounds_to_const.h" #include "linter/forbid_empty_enum.h" +#include "linter/format_type_mismatch.h" #include "linter/incoherent_min_max_contains.h" #include "linter/invalid_external_ref.h" #include "linter/items_array_default.h" @@ -248,6 +249,7 @@ auto add(sourcemeta::core::SchemaTransformer &bundle, bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/src/alterschema/linter/format_type_mismatch.h b/src/alterschema/linter/format_type_mismatch.h new file mode 100644 index 000000000..7ce4d76ea --- /dev/null +++ b/src/alterschema/linter/format_type_mismatch.h @@ -0,0 +1,33 @@ +class FormatTypeMismatch final : public SchemaTransformRule { +public: + using mutates = std::false_type; + using reframe_after_transform = std::false_type; + FormatTypeMismatch() + : SchemaTransformRule{ + "format_type_mismatch", + "The `format` keyword validates string instances but `type` " + "is not `string`"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &, + const sourcemeta::core::Vocabularies &vocabularies, + const sourcemeta::core::SchemaFrame &, + const sourcemeta::core::SchemaFrame::Location &, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + using Known = Vocabularies::Known; + ONLY_CONTINUE_IF( + vocabularies.contains_any( + {Known::JSON_Schema_2020_12_Validation, + Known::JSON_Schema_2019_09_Validation, Known::JSON_Schema_Draft_7, + Known::JSON_Schema_Draft_6, Known::JSON_Schema_Draft_4, + Known::JSON_Schema_Draft_3}) && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() != "string" && schema.defines("format") && + schema.at("format").is_string()); + return APPLIES_TO_KEYWORDS("format", "type"); + } +}; \ No newline at end of file diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index b877038f9..75efe3bf6 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -3534,6 +3534,202 @@ TEST(AlterSchema_lint_2019_09, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_2019_09, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "$ref": "#/$defs/foo", + "$defs": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "https://example.com" ], + "$ref": "#/$defs/A/properties/bar", + "$defs": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_2019_09, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2019-09/schema", diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index 99e1f2d67..b159b3359 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -3963,6 +3963,202 @@ TEST(AlterSchema_lint_2020_12, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_2020_12, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "$ref": "#/$defs/foo", + "$defs": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "https://example.com" ], + "$ref": "#/$defs/A/properties/bar", + "$defs": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_2020_12, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index 02dd03541..296aaf466 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -1082,6 +1082,197 @@ TEST(AlterSchema_lint_draft3, non_applicable_type_specific_keywords_1) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft3, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "additionalProperties": { + "type": "integer", + "format": "uri" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "additionalProperties": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft3, unknown_keywords_prefix_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-03/schema#", diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 41763b726..6f56a2dac 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -1753,6 +1753,197 @@ TEST(AlterSchema_lint_draft4, non_applicable_type_specific_keywords_1) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft4, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft4, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-04/schema#", diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index cc0e642ce..50004b832 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -2022,6 +2022,206 @@ TEST(AlterSchema_lint_draft6, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft6, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft6, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-06/schema#", diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index 78f538833..90c8c6057 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -2370,6 +2370,206 @@ TEST(AlterSchema_lint_draft7, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft7, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft7, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-07/schema#", From 3a0777df675d50038dfcfdb3ee3bf50cfb7e2aa9 Mon Sep 17 00:00:00 2001 From: Vaibhav mittal Date: Fri, 10 Apr 2026 14:06:03 +0000 Subject: [PATCH 2/3] fix(alterschema): align format_type_mismatch with Blaze linter semantics - use walker metadata for keyword applicability - target the format keyword location only - decouple tests from unrelated trace ordering - restore minimal CMake diff Signed-off-by: Vaibhav mittal --- src/alterschema/CMakeLists.txt | 228 +++++++++--------- src/alterschema/linter/format_type_mismatch.h | 28 +-- .../alterschema_lint_2019_09_test.cc | 59 ++--- .../alterschema_lint_2020_12_test.cc | 59 ++--- .../alterschema_lint_draft3_test.cc | 71 ++---- .../alterschema_lint_draft4_test.cc | 59 ++--- .../alterschema_lint_draft6_test.cc | 59 ++--- .../alterschema_lint_draft7_test.cc | 59 ++--- test/alterschema/alterschema_test_utils.h | 21 ++ 9 files changed, 271 insertions(+), 372 deletions(-) diff --git a/src/alterschema/CMakeLists.txt b/src/alterschema/CMakeLists.txt index 6fccee440..f8381a640 100644 --- a/src/alterschema/CMakeLists.txt +++ b/src/alterschema/CMakeLists.txt @@ -1,126 +1,126 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema - FOLDER "Blaze/AlterSchema" - PRIVATE_HEADERS error.h - SOURCES alterschema.cc schema_rule.cc - # Canonicalizer - canonicalizer/const_as_enum.h - canonicalizer/exclusive_maximum_integer_to_maximum.h - canonicalizer/exclusive_minimum_integer_to_minimum.h - canonicalizer/items_implicit.h - canonicalizer/max_contains_covered_by_max_items.h - canonicalizer/min_items_given_min_contains.h - canonicalizer/min_items_implicit.h - canonicalizer/min_length_implicit.h - canonicalizer/min_properties_covered_by_required.h - canonicalizer/min_properties_implicit.h - canonicalizer/multiple_of_implicit.h - canonicalizer/properties_implicit.h - canonicalizer/type_array_to_any_of.h - canonicalizer/type_boolean_as_enum.h - canonicalizer/type_null_as_enum.h - canonicalizer/type_union_implicit.h + FOLDER "Blaze/AlterSchema" + PRIVATE_HEADERS error.h + SOURCES alterschema.cc schema_rule.cc + # Canonicalizer + canonicalizer/const_as_enum.h + canonicalizer/exclusive_maximum_integer_to_maximum.h + canonicalizer/exclusive_minimum_integer_to_minimum.h + canonicalizer/items_implicit.h + canonicalizer/max_contains_covered_by_max_items.h + canonicalizer/min_items_given_min_contains.h + canonicalizer/min_items_implicit.h + canonicalizer/min_length_implicit.h + canonicalizer/min_properties_covered_by_required.h + canonicalizer/min_properties_implicit.h + canonicalizer/multiple_of_implicit.h + canonicalizer/properties_implicit.h + canonicalizer/type_array_to_any_of.h + canonicalizer/type_boolean_as_enum.h + canonicalizer/type_null_as_enum.h + canonicalizer/type_union_implicit.h - # Common - common/allof_false_simplify.h - common/anyof_false_simplify.h - common/anyof_remove_false_schemas.h - common/anyof_true_simplify.h - common/const_in_enum.h - common/const_with_type.h - common/orphan_definitions.h - common/content_media_type_without_encoding.h - common/content_schema_without_media_type.h - common/dependencies_property_tautology.h - common/dependent_required_tautology.h - common/draft_official_dialect_with_https.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 - common/duplicate_required_values.h - common/empty_object_as_true.h - common/else_empty.h - common/else_without_if.h - common/enum_with_type.h - common/equal_numeric_bounds_to_enum.h - common/exclusive_maximum_number_and_maximum.h - common/exclusive_minimum_number_and_minimum.h - common/if_without_then_else.h - common/ignored_metaschema.h - common/max_contains_without_contains.h - common/maximum_real_for_integer.h - common/min_contains_without_contains.h - common/minimum_real_for_integer.h - common/modern_official_dialect_with_empty_fragment.h - common/modern_official_dialect_with_http.h - common/non_applicable_additional_items.h - common/non_applicable_enum_validation_keywords.h - common/non_applicable_type_specific_keywords.h - common/not_false.h - common/oneof_false_simplify.h - common/oneof_to_anyof_disjoint_types.h - common/required_properties_in_properties.h - common/single_type_array.h - common/then_empty.h - common/then_without_if.h - common/unknown_keywords_prefix.h - common/unknown_local_ref.h - common/unsatisfiable_drop_validation.h - common/unnecessary_allof_ref_wrapper_draft.h - common/unnecessary_allof_ref_wrapper_modern.h - common/unnecessary_allof_wrapper.h - common/unsatisfiable_in_place_applicator_type.h + # Common + common/allof_false_simplify.h + common/anyof_false_simplify.h + common/anyof_remove_false_schemas.h + common/anyof_true_simplify.h + common/const_in_enum.h + common/const_with_type.h + common/orphan_definitions.h + common/content_media_type_without_encoding.h + common/content_schema_without_media_type.h + common/dependencies_property_tautology.h + common/dependent_required_tautology.h + common/draft_official_dialect_with_https.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 + common/duplicate_required_values.h + common/empty_object_as_true.h + common/else_empty.h + common/else_without_if.h + common/enum_with_type.h + common/equal_numeric_bounds_to_enum.h + common/exclusive_maximum_number_and_maximum.h + common/exclusive_minimum_number_and_minimum.h + common/if_without_then_else.h + common/ignored_metaschema.h + common/max_contains_without_contains.h + common/maximum_real_for_integer.h + common/min_contains_without_contains.h + common/minimum_real_for_integer.h + common/modern_official_dialect_with_empty_fragment.h + common/modern_official_dialect_with_http.h + common/non_applicable_additional_items.h + common/non_applicable_enum_validation_keywords.h + common/non_applicable_type_specific_keywords.h + common/not_false.h + common/oneof_false_simplify.h + common/oneof_to_anyof_disjoint_types.h + common/required_properties_in_properties.h + common/single_type_array.h + common/then_empty.h + common/then_without_if.h + common/unknown_keywords_prefix.h + common/unknown_local_ref.h + common/unsatisfiable_drop_validation.h + common/unnecessary_allof_ref_wrapper_draft.h + common/unnecessary_allof_ref_wrapper_modern.h + common/unnecessary_allof_wrapper.h + common/unsatisfiable_in_place_applicator_type.h - # Linter - linter/comment_trim.h - linter/const_not_in_enum.h - linter/content_schema_default.h - linter/definitions_to_defs.h - linter/dependencies_default.h - linter/dependent_required_default.h - linter/description_trailing_period.h - linter/description_trim.h - linter/duplicate_examples.h - linter/enum_to_const.h - linter/equal_numeric_bounds_to_const.h - linter/forbid_empty_enum.h - linter/format_type_mismatch.h - linter/incoherent_min_max_contains.h - linter/invalid_external_ref.h - linter/items_array_default.h - linter/items_schema_default.h - linter/multiple_of_default.h - linter/pattern_properties_default.h - 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 - linter/top_level_description.h - linter/top_level_examples.h - linter/top_level_title.h - linter/unevaluated_items_default.h - linter/unevaluated_properties_default.h - linter/unsatisfiable_max_contains.h - linter/unsatisfiable_min_properties.h - linter/valid_default.h - linter/valid_examples.h) + # Linter + linter/comment_trim.h + linter/const_not_in_enum.h + linter/content_schema_default.h + linter/definitions_to_defs.h + linter/dependencies_default.h + linter/dependent_required_default.h + linter/description_trailing_period.h + linter/description_trim.h + linter/duplicate_examples.h + linter/enum_to_const.h + linter/equal_numeric_bounds_to_const.h + linter/forbid_empty_enum.h + linter/format_type_mismatch.h + linter/incoherent_min_max_contains.h + linter/invalid_external_ref.h + linter/items_array_default.h + linter/items_schema_default.h + linter/multiple_of_default.h + linter/pattern_properties_default.h + 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 + linter/top_level_description.h + linter/top_level_examples.h + linter/top_level_title.h + linter/unevaluated_items_default.h + linter/unevaluated_properties_default.h + linter/unsatisfiable_max_contains.h + linter/unsatisfiable_min_properties.h + linter/valid_default.h + linter/valid_examples.h) if(BLAZE_INSTALL) - sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME alterschema) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME alterschema) endif() target_link_libraries(sourcemeta_blaze_alterschema PUBLIC - sourcemeta::core::jsonschema) + sourcemeta::core::jsonschema) target_link_libraries(sourcemeta_blaze_alterschema PUBLIC - sourcemeta::blaze::compiler) + sourcemeta::blaze::compiler) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::blaze::evaluator) + sourcemeta::blaze::evaluator) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::blaze::output) + sourcemeta::blaze::output) target_link_libraries(sourcemeta_blaze_alterschema PRIVATE - sourcemeta::core::regex) + sourcemeta::core::regex) diff --git a/src/alterschema/linter/format_type_mismatch.h b/src/alterschema/linter/format_type_mismatch.h index 7ce4d76ea..3020ac374 100644 --- a/src/alterschema/linter/format_type_mismatch.h +++ b/src/alterschema/linter/format_type_mismatch.h @@ -14,20 +14,20 @@ class FormatTypeMismatch 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 Known = Vocabularies::Known; - ONLY_CONTINUE_IF( - vocabularies.contains_any( - {Known::JSON_Schema_2020_12_Validation, - Known::JSON_Schema_2019_09_Validation, Known::JSON_Schema_Draft_7, - Known::JSON_Schema_Draft_6, Known::JSON_Schema_Draft_4, - Known::JSON_Schema_Draft_3}) && - schema.is_object() && schema.defines("type") && - schema.at("type").is_string() && - schema.at("type").to_string() != "string" && schema.defines("format") && - schema.at("format").is_string()); - return APPLIES_TO_KEYWORDS("format", "type"); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines("format") && + schema.at("format").is_string() && + schema.defines("type") && schema.at("type").is_string()); + + const auto current_types{parse_schema_type(schema.at("type"))}; + ONLY_CONTINUE_IF(current_types.any()); + + const auto &metadata{walker("format", vocabularies)}; + ONLY_CONTINUE_IF(metadata.instances.any() && + (metadata.instances & current_types).none()); + + return APPLIES_TO_KEYWORDS("format"); } -}; \ No newline at end of file +}; diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index 75efe3bf6..a579417a6 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -3547,15 +3547,10 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_2) { @@ -3606,16 +3601,10 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_5) { @@ -3649,16 +3638,10 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { @@ -3679,11 +3662,10 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { @@ -3707,11 +3689,10 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index b159b3359..1fb928fc5 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -3976,15 +3976,10 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_2) { @@ -4035,16 +4030,10 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_5) { @@ -4078,16 +4067,10 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { @@ -4108,11 +4091,10 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { @@ -4136,11 +4118,10 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index 296aaf466..a33fc02e3 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -1094,15 +1094,10 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_2) { @@ -1150,16 +1145,10 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_5) { @@ -1191,16 +1180,10 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { @@ -1217,16 +1200,11 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS( + traces, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but `type` is not " + "`string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { @@ -1246,16 +1224,11 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS( + traces, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but `type` is not " + "`string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 6f56a2dac..70d8c45d5 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -1765,15 +1765,10 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_2) { @@ -1821,16 +1816,10 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_5) { @@ -1862,16 +1851,10 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { @@ -1893,11 +1876,10 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { @@ -1922,11 +1904,10 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index 50004b832..ef41a1ab0 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -2035,15 +2035,10 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_2) { @@ -2094,16 +2089,10 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_5) { @@ -2137,16 +2126,10 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { @@ -2169,11 +2152,10 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { @@ -2199,11 +2181,10 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index 90c8c6057..3068d645b 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -2383,15 +2383,10 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_2) { @@ -2442,16 +2437,10 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/properties/foo", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_5) { @@ -2485,16 +2474,10 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 2); - EXPECT_LINT_TRACE(traces, 0, "/items", - "non_applicable_type_specific_keywords", - "Avoid keywords that don't apply to the type or " - "types that the current subschema expects", - true); - EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { @@ -2517,11 +2500,10 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { @@ -2547,11 +2529,10 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_EQ(traces.size(), 1); - EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances but " - "`type` is not `string`", - false); + EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances " + "but `type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_test_utils.h b/test/alterschema/alterschema_test_utils.h index 1781af9c9..fc8936b20 100644 --- a/test/alterschema/alterschema_test_utils.h +++ b/test/alterschema/alterschema_test_utils.h @@ -4,9 +4,26 @@ #include #include +#include +#include #include #include +template +static auto has_lint_trace(const TraceCollection &traces, + std::string_view pointer, std::string_view name, + std::string_view message, const bool fixable) + -> bool { + return std::any_of(traces.cbegin(), traces.cend(), + [&pointer, &name, &message, fixable](const auto &trace) { + return sourcemeta::core::to_string(std::get<0>(trace)) == + pointer && + std::get<1>(trace) == name && + std::get<2>(trace) == message && + std::get<4>(trace) == fixable; + }); +} + static auto alterschema_test_resolver(std::string_view identifier) -> std::optional { if (identifier == @@ -75,6 +92,10 @@ static auto alterschema_test_resolver(std::string_view identifier) EXPECT_EQ(std::get<2>((traces).at(index)), (message)); \ EXPECT_EQ(std::get<4>((traces).at(index)), (fixable)); +#define EXPECT_LINT_TRACE_EXISTS(traces, pointer, name, message, fixable) \ + EXPECT_TRUE( \ + has_lint_trace((traces), (pointer), (name), (message), (fixable))); + #define LINT_AND_FIX(document, result, traces) \ std::vector> \ From 2fcb7994759ad9b3fa6e39264dabafd9faa51295 Mon Sep 17 00:00:00 2001 From: Vaibhav mittal Date: Fri, 10 Apr 2026 14:23:41 +0000 Subject: [PATCH 3/3] fix(alterschema): restore core parity for format_type_mismatch - revert rule logic to the original core condition - restore keyword targeting and vocabulary guard - remove Blaze-specific test helper indirection - match core trace expectations in all dialect tests Signed-off-by: Vaibhav mittal --- src/alterschema/linter/format_type_mismatch.h | 27 +++---- .../alterschema_lint_2019_09_test.cc | 59 +++++++++------ .../alterschema_lint_2020_12_test.cc | 59 +++++++++------ .../alterschema_lint_draft3_test.cc | 71 +++++++++++++------ .../alterschema_lint_draft4_test.cc | 59 +++++++++------ .../alterschema_lint_draft6_test.cc | 59 +++++++++------ .../alterschema_lint_draft7_test.cc | 59 +++++++++------ test/alterschema/alterschema_test_utils.h | 21 ------ 8 files changed, 258 insertions(+), 156 deletions(-) diff --git a/src/alterschema/linter/format_type_mismatch.h b/src/alterschema/linter/format_type_mismatch.h index 3020ac374..b6ec7f3bd 100644 --- a/src/alterschema/linter/format_type_mismatch.h +++ b/src/alterschema/linter/format_type_mismatch.h @@ -14,20 +14,21 @@ class FormatTypeMismatch final : public SchemaTransformRule { const sourcemeta::core::Vocabularies &vocabularies, const sourcemeta::core::SchemaFrame &, const sourcemeta::core::SchemaFrame::Location &, - const sourcemeta::core::SchemaWalker &walker, + const sourcemeta::core::SchemaWalker &, const sourcemeta::core::SchemaResolver &) const -> sourcemeta::core::SchemaTransformRule::Result override { - ONLY_CONTINUE_IF(schema.is_object() && schema.defines("format") && - schema.at("format").is_string() && - schema.defines("type") && schema.at("type").is_string()); - - const auto current_types{parse_schema_type(schema.at("type"))}; - ONLY_CONTINUE_IF(current_types.any()); - - const auto &metadata{walker("format", vocabularies)}; - ONLY_CONTINUE_IF(metadata.instances.any() && - (metadata.instances & current_types).none()); - - return APPLIES_TO_KEYWORDS("format"); + 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, + Vocabularies::Known::JSON_Schema_Draft_4, + Vocabularies::Known::JSON_Schema_Draft_3}) && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() != "string" && + schema.defines("format") && + schema.at("format").is_string()); + return APPLIES_TO_KEYWORDS("format", "type"); } }; diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index a579417a6..75efe3bf6 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -3547,10 +3547,15 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_2) { @@ -3601,10 +3606,16 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_5) { @@ -3638,10 +3649,16 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { @@ -3662,10 +3679,11 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { @@ -3689,10 +3707,11 @@ TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/A", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2019_09, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index 1fb928fc5..b159b3359 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -3976,10 +3976,15 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_2) { @@ -4030,10 +4035,16 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_5) { @@ -4067,10 +4078,16 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { @@ -4091,10 +4108,11 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { @@ -4118,10 +4136,11 @@ TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/$defs/A", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_2020_12, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index a33fc02e3..296aaf466 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -1094,10 +1094,15 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_2) { @@ -1145,10 +1150,16 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_5) { @@ -1180,10 +1191,16 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { @@ -1200,11 +1217,16 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS( - traces, "/additionalProperties", "format_type_mismatch", - "The `format` keyword validates string instances but `type` is not " - "`string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { @@ -1224,11 +1246,16 @@ TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS( - traces, "/additionalProperties", "format_type_mismatch", - "The `format` keyword validates string instances but `type` is not " - "`string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft3, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 70d8c45d5..6f56a2dac 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -1765,10 +1765,15 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_2) { @@ -1816,10 +1821,16 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_5) { @@ -1851,10 +1862,16 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { @@ -1876,10 +1893,11 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { @@ -1904,10 +1922,11 @@ TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft4, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index ef41a1ab0..50004b832 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -2035,10 +2035,15 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_2) { @@ -2089,10 +2094,16 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_5) { @@ -2126,10 +2137,16 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { @@ -2152,10 +2169,11 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { @@ -2181,10 +2199,11 @@ TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft6, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index 3068d645b..90c8c6057 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -2383,10 +2383,15 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_1) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_2) { @@ -2437,10 +2442,16 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_4) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/properties/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_5) { @@ -2474,10 +2485,16 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_6) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/items", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { @@ -2500,10 +2517,11 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/foo", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { @@ -2529,10 +2547,11 @@ TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { LINT_WITHOUT_FIX(document, result, traces); EXPECT_FALSE(result.first); - EXPECT_LINT_TRACE_EXISTS(traces, "/definitions/A", "format_type_mismatch", - "The `format` keyword validates string instances " - "but `type` is not `string`", - false); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); } TEST(AlterSchema_lint_draft7, format_type_mismatch_9) { diff --git a/test/alterschema/alterschema_test_utils.h b/test/alterschema/alterschema_test_utils.h index fc8936b20..1781af9c9 100644 --- a/test/alterschema/alterschema_test_utils.h +++ b/test/alterschema/alterschema_test_utils.h @@ -4,26 +4,9 @@ #include #include -#include -#include #include #include -template -static auto has_lint_trace(const TraceCollection &traces, - std::string_view pointer, std::string_view name, - std::string_view message, const bool fixable) - -> bool { - return std::any_of(traces.cbegin(), traces.cend(), - [&pointer, &name, &message, fixable](const auto &trace) { - return sourcemeta::core::to_string(std::get<0>(trace)) == - pointer && - std::get<1>(trace) == name && - std::get<2>(trace) == message && - std::get<4>(trace) == fixable; - }); -} - static auto alterschema_test_resolver(std::string_view identifier) -> std::optional { if (identifier == @@ -92,10 +75,6 @@ static auto alterschema_test_resolver(std::string_view identifier) EXPECT_EQ(std::get<2>((traces).at(index)), (message)); \ EXPECT_EQ(std::get<4>((traces).at(index)), (fixable)); -#define EXPECT_LINT_TRACE_EXISTS(traces, pointer, name, message, fixable) \ - EXPECT_TRUE( \ - has_lint_trace((traces), (pointer), (name), (message), (fixable))); - #define LINT_AND_FIX(document, result, traces) \ std::vector> \