Index: include/llvm/Support/YAMLTraits.h =================================================================== --- include/llvm/Support/YAMLTraits.h +++ include/llvm/Support/YAMLTraits.h @@ -1620,8 +1620,9 @@ bool NeedBitValueComma = false; bool NeedFlowSequenceComma = false; bool EnumerationMatchFound = false; - bool NeedsNewLine = false; bool WriteDefaultValues = false; + StringRef Padding; + StringRef PaddingBeforeContainer; }; /// YAML I/O does conversion based on types. But often native data types Index: lib/Support/YAMLTraits.cpp =================================================================== --- lib/Support/YAMLTraits.cpp +++ lib/Support/YAMLTraits.cpp @@ -446,7 +446,8 @@ void Output::beginMapping() { StateStack.push_back(inMapFirstKey); - NeedsNewLine = true; + PaddingBeforeContainer = Padding; + Padding = "\n"; } bool Output::mapTag(StringRef Tag, bool Use) { @@ -474,7 +475,7 @@ } // Tags inside maps in sequences should act as keys in the map from a // formatting perspective, so we always want a newline in a sequence. - NeedsNewLine = true; + Padding = "\n"; } } return Use; @@ -482,8 +483,12 @@ void Output::endMapping() { // If we did not map anything, we should explicitly emit an empty map - if (StateStack.back() == inMapFirstKey) + if (StateStack.back() == inMapFirstKey) { + Padding = PaddingBeforeContainer; + newLineCheck(); output("{}"); + Padding = "\n"; + } StateStack.pop_back(); } @@ -548,14 +553,19 @@ unsigned Output::beginSequence() { StateStack.push_back(inSeqFirstElement); - NeedsNewLine = true; + PaddingBeforeContainer = Padding; + Padding = "\n"; return 0; } void Output::endSequence() { // If we did not emit anything, we should explicitly emit an empty sequence - if (StateStack.back() == inSeqFirstElement) + if (StateStack.back() == inSeqFirstElement) { + Padding = PaddingBeforeContainer; + newLineCheck(); output("[]"); + Padding = "\n"; + } StateStack.pop_back(); } @@ -746,7 +756,7 @@ output(s); if (StateStack.empty() || (!inFlowSeqAnyElement(StateStack.back()) && !inFlowMapAnyKey(StateStack.back()))) - NeedsNewLine = true; + Padding = "\n"; } void Output::outputNewLine() { @@ -759,11 +769,13 @@ // void Output::newLineCheck() { - if (!NeedsNewLine) + if (Padding != "\n") { + output(Padding); + Padding = {}; return; - NeedsNewLine = false; - + } outputNewLine(); + Padding = {}; if (StateStack.size() == 0) return; @@ -797,9 +809,9 @@ output(":"); const char *spaces = " "; if (key.size() < strlen(spaces)) - output(&spaces[key.size()]); + Padding = &spaces[key.size()]; else - output(" "); + Padding = " "; } void Output::flowKey(StringRef Key) { Index: unittests/BinaryFormat/MsgPackDocumentTest.cpp =================================================================== --- unittests/BinaryFormat/MsgPackDocumentTest.cpp +++ unittests/BinaryFormat/MsgPackDocumentTest.cpp @@ -127,7 +127,7 @@ ASSERT_EQ(OStream.str(), "---\n" "bar: 2\n" "foo: 1\n" - "qux: \n" + "qux:\n" " baz: true\n" "...\n"); } @@ -147,7 +147,7 @@ ASSERT_EQ(OStream.str(), "---\n" "bar: 0x2\n" "foo: 1\n" - "qux: \n" + "qux:\n" " baz: true\n" "...\n"); } Index: unittests/Support/YAMLIOTest.cpp =================================================================== --- unittests/Support/YAMLIOTest.cpp +++ unittests/Support/YAMLIOTest.cpp @@ -2528,7 +2528,7 @@ ostr.flush(); EXPECT_EQ(1, Context.A); EXPECT_EQ("---\n" - "Simple: \n" + "Simple:\n" " B: 0\n" " C: 0\n" " Context: 1\n" @@ -2543,7 +2543,7 @@ ostr.flush(); EXPECT_EQ(2, Context.A); EXPECT_EQ("---\n" - "Simple: \n" + "Simple:\n" " B: 2\n" " C: 3\n" " Context: 2\n" @@ -2556,13 +2556,22 @@ TEST(YAMLIO, TestCustomMapping) { std::map x; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + xout << x; + ostr.flush(); + EXPECT_EQ("---\n" + "{}\n" + "...\n", + out); + x["foo"] = 1; x["bar"] = 2; - std::string out; - llvm::raw_string_ostream ostr(out); - Output xout(ostr, nullptr, 0); - + out.clear(); xout << x; ostr.flush(); EXPECT_EQ("---\n" @@ -2595,10 +2604,10 @@ xout << x; ostr.flush(); EXPECT_EQ("---\n" - "bar: \n" + "bar:\n" " foo: 3\n" " bar: 4\n" - "foo: \n" + "foo:\n" " foo: 1\n" " bar: 2\n" "...\n", @@ -2614,6 +2623,34 @@ EXPECT_EQ(4, y["bar"].bar); } +struct FooBarMapMap { + std::map fbm; +}; + +template <> struct MappingTraits { + static void mapping(IO &io, FooBarMapMap &x) { + io.mapRequired("fbm", x.fbm); + } +}; + +TEST(YAMLIO, TestEmptyMapWrite) { + FooBarMapMap cont; + std::string str; + llvm::raw_string_ostream OS(str); + Output yout(OS); + yout << cont; + EXPECT_EQ(OS.str(), "---\nfbm: {}\n...\n"); +} + +TEST(YAMLIO, TestEmptySequenceWrite) { + FooBarContainer cont; + std::string str; + llvm::raw_string_ostream OS(str); + Output yout(OS); + yout << cont; + EXPECT_EQ(OS.str(), "---\nfbs: []\n...\n"); +} + static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) { std::string out; llvm::raw_string_ostream ostr(out); Index: unittests/TextAPI/ELFYAMLTest.cpp =================================================================== --- unittests/TextAPI/ELFYAMLTest.cpp +++ unittests/TextAPI/ELFYAMLTest.cpp @@ -149,7 +149,7 @@ "--- !tapi-tbe\n" "TbeVersion: 1.0\n" "Arch: AArch64\n" - "Symbols: \n" + "Symbols:\n" " bar: { Type: Func, Weak: true }\n" " foo: { Type: NoType, Size: 99, Warning: Does nothing }\n" " nor: { Type: Func, Undefined: true }\n" @@ -205,7 +205,7 @@ "TbeVersion: 1.0\n" "SoName: nosyms.so\n" "Arch: x86_64\n" - "NeededLibs: \n" + "NeededLibs:\n" " - libc.so\n" " - libfoo.so\n" " - libbar.so\n" Index: unittests/TextAPI/TextStubV1Tests.cpp =================================================================== --- unittests/TextAPI/TextStubV1Tests.cpp +++ unittests/TextAPI/TextStubV1Tests.cpp @@ -159,7 +159,7 @@ "compatibility-version: 0\n" "swift-version: 5\n" "objc-constraint: retain_release\n" - "exports: \n" + "exports:\n" " - archs: [ i386 ]\n" " symbols: [ _sym1 ]\n" " weak-def-symbols: [ _sym2 ]\n" Index: unittests/TextAPI/TextStubV2Tests.cpp =================================================================== --- unittests/TextAPI/TextStubV2Tests.cpp +++ unittests/TextAPI/TextStubV2Tests.cpp @@ -182,7 +182,7 @@ "current-version: 1.2.3\n" "compatibility-version: 0\n" "swift-version: 5\n" - "exports: \n" + "exports:\n" " - archs: [ i386 ]\n" " symbols: [ _sym1 ]\n" " weak-def-symbols: [ _sym2 ]\n"