]> granicus.if.org Git - clang/commitdiff
Adding Replacement serialization support
authorEdwin Vane <edwin.vane@intel.com>
Tue, 20 Aug 2013 19:07:21 +0000 (19:07 +0000)
committerEdwin Vane <edwin.vane@intel.com>
Tue, 20 Aug 2013 19:07:21 +0000 (19:07 +0000)
Adding a new data structure for storing the Replacements generated for a single
translation unit. Structure contains a vector of Replacements as well a field
indicating the main source file of the translation unit. An optional 'Context'
field allows for tools to provide any information they want about the context
the Replacements were generated in. This context is printed, for example, when
detecting conflicts during Replacement deduplication.

YAML serialization for this data structure is implemented in this patch. Tests
are included.

Differential Revision: http://llvm-reviews.chandlerc.com/D1422

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188818 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Tooling/Refactoring.h
include/clang/Tooling/ReplacementsYaml.h [new file with mode: 0644]
unittests/Tooling/CMakeLists.txt
unittests/Tooling/ReplacementsYamlTest.cpp [new file with mode: 0644]

index f5b621154f29c9e0b72ca85009bcd5ec3e051b8f..aef0208771847d82534a2ffee27cdf236b0c16a9 100644 (file)
@@ -170,6 +170,19 @@ unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position);
 void deduplicate(std::vector<Replacement> &Replaces,
                  std::vector<Range> &Conflicts);
 
+/// \brief Collection of Replacements generated from a single translation unit.
+struct TranslationUnitReplacements {
+  /// Name of the main source for the translation unit.
+  std::string MainSourceFile;
+
+  /// A freeform chunk of text to describe the context of the replacements.
+  /// Will be printed, for example, when detecting conflicts during replacement
+  /// deduplication.
+  std::string Context;
+
+  std::vector<Replacement> Replacements;
+};
+
 /// \brief A tool to run refactorings.
 ///
 /// This is a refactoring specific version of \see ClangTool. FrontendActions
diff --git a/include/clang/Tooling/ReplacementsYaml.h b/include/clang/Tooling/ReplacementsYaml.h
new file mode 100644 (file)
index 0000000..0bef5ed
--- /dev/null
@@ -0,0 +1,91 @@
+//===-- ReplacementsYaml.h -- Serialiazation for Replacements ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file defines the structure of a YAML document for serializing
+/// replacements.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REPLACEMENTS_YAML_H
+#define LLVM_CLANG_TOOLING_REPLACEMENTS_YAML_H
+
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <vector>
+#include <string>
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::Replacement)
+
+namespace llvm {
+namespace yaml {
+
+/// \brief ScalarTraits to read/write std::string objects.
+template <> struct ScalarTraits<std::string> {
+  static void output(const std::string &Val, void *, llvm::raw_ostream &Out) {
+    // We need to put quotes around the string to make sure special characters
+    // in the string is not treated as YAML tokens.
+    std::string NormalizedVal = std::string("\"") + Val + std::string("\"");
+    Out << NormalizedVal;
+  }
+
+  static StringRef input(StringRef Scalar, void *, std::string &Val) {
+    Val = Scalar;
+    return StringRef();
+  }
+};
+
+/// \brief Specialized MappingTraits to describe how a Replacement is
+/// (de)serialized.
+template <> struct MappingTraits<clang::tooling::Replacement> {
+  /// \brief Helper to (de)serialize a Replacement since we don't have direct
+  /// access to its data members.
+  struct NormalizedReplacement {
+    NormalizedReplacement(const IO &)
+        : FilePath(""), Offset(0), Length(0), ReplacementText("") {}
+
+    NormalizedReplacement(const IO &, const clang::tooling::Replacement &R)
+        : FilePath(R.getFilePath()), Offset(R.getOffset()),
+          Length(R.getLength()), ReplacementText(R.getReplacementText()) {}
+
+    clang::tooling::Replacement denormalize(const IO &) {
+      return clang::tooling::Replacement(FilePath, Offset, Length,
+                                         ReplacementText);
+    }
+
+    std::string FilePath;
+    unsigned int Offset;
+    unsigned int Length;
+    std::string ReplacementText;
+  };
+
+  static void mapping(IO &Io, clang::tooling::Replacement &R) {
+    MappingNormalization<NormalizedReplacement, clang::tooling::Replacement>
+    Keys(Io, R);
+    Io.mapRequired("FilePath", Keys->FilePath);
+    Io.mapRequired("Offset", Keys->Offset);
+    Io.mapRequired("Length", Keys->Length);
+    Io.mapRequired("ReplacementText", Keys->ReplacementText);
+  }
+};
+
+/// \brief Specialized MappingTraits to describe how a
+/// TranslationUnitReplacements is (de)serialized.
+template <> struct MappingTraits<clang::tooling::TranslationUnitReplacements> {
+  static void mapping(IO &Io,
+                      clang::tooling::TranslationUnitReplacements &Doc) {
+    Io.mapRequired("MainSourceFile", Doc.MainSourceFile);
+    Io.mapOptional("Context", Doc.Context, std::string());
+    Io.mapRequired("Replacements", Doc.Replacements);
+  }
+};
+} // end namespace yaml
+} // end namespace llvm
+
+#endif // LLVM_CLANG_TOOLING_REPLACEMENTS_YAML_H
index 245c0599d4270c20e40b85e6c065f7962b115720..33d761700738327af38a20e28df6ed96bac45963 100644 (file)
@@ -14,6 +14,7 @@ add_clang_unittest(ToolingTests
   RefactoringTest.cpp
   RewriterTest.cpp
   RefactoringCallbacksTest.cpp
+  ReplacementsYamlTest.cpp
   )
 
 target_link_libraries(ToolingTests
diff --git a/unittests/Tooling/ReplacementsYamlTest.cpp b/unittests/Tooling/ReplacementsYamlTest.cpp
new file mode 100644 (file)
index 0000000..627002a
--- /dev/null
@@ -0,0 +1,106 @@
+//===- unittests/Tooling/ReplacementsYamlTest.cpp - Serialization tests ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for serialization of Replacements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang::tooling;
+
+TEST(ReplacementsYamlTest, serializesReplacements) {
+
+  TranslationUnitReplacements Doc;
+
+  Doc.MainSourceFile = "/path/to/source.cpp";
+  Doc.Context = "some context";
+  Doc.Replacements
+      .push_back(Replacement("/path/to/file1.h", 232, 56, "replacement #1"));
+  Doc.Replacements
+      .push_back(Replacement("/path/to/file2.h", 301, 2, "replacement #2"));
+
+  std::string YamlContent;
+  llvm::raw_string_ostream YamlContentStream(YamlContent);
+
+  yaml::Output YAML(YamlContentStream);
+  YAML << Doc;
+
+  // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+  ASSERT_STREQ("---\n"
+               "MainSourceFile:  \"/path/to/source.cpp\"\n"
+               "Context:         \"some context\"\n"
+               "Replacements:    \n" // Extra whitespace here!
+               "  - FilePath:        \"/path/to/file1.h\"\n"
+               "    Offset:          232\n"
+               "    Length:          56\n"
+               "    ReplacementText: \"replacement #1\"\n"
+               "  - FilePath:        \"/path/to/file2.h\"\n"
+               "    Offset:          301\n"
+               "    Length:          2\n"
+               "    ReplacementText: \"replacement #2\"\n"
+               "...\n",
+               YamlContentStream.str().c_str());
+}
+
+TEST(ReplacementsYamlTest, deserializesReplacements) {
+  std::string YamlContent = "---\n"
+                            "MainSourceFile:      \"/path/to/source.cpp\"\n"
+                            "Context:             \"some context\"\n"
+                            "Replacements:\n"
+                            "  - FilePath:        \"/path/to/file1.h\"\n"
+                            "    Offset:          232\n"
+                            "    Length:          56\n"
+                            "    ReplacementText: \"replacement #1\"\n"
+                            "  - FilePath:        \"/path/to/file2.h\"\n"
+                            "    Offset:          301\n"
+                            "    Length:          2\n"
+                            "    ReplacementText: \"replacement #2\"\n"
+                            "...\n";
+  TranslationUnitReplacements DocActual;
+  yaml::Input YAML(YamlContent);
+  YAML >> DocActual;
+  ASSERT_FALSE(YAML.error());
+  ASSERT_EQ(2u, DocActual.Replacements.size());
+  ASSERT_EQ("/path/to/source.cpp", DocActual.MainSourceFile);
+  ASSERT_EQ("some context", DocActual.Context);
+  ASSERT_EQ("/path/to/file1.h", DocActual.Replacements[0].getFilePath());
+  ASSERT_EQ(232u, DocActual.Replacements[0].getOffset());
+  ASSERT_EQ(56u, DocActual.Replacements[0].getLength());
+  ASSERT_EQ("replacement #1", DocActual.Replacements[0].getReplacementText());
+  ASSERT_EQ("/path/to/file2.h", DocActual.Replacements[1].getFilePath());
+  ASSERT_EQ(301u, DocActual.Replacements[1].getOffset());
+  ASSERT_EQ(2u, DocActual.Replacements[1].getLength());
+  ASSERT_EQ("replacement #2", DocActual.Replacements[1].getReplacementText());
+}
+
+TEST(ReplacementsYamlTest, deserializesWithoutContext) {
+  // Make sure a doc can be read without the context field.
+  std::string YamlContent = "---\n"
+                            "MainSourceFile:      \"/path/to/source.cpp\"\n"
+                            "Replacements:\n"
+                            "  - FilePath:        \"target_file.h\"\n"
+                            "    Offset:          1\n"
+                            "    Length:          10\n"
+                            "    ReplacementText: \"replacement\"\n"
+                            "...\n";
+  TranslationUnitReplacements DocActual;
+  yaml::Input YAML(YamlContent);
+  YAML >> DocActual;
+  ASSERT_FALSE(YAML.error());
+  ASSERT_EQ("/path/to/source.cpp", DocActual.MainSourceFile);
+  ASSERT_EQ(1u, DocActual.Replacements.size());
+  ASSERT_EQ(std::string(), DocActual.Context);
+  ASSERT_EQ("target_file.h", DocActual.Replacements[0].getFilePath());
+  ASSERT_EQ(1u, DocActual.Replacements[0].getOffset());
+  ASSERT_EQ(10u, DocActual.Replacements[0].getLength());
+  ASSERT_EQ("replacement", DocActual.Replacements[0].getReplacementText());
+}