Index: include/llvm/Support/YAMLTraits.h =================================================================== --- include/llvm/Support/YAMLTraits.h +++ include/llvm/Support/YAMLTraits.h @@ -540,11 +540,14 @@ case '.': case ',': case ' ': - // TAB (0x9), LF (0xA), CR (0xD) and NEL (0x85) are allowed. + // TAB (0x9) is allowed in unquoted strings. case 0x9: + continue; + // LF(0xA) and CR(0xD) may delimit values and so require at least single + // quotes. case 0xA: case 0xD: - case 0x85: + MaxQuotingNeeded = QuotingType::Single; continue; // DEL (0x7F) are excluded from the allowed character range. case 0x7F: Index: unittests/Support/YAMLIOTest.cpp =================================================================== --- unittests/Support/YAMLIOTest.cpp +++ unittests/Support/YAMLIOTest.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/YAMLTraits.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using llvm::yaml::Input; @@ -25,6 +26,7 @@ using llvm::yaml::Hex16; using llvm::yaml::Hex32; using llvm::yaml::Hex64; +using ::testing::StartsWith; @@ -249,6 +251,72 @@ EXPECT_TRUE(!!yin.error()); } +struct WithStringField { + std::string str1; + std::string str2; + std::string str3; +}; + +namespace llvm { +namespace yaml { +template <> struct MappingTraits { + static void mapping(IO &io, WithStringField &fb) { + io.mapRequired("str1", fb.str1); + io.mapRequired("str2", fb.str2); + io.mapRequired("str3", fb.str3); + } +}; +} // namespace yaml +} // namespace llvm + +TEST(YAMLIO, MultilineStrings) { + WithStringField Original; + Original.str1 = "a multiline string\nfoobarbaz"; + Original.str2 = "another one\rfoobarbaz"; + Original.str3 = "a one-line string"; + + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + Output YOut(OS); + YOut << Original; + } + auto Expected = "---\n" + "str1: 'a multiline string\n" + "foobarbaz'\n" + "str2: 'another one\r" + "foobarbaz'\n" + "str3: a one-line string\n" + "...\n"; + ASSERT_EQ(Serialized, Expected); + + // Also check it parses back without the errors. + WithStringField Deserialized; + { + Input YIn(Serialized); + YIn >> Deserialized; + ASSERT_FALSE(YIn.error()) + << "Parsing error occurred during deserialization. Serialized string:\n" + << Serialized; + } + EXPECT_EQ(Original.str1, Deserialized.str1); + EXPECT_EQ(Original.str2, Deserialized.str2); + EXPECT_EQ(Original.str3, Deserialized.str3); +} + +TEST(YAMLIO, NoQuotesForTab) { + WithStringField WithTab; + WithTab.str1 = "aba\tcaba"; + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + Output YOut(OS); + YOut << WithTab; + } + auto ExpectedPrefix = "---\n" + "str1: aba\tcaba\n"; + EXPECT_THAT(Serialized, StartsWith(ExpectedPrefix)); +} //===----------------------------------------------------------------------===// // Test built-in types