@@ -34,7 +34,11 @@ auto resolve_destination(const sourcemeta::core::JSON::String &raw_ref,
3434 return std::nullopt ;
3535}
3636
37- using VisitedSchemas = std::map<const sourcemeta::core::JSON *, std::size_t >;
37+ struct VisitedEntry {
38+ std::size_t identifier;
39+ sourcemeta::core::JSON path;
40+ };
41+ using VisitedSchemas = std::map<const sourcemeta::core::JSON *, VisitedEntry>;
3842using RefChain = std::set<const sourcemeta::core::JSON *>;
3943
4044auto type_expression_of (const sourcemeta::core::JSON &schema,
@@ -71,8 +75,9 @@ auto type_expression_of(const sourcemeta::core::JSON &schema,
7175 if (visited_entry != visited.end ()) {
7276 result.assign (" kind" , sourcemeta::core::JSON{" recursiveRef" });
7377 result.assign (" identifier" ,
74- sourcemeta::core::JSON{
75- static_cast <std::int64_t >(visited_entry->second )});
78+ sourcemeta::core::JSON{static_cast <std::int64_t >(
79+ visited_entry->second .identifier )});
80+ result.assign (" path" , visited_entry->second .path );
7681 return result;
7782 }
7883
@@ -689,7 +694,8 @@ auto walk_properties(const sourcemeta::core::JSON &schema,
689694 resolved.at (" type" ).is_string ()) {
690695 const auto &resolved_type{resolved.at (" type" ).to_string ()};
691696 if (resolved_type == " object" ) {
692- visited.emplace (&resolved, row_identifier);
697+ visited.emplace (&resolved, VisitedEntry{.identifier = row_identifier,
698+ .path = path});
693699 walk_properties (resolved, path, rows, frame, root, visited,
694700 next_identifier);
695701 walk_pattern_properties (resolved, path, rows, frame, root, visited,
@@ -704,26 +710,30 @@ auto walk_properties(const sourcemeta::core::JSON &schema,
704710 !resolved.defines (" prefixItems" )) {
705711 const auto &items_schema{
706712 resolve_ref (resolved.at (" items" ), frame, root, visited)};
707- if (items_schema.is_object () && items_schema.defines (" type" ) &&
708- items_schema.at (" type" ).is_string () &&
709- items_schema.at (" type" ).to_string () == " object" ) {
713+ if (items_schema.is_object ()) {
710714 auto wildcard_path{path};
711715 wildcard_path.push_back (make_path_segment (" wildcard" , " *" ));
712716 const auto items_row_id{next_identifier};
713717 emit_row (items_schema, wildcard_path, rows, frame, root, visited,
714718 next_identifier);
715- visited.emplace (&items_schema, items_row_id);
716- walk_properties (items_schema, wildcard_path, rows, frame, root,
717- visited, next_identifier);
718- walk_pattern_properties (items_schema, wildcard_path, rows, frame,
719- root, visited, next_identifier);
720- walk_wildcard_keyword (items_schema, " additionalProperties" ,
721- wildcard_path, rows, frame, root, visited,
722- next_identifier);
723- walk_wildcard_keyword (items_schema, " unevaluatedProperties" ,
724- wildcard_path, rows, frame, root, visited,
725- next_identifier);
726- visited.erase (&items_schema);
719+ if (items_schema.defines (" type" ) &&
720+ items_schema.at (" type" ).is_string () &&
721+ items_schema.at (" type" ).to_string () == " object" ) {
722+ visited.emplace (&items_schema,
723+ VisitedEntry{.identifier = items_row_id,
724+ .path = wildcard_path});
725+ walk_properties (items_schema, wildcard_path, rows, frame, root,
726+ visited, next_identifier);
727+ walk_pattern_properties (items_schema, wildcard_path, rows, frame,
728+ root, visited, next_identifier);
729+ walk_wildcard_keyword (items_schema, " additionalProperties" ,
730+ wildcard_path, rows, frame, root, visited,
731+ next_identifier);
732+ walk_wildcard_keyword (items_schema, " unevaluatedProperties" ,
733+ wildcard_path, rows, frame, root, visited,
734+ next_identifier);
735+ visited.erase (&items_schema);
736+ }
727737 }
728738 }
729739 }
@@ -738,19 +748,31 @@ auto walk_wildcard_keyword(const sourcemeta::core::JSON &schema,
738748 const sourcemeta::core::JSON &root,
739749 VisitedSchemas &visited,
740750 std::size_t &next_identifier) -> void {
741- if (!schema.is_object () || !schema.defines (keyword) ||
742- !schema.at (keyword).is_object ()) {
751+ if (!schema.is_object () || !schema.defines (keyword)) {
743752 return ;
744753 }
745754
755+ const auto &value{schema.at (keyword)};
756+
746757 if (keyword == " unevaluatedItems" && schema.defines (" prefixItems" )) {
747758 return ;
748759 }
749760
761+ if (value.is_boolean () && value.to_boolean ()) {
762+ auto path{base_path};
763+ path.push_back (make_path_segment (" wildcard" , " *" ));
764+ emit_row (value, std::move (path), rows, frame, root, visited,
765+ next_identifier);
766+ return ;
767+ }
768+
769+ if (!value.is_object ()) {
770+ return ;
771+ }
772+
750773 auto path{base_path};
751774 path.push_back (make_path_segment (" wildcard" , " *" ));
752- emit_row (schema.at (keyword), std::move (path), rows, frame, root, visited,
753- next_identifier);
775+ emit_row (value, std::move (path), rows, frame, root, visited, next_identifier);
754776}
755777
756778auto walk_pattern_properties (const sourcemeta::core::JSON &schema,
@@ -1128,14 +1150,19 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
11281150 auto type_expr{sourcemeta::core::JSON::make_object ()};
11291151 type_expr.assign (" kind" , sourcemeta::core::JSON{" recursiveRef" });
11301152 type_expr.assign (" identifier" ,
1131- sourcemeta::core::JSON{
1132- static_cast <std::int64_t >(visited_entry->second )});
1153+ sourcemeta::core::JSON{static_cast <std::int64_t >(
1154+ visited_entry->second .identifier )});
1155+ type_expr.assign (" path" , visited_entry->second .path );
11331156 row.assign (" type" , std::move (type_expr));
11341157 rows.push_back (std::move (row));
11351158 documentation.assign (" rows" , std::move (rows));
11361159 return documentation;
11371160 }
1138- visited.emplace (&target_schema, next_identifier);
1161+ auto ref_path{sourcemeta::core::JSON::make_array ()};
1162+ ref_path.push_back (make_path_segment (" synthetic" , " root" ));
1163+ visited.emplace (&target_schema,
1164+ VisitedEntry{.identifier = next_identifier,
1165+ .path = std::move (ref_path)});
11391166 auto result{walk_schema (target_schema, include_root, frame, root, visited,
11401167 next_identifier)};
11411168 visited.erase (&target_schema);
@@ -1166,7 +1193,11 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
11661193 next_identifier);
11671194 const auto root_row_identifier{static_cast <std::size_t >(
11681195 rows.at (rows.size () - 1 ).at (" identifier" ).to_integer ())};
1169- visited.emplace (&schema, root_row_identifier);
1196+ auto visited_root_path{sourcemeta::core::JSON::make_array ()};
1197+ visited_root_path.push_back (make_path_segment (" synthetic" , " root" ));
1198+ visited.emplace (&schema,
1199+ VisitedEntry{.identifier = root_row_identifier,
1200+ .path = std::move (visited_root_path)});
11701201 }
11711202
11721203 if (!schema.is_object ()) {
@@ -1201,25 +1232,28 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12011232 schema.at (" items" ).is_object () && !schema.defines (" prefixItems" )) {
12021233 const auto &items_schema{
12031234 resolve_ref (schema.at (" items" ), frame, root, visited)};
1204- if (items_schema.is_object () && items_schema.defines (" type" ) &&
1205- items_schema.at (" type" ).is_string () &&
1206- items_schema.at (" type" ).to_string () == " object" ) {
1235+ if (items_schema.is_object ()) {
12071236 auto wildcard_path{sourcemeta::core::JSON::make_array ()};
12081237 wildcard_path.push_back (make_path_segment (" wildcard" , " *" ));
12091238 const auto items_row_id{next_identifier};
12101239 emit_row (items_schema, wildcard_path, rows, frame, root, visited,
12111240 next_identifier);
1212- visited.emplace (&items_schema, items_row_id);
1213- walk_properties (items_schema, wildcard_path, rows, frame, root, visited,
1214- next_identifier);
1215- walk_pattern_properties (items_schema, wildcard_path, rows, frame, root,
1216- visited, next_identifier);
1217- walk_wildcard_keyword (items_schema, " additionalProperties" , wildcard_path,
1218- rows, frame, root, visited, next_identifier);
1219- walk_wildcard_keyword (items_schema, " unevaluatedProperties" ,
1220- wildcard_path, rows, frame, root, visited,
1221- next_identifier);
1222- visited.erase (&items_schema);
1241+ if (items_schema.defines (" type" ) && items_schema.at (" type" ).is_string () &&
1242+ items_schema.at (" type" ).to_string () == " object" ) {
1243+ visited.emplace (&items_schema, VisitedEntry{.identifier = items_row_id,
1244+ .path = wildcard_path});
1245+ walk_properties (items_schema, wildcard_path, rows, frame, root, visited,
1246+ next_identifier);
1247+ walk_pattern_properties (items_schema, wildcard_path, rows, frame, root,
1248+ visited, next_identifier);
1249+ walk_wildcard_keyword (items_schema, " additionalProperties" ,
1250+ wildcard_path, rows, frame, root, visited,
1251+ next_identifier);
1252+ walk_wildcard_keyword (items_schema, " unevaluatedProperties" ,
1253+ wildcard_path, rows, frame, root, visited,
1254+ next_identifier);
1255+ visited.erase (&items_schema);
1256+ }
12231257 }
12241258 }
12251259
@@ -1276,26 +1310,21 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12761310 }
12771311 }
12781312
1279- if (schema.is_object () && schema.defines (" not" ) &&
1280- schema.at (" not" ).is_object ()) {
1313+ if (schema.is_object () && schema.defines (" not" )) {
12811314 const auto ¬_schema{schema.at (" not" )};
12821315 const auto is_branching{
1283- not_schema.defines (" anyOf" ) || not_schema.defines (" oneOf" ) ||
1284- not_schema.defines (" allOf" ) || not_schema.defines (" not" )};
1285- if (is_branching) {
1316+ not_schema.is_object () &&
1317+ (not_schema.defines (" anyOf" ) || not_schema.defines (" oneOf" ) ||
1318+ not_schema.defines (" allOf" ) || not_schema.defines (" not" ))};
1319+ const auto has_inline_constraints{!is_branching && not_schema.is_object () &&
1320+ !constraints_of (not_schema).empty ()};
1321+ if (!has_inline_constraints) {
12861322 walk_branching_subschema (" Must NOT match" , " value" , not_schema,
12871323 doc_children, frame, root, visited,
12881324 next_identifier, false );
12891325 }
12901326 }
12911327
1292- if (schema.is_object () && schema.defines (" not" ) &&
1293- schema.at (" not" ).is_boolean ()) {
1294- walk_branching_subschema (" Must NOT match" , " value" , schema.at (" not" ),
1295- doc_children, frame, root, visited,
1296- next_identifier, false );
1297- }
1298-
12991328 assert (!rows.empty () || !doc_children.empty ());
13001329
13011330 documentation.assign (" rows" , std::move (rows));
0 commit comments