diff --git a/src/parser.c b/src/parser.c index 10770936..9ae6a1bb 100644 --- a/src/parser.c +++ b/src/parser.c @@ -491,12 +491,6 @@ yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event) implicit = 0; } - while (!STACK_EMPTY(parser, parser->tag_directives)) { - yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives); - yaml_free(tag_directive.handle); - yaml_free(tag_directive.prefix); - } - parser->state = YAML_PARSE_DOCUMENT_START_STATE; DOCUMENT_END_EVENT_INIT(*event, implicit, start_mark, end_mark); @@ -1250,6 +1244,7 @@ yaml_parser_process_directives(yaml_parser_t *parser, }; yaml_tag_directive_t *default_tag_directive; yaml_version_directive_t *version_directive = NULL; + int first_directive = 1; struct { yaml_tag_directive_t *start; yaml_tag_directive_t *end; @@ -1266,6 +1261,17 @@ yaml_parser_process_directives(yaml_parser_t *parser, while (token->type == YAML_VERSION_DIRECTIVE_TOKEN || token->type == YAML_TAG_DIRECTIVE_TOKEN) { + if (first_directive) { + /* First directive seen: this document has its own directives, + * so discard any inherited from the previous document. */ + while (!STACK_EMPTY(parser, parser->tag_directives)) { + yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives); + yaml_free(tag_directive.handle); + yaml_free(tag_directive.prefix); + } + first_directive = 0; + } + if (token->type == YAML_VERSION_DIRECTIVE_TOKEN) { if (version_directive) { yaml_parser_set_parser_error(parser, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 17a9c671..97010eae 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,10 +19,12 @@ foreach(name IN ITEMS test-nesting test-reader test-version + test-directive-inheritance ) add_yaml_executable(${name}) endforeach() +add_test(NAME directive-inheritance COMMAND test-directive-inheritance) add_test(NAME version COMMAND test-version) add_test(NAME reader COMMAND test-reader) add_test(NAME nesting COMMAND test-nesting) diff --git a/tests/test-directive-inheritance.c b/tests/test-directive-inheritance.c new file mode 100644 index 00000000..399b2420 --- /dev/null +++ b/tests/test-directive-inheritance.c @@ -0,0 +1,59 @@ +#include + +#include +#include +#include + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include + +/* + * Test: %TAG directive inheritance across documents in a multi-document stream. + * + * YAML 1.1 spec section 7.4: + * "If the document specifies no directives, it is parsed using the same + * settings as the previous document. If the document does specify any + * directives, all directives of previous documents, if any, are ignored." + * + * libyaml bug: yaml_parser_parse_document_end() unconditionally clears + * parser->tag_directives, so document 2 (which has no directives) cannot + * inherit the %TAG mapping from document 1. When the parser then encounters + * !x!bar in document 2 it cannot resolve the handle and throws a parse + * error. The assert inside the loop will FAIL until the bug is fixed. + */ + +static const unsigned char yaml_input[] = + "%YAML 1.1\n" + "%TAG !x! tag:example.com,2026:\n" + "--- !x!food\n" + "x: 0\n" + "--- !x!bar\n" + "x: 1\n"; + +int main(void) +{ + yaml_parser_t parser; + yaml_event_t event; + int done = 0; + + assert(yaml_parser_initialize(&parser)); + yaml_parser_set_input_string(&parser, yaml_input, sizeof(yaml_input) - 1); + + printf("Testing Directive Inheritance ... "); + fflush(stdout); + + while (!done) { + /* With the bug, this assert fires when the parser hits !x!bar + * in doc 2 and cannot resolve the handle. A spec-compliant + * parser must succeed for the entire stream. */ + assert(yaml_parser_parse(&parser, &event)); + done = (event.type == YAML_STREAM_END_EVENT); + yaml_event_delete(&event); + } + + yaml_parser_delete(&parser); + printf("PASSED\n"); + return 0; +} \ No newline at end of file