From 32ff5ed96919e32ffd89c3b58f094bbc4a55c9d0 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 28 Sep 2026 16:28:18 -0300 Subject: [PATCH] Reject a YAML flow mapping entry that is missing its comma Signed-off-by: Juan Cruz Viotti --- src/core/yaml/parser.h | 12 +++++++++++- test/yaml/yaml_parse_test.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/core/yaml/parser.h b/src/core/yaml/parser.h index 2894fc1da..a26c7088f 100644 --- a/src/core/yaml/parser.h +++ b/src/core/yaml/parser.h @@ -789,7 +789,17 @@ class Parser { switch (current_token.type) { case TokenType::Scalar: { auto next{this->next_token()}; - if (next.has_value() && next->type == TokenType::BlockMappingValue) { + // YAML 1.2.2 Section 7.4.2: the value of an entry of a flow collection + // is a single node, and a pair carrying no brackets of its own is a + // node only where a flow sequence takes its entries. Leaving the + // indicator unread in that position hands the scalar back on its own, + // which is what lets the caller report the separator the entry is + // really missing + const auto pair_without_brackets_allowed{ + this->lexer_->flow_level() == 0 || + context != JSON::ParseContext::Property}; + if (next.has_value() && next->type == TokenType::BlockMappingValue && + pair_without_brackets_allowed) { if (current_token.multiline) [[unlikely]] { throw YAMLParseError{current_token.line, current_token.column, "Multi-line implicit mapping key"}; diff --git a/test/yaml/yaml_parse_test.cc b/test/yaml/yaml_parse_test.cc index af5acd29e..7db0e6306 100644 --- a/test/yaml/yaml_parse_test.cc +++ b/test/yaml/yaml_parse_test.cc @@ -2769,3 +2769,35 @@ TEST(implicit_key_anchor_on_a_plain_scalar_stays_a_string) { sourcemeta::core::parse_json(R"JSON({ "foo": 1, "b": "foo" })JSON")}; EXPECT_EQ(result, expected); } + +// YAML 1.2.2 Section 7.4.2: the entries of a flow mapping are separated by a +// comma, and the value of an entry is a single node, so a second pair cannot +// stand in the place of the first entry's value +TEST(flow_mapping_entries_without_a_comma) { + const std::string input{"{a: 1 b: 2}"}; + try { + sourcemeta::core::parse_yaml(input); + FAIL(); + } catch (const sourcemeta::core::YAMLParseError &error) { + EXPECT_STREQ(error.what(), "Missing comma between flow mapping entries"); + } +} + +TEST(flow_sequence_single_pair_entry_still_parses) { + const std::string input{"[a: 1]"}; + const auto result{sourcemeta::core::parse_yaml(input)}; + EXPECT_TRUE(result.is_array()); + EXPECT_EQ(result.size(), 1); + EXPECT_TRUE(result.at(0).is_object()); + EXPECT_EQ(result.at(0).at("a"), sourcemeta::core::JSON{1}); +} + +TEST(flow_sequence_single_pair_value_cannot_be_another_pair) { + const std::string input{"[a: b: c]"}; + try { + sourcemeta::core::parse_yaml(input); + FAIL(); + } catch (const sourcemeta::core::YAMLParseError &error) { + EXPECT_STREQ(error.what(), "Missing comma in flow sequence"); + } +}