cameron314 updated this revision to Diff 56917.

http://reviews.llvm.org/D20125

Files:
  include/clang-c/Index.h
  tools/libclang/CXSourceLocation.cpp
  unittests/libclang/LibclangTest.cpp

Index: unittests/libclang/LibclangTest.cpp
===================================================================
--- unittests/libclang/LibclangTest.cpp
+++ unittests/libclang/LibclangTest.cpp
@@ -15,6 +15,9 @@
 #include "gtest/gtest.h"
 #include <fstream>
 #include <set>
+#include <map>
+#include <memory>
+#include <functional>
 #define DEBUG_TYPE "libclang-test"
 
 TEST(libclang, clang_parseTranslationUnit2_InvalidArgs) {
@@ -349,21 +352,25 @@
   clang_ModuleMapDescriptor_dispose(MMD);
 }
 
-class LibclangReparseTest : public ::testing::Test {
+class LibclangParseTest : public ::testing::Test {
   std::set<std::string> Files;
+  typedef std::unique_ptr<std::string> fixed_addr_string;
+  std::map<fixed_addr_string, fixed_addr_string> UnsavedFileContents;
 public:
   std::string TestDir;
   CXIndex Index;
   CXTranslationUnit ClangTU;
   unsigned TUFlags;
+  std::vector<CXUnsavedFile> UnsavedFiles;
 
   void SetUp() override {
     llvm::SmallString<256> Dir;
     ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("libclang-test", Dir));
     TestDir = Dir.str();
     TUFlags = CXTranslationUnit_DetailedPreprocessingRecord |
-              clang_defaultEditingTranslationUnitOptions();
+      clang_defaultEditingTranslationUnitOptions();
     Index = clang_createIndex(0, 0);
+    ClangTU = nullptr;
   }
   void TearDown() override {
     clang_disposeTranslationUnit(ClangTU);
@@ -384,6 +391,64 @@
     OS << Contents;
     assert(OS.good());
   }
+  void MapUnsavedFile(const char* name, const std::string &contents) {
+    auto it = UnsavedFileContents.emplace(
+        fixed_addr_string(new std::string(name)),
+        fixed_addr_string(new std::string(contents)));
+    UnsavedFiles.push_back({
+        it.first->first->c_str(),   // filename
+        it.first->second->c_str(),  // contents
+        it.first->second->size()    // length
+    });
+  }
+  template<typename F>
+  void Traverse(const F &TraversalFunctor) {
+    CXCursor TuCursor = clang_getTranslationUnitCursor(ClangTU);
+    std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor);
+    clang_visitChildren(TuCursor,
+        &TraverseStateless<std::reference_wrapper<const F>>,
+        &FunctorRef);
+  }
+
+private:
+  template<typename TState>
+  static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent,
+      CXClientData data) {
+    TState *State = static_cast<TState*>(data);
+    return State->get()(cx, parent);
+  }
+};
+
+TEST_F(LibclangParseTest, SpellingLocation) {
+  MapUnsavedFile("main.cpp",
+    "#define BANANAS 4011\n"
+    "int plu = BANANAS;");
+
+  ClangTU = clang_parseTranslationUnit(Index, "main.cpp", nullptr, 0,
+    UnsavedFiles.data(), UnsavedFiles.size(), TUFlags);
+
+  bool sawInt = false;
+  Traverse([&](CXCursor cx, CXCursor) {
+    if (cx.kind == CXCursor_IntegerLiteral) {
+      sawInt = true;
+      auto cxl = clang_getCursorLocation(cx);
+      CXFile expansionFile, spellingFile;
+      unsigned line, column, offset;
+      clang_getSpellingLocation(cxl, &expansionFile, &line, nullptr, nullptr);
+      EXPECT_EQ(2, line);  // getSpellingLocation returns expansion location
+      clang_getRealSpellingLocation(cxl, &spellingFile, &line, &column, &offset);
+      EXPECT_TRUE(clang_File_isEqual(expansionFile, spellingFile));
+      EXPECT_EQ(1, line);
+      EXPECT_EQ(17, column);
+      EXPECT_EQ(16, offset);
+    }
+    return CXChildVisit_Recurse;
+  });
+  EXPECT_TRUE(sawInt);
+}
+
+class LibclangReparseTest : public LibclangParseTest {
+public:
   void DisplayDiagnostics() {
     unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU);
     for (unsigned i = 0; i < NumDiagnostics; ++i) {
Index: tools/libclang/CXSourceLocation.cpp
===================================================================
--- tools/libclang/CXSourceLocation.cpp
+++ tools/libclang/CXSourceLocation.cpp
@@ -346,6 +346,42 @@
     *offset = FileOffset;
 }
 
+void clang_getRealSpellingLocation(CXSourceLocation location,
+                                   CXFile *file,
+                                   unsigned *line,
+                                   unsigned *column,
+                                   unsigned *offset) {
+
+  if (!isASTUnitSourceLocation(location)) {
+    CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset);
+    return;
+  }
+
+  SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
+
+  if (!location.ptr_data[0] || Loc.isInvalid())
+    return createNullLocation(file, line, column, offset);
+
+  const SourceManager &SM =
+  *static_cast<const SourceManager*>(location.ptr_data[0]);
+  SourceLocation SpellLoc = SM.getSpellingLoc(Loc);
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc);
+  FileID FID = LocInfo.first;
+  unsigned FileOffset = LocInfo.second;
+
+  if (FID.isInvalid())
+    return createNullLocation(file, line, column, offset);
+
+  if (file)
+    *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID));
+  if (line)
+    *line = SM.getLineNumber(FID, FileOffset);
+  if (column)
+    *column = SM.getColumnNumber(FID, FileOffset);
+  if (offset)
+    *offset = FileOffset;
+}
+
 void clang_getFileLocation(CXSourceLocation location,
                            CXFile *file,
                            unsigned *line,
Index: include/clang-c/Index.h
===================================================================
--- include/clang-c/Index.h
+++ include/clang-c/Index.h
@@ -568,6 +568,36 @@
  * \brief Retrieve the file, line, column, and offset represented by
  * the given source location.
  *
+ * If the location refers into a macro instantiation, return where the
+ * location was originally spelled in the source file (i.e. where the token
+ * text is located). clang_getSpellingLocation() is supposed to do this
+ * but doesn't, hence this function.
+ *
+ * \param location the location within a source file that will be decomposed
+ * into its parts.
+ *
+ * \param file [out] if non-NULL, will be set to the file to which the given
+ * source location points.
+ *
+ * \param line [out] if non-NULL, will be set to the line to which the given
+ * source location points.
+ *
+ * \param column [out] if non-NULL, will be set to the column to which the given
+ * source location points.
+ *
+ * \param offset [out] if non-NULL, will be set to the offset into the
+ * buffer to which the given source location points.
+ */
+CINDEX_LINKAGE void clang_getRealSpellingLocation(CXSourceLocation location,
+                                                  CXFile *file,
+                                                  unsigned *line,
+                                                  unsigned *column,
+                                                  unsigned *offset);
+
+/**
+ * \brief Retrieve the file, line, column, and offset represented by
+ * the given source location.
+ *
  * If the location refers into a macro expansion, return where the macro was
  * expanded or where the macro argument was written, if the location points at
  * a macro argument.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to