diff --git a/src/emitfromevents.cpp b/src/emitfromevents.cpp index 350bde648..0d410c426 100644 --- a/src/emitfromevents.cpp +++ b/src/emitfromevents.cpp @@ -117,7 +117,12 @@ void EmitFromEvents::BeginNode() { void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) { if (!tag.empty() && tag != "?" && tag != "!"){ - if (tag[0] == '!') { + if (tag.size() >= 2 && tag[0] == '!' && tag[1] == '!') { + // Tags like "!!str" use the secondary tag handle; emit them through + // SecondaryTag so the rendered output keeps both bangs instead of + // failing the LocalTag regex check on the inner '!'. See #1373. + m_emitter << SecondaryTag(std::string(tag.begin()+2, tag.end())); + } else if (tag[0] == '!') { m_emitter << LocalTag(std::string(tag.begin()+1, tag.end())); } else { m_emitter << VerbatimTag(tag); diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index d7698434c..65366175e 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -231,6 +231,33 @@ TEST(NodeTest, EmitEmptyNode) { EXPECT_EQ("", std::string(emitter.c_str())); } +// Regression for #1373: emitting a node whose tag begins with "!!" +// (a YAML secondary tag handle, e.g. "!!str") used to bail out with +// INVALID_TAG and truncate the output after the first '!'. +TEST(NodeTest, EmitSetTagSecondaryHandle) { + Node root; + Node string_node{"hello"}; + string_node.SetTag("!!str"); + root["some_string"] = string_node; + root["some_int"] = 2; + + Emitter emitter; + emitter << root; + EXPECT_EQ("some_string: !!str hello\nsome_int: 2", + std::string(emitter.c_str())); +} + +TEST(NodeTest, EmitSetTagPrimaryHandle) { + Node root; + Node string_node{"hello"}; + string_node.SetTag("!mytag"); + root["v"] = string_node; + + Emitter emitter; + emitter << root; + EXPECT_EQ("v: !mytag hello", std::string(emitter.c_str())); +} + TEST(NodeTest, ParseNodeStyle) { EXPECT_EQ(EmitterStyle::Flow, Load("[1, 2, 3]").Style()); EXPECT_EQ(EmitterStyle::Flow, Load("{foo: bar}").Style());