njames93 created this revision.
njames93 added reviewers: aaron.ballman, gribozavr2, AlexanderLanin.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Escapes replacement text when exporting to yaml and unescapes when importing 
from yaml.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D76037

Files:
  clang/include/clang/Tooling/ReplacementsYaml.h
  clang/unittests/Tooling/ReplacementsYamlTest.cpp

Index: clang/unittests/Tooling/ReplacementsYamlTest.cpp
===================================================================
--- clang/unittests/Tooling/ReplacementsYamlTest.cpp
+++ clang/unittests/Tooling/ReplacementsYamlTest.cpp
@@ -46,28 +46,35 @@
                YamlContentStream.str().c_str());
 }
 
-TEST(ReplacementsYamlTest, serializesNewLines) {
-  TranslationUnitReplacements Doc;
+TEST(ReplacementsYamlTest, handlesEscaped) {
+  TranslationUnitReplacements Doc, NewDoc;
 
   Doc.MainSourceFile = "/path/to/source.cpp";
-  Doc.Replacements.emplace_back("/path/to/file1.h", 0, 0, "#include <utility>\n");
+  const StringRef FilePath = "/path/to/file1.h";
+  Doc.Replacements.emplace_back(FilePath, 0, 0, "#include <utility>\n");
+  Doc.Replacements.emplace_back(FilePath, 0, 0, "'\\\t\r\n");
 
-  std::string YamlContent;
-  llvm::raw_string_ostream YamlContentStream(YamlContent);
+  SmallString<512> YamlContent;
+  llvm::raw_svector_ostream YamlContentStream(YamlContent);
 
   yaml::Output YAML(YamlContentStream);
   YAML << Doc;
+  yaml::Input IYAML(YamlContent);
+  IYAML >> NewDoc;
 
-  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
-  ASSERT_STREQ("---\n"
-               "MainSourceFile:  '/path/to/source.cpp'\n"
-               "Replacements:\n"
-               "  - FilePath:        '/path/to/file1.h'\n"
-               "    Offset:          0\n"
-               "    Length:          0\n"
-               "    ReplacementText: '#include <utility>\n\n'\n"
-               "...\n",
-               YamlContentStream.str().c_str());
+  ASSERT_EQ(Doc.MainSourceFile, NewDoc.MainSourceFile);
+  ASSERT_EQ(Doc.Replacements.size(), NewDoc.Replacements.size());
+  if (Doc.Replacements.size() != NewDoc.Replacements.size()) {
+    for (auto DocR = Doc.Replacements.begin(),
+              NewDocR = NewDoc.Replacements.begin(),
+              DocE = Doc.Replacements.end();
+         DocR != DocE; DocR++, NewDocR++) {
+      ASSERT_EQ(DocR->getFilePath(), NewDocR->getFilePath());
+      ASSERT_EQ(DocR->getLength(), NewDocR->getLength());
+      ASSERT_EQ(DocR->getOffset(), NewDocR->getOffset());
+      ASSERT_EQ(DocR->getReplacementText(), NewDocR->getReplacementText());
+    }
+  }
 }
 
 TEST(ReplacementsYamlTest, deserializesReplacements) {
Index: clang/include/clang/Tooling/ReplacementsYaml.h
===================================================================
--- clang/include/clang/Tooling/ReplacementsYaml.h
+++ clang/include/clang/Tooling/ReplacementsYaml.h
@@ -35,17 +35,66 @@
 
     NormalizedReplacement(const IO &, const clang::tooling::Replacement &R)
         : FilePath(R.getFilePath()), Offset(R.getOffset()),
-          Length(R.getLength()), ReplacementText(R.getReplacementText()) {
-      size_t lineBreakPos = ReplacementText.find('\n');
-      while (lineBreakPos != std::string::npos) {
-        ReplacementText.replace(lineBreakPos, 1, "\n\n");
-        lineBreakPos = ReplacementText.find('\n', lineBreakPos + 2);
+          Length(R.getLength()),
+          ReplacementText(escape(R.getReplacementText())) {}
+
+    struct Escapes {
+      char EscapeChar;
+      char WrittenChar;
+    };
+
+    std::string escape(StringRef Str) {
+      constexpr Escapes EscapeChars[] = {
+          {'\n', 'n'}, {'\r', 'r'}, {'\t', 't'}, {'\\', '\\'}};
+      std::string Res;
+      llvm::raw_string_ostream Builder(Res);
+      for (char C : Str) {
+        if (llvm::none_of(EscapeChars, [&](Escapes Escape) {
+              if (C == Escape.EscapeChar) {
+                Builder << '\\' << Escape.WrittenChar;
+                return true;
+              }
+              return false;
+            })) {
+          Builder << C;
+        }
+      }
+      return Res;
+    }
+
+    std::string unescape(StringRef Str) {
+      constexpr Escapes EscapeChars[] = {
+          {'\n', 'n'}, {'\r', 'r'}, {'\t', 't'}, {'\\', '\\'}};
+      std::string Res;
+      llvm::raw_string_ostream Builder(Res);
+      while (!Str.empty()) {
+        if (Str.consume_front("\\")) {
+          if (Str.empty()) {
+            Builder << '\\';
+            break;
+          }
+          char C = Str.front();
+          Str = Str.drop_front();
+          if (llvm::none_of(EscapeChars, [&](Escapes Escape) {
+                if (C == Escape.WrittenChar) {
+                  Builder << Escape.EscapeChar;
+                  return true;
+                }
+                return false;
+              })) {
+            Builder << '\\' << C;
+          }
+          continue;
+        }
+        Builder << Str.front();
+        Str = Str.drop_front();
       }
+      return Res;
     }
 
     clang::tooling::Replacement denormalize(const IO &) {
       return clang::tooling::Replacement(FilePath, Offset, Length,
-                                         ReplacementText);
+                                         unescape(ReplacementText));
     }
 
     std::string FilePath;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to