diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 170e4e7fad5f70ac31114f228e4d87878c484f12..192560e9d51683e99df5b7b650ac75443b3fd7d0 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -626,6 +626,15 @@ typedef struct { CINDEX_LINKAGE CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit tu, CXFile file); +/** + * \brief Retrieve all ranges from all files that were skipped by the + * preprocessor. + * + * The preprocessor will skip lines when they are surrounded by an + * if/ifdef/ifndef directive whose condition does not evaluate to true. + */ +CINDEX_LINKAGE CXSourceRangeList *clang_getAllSkippedRanges(CXTranslationUnit tu); + /** * \brief Destroy the given \c CXSourceRangeList. */ diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 4a359299bdd07d6ce53d4a4a68859b3f8e477c84..22a17adea12adfc93da1f11451d066dd05ad3fab 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -7773,6 +7773,33 @@ CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) { return skipped; } +CXSourceRangeList *clang_getAllSkippedRanges(CXTranslationUnit TU) { + CXSourceRangeList *skipped = new CXSourceRangeList; + skipped->count = 0; + skipped->ranges = nullptr; + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return skipped; + } + + ASTUnit *astUnit = cxtu::getASTUnit(TU); + PreprocessingRecord *ppRec = astUnit->getPreprocessor().getPreprocessingRecord(); + if (!ppRec) + return skipped; + + ASTContext &Ctx = astUnit->getASTContext(); + + const std::vector<SourceRange> &SkippedRanges = ppRec->getSkippedRanges(); + + skipped->count = SkippedRanges.size(); + skipped->ranges = new CXSourceRange[skipped->count]; + for (unsigned i = 0, ei = skipped->count; i != ei; ++i) + skipped->ranges[i] = cxloc::translateSourceRange(Ctx, SkippedRanges[i]); + + return skipped; +} + void clang_disposeSourceRangeList(CXSourceRangeList *ranges) { if (ranges) { delete[] ranges->ranges; diff --git a/unittests/libclang/LibclangTest.cpp b/unittests/libclang/LibclangTest.cpp index d5c78272af5478af903e0aed4dd04cacdaf575ee..78229a18412e835b48a3e686b8fe721da8d57481 100644 --- a/unittests/libclang/LibclangTest.cpp +++ b/unittests/libclang/LibclangTest.cpp @@ -14,6 +14,9 @@ #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include <fstream> +#include <functional> +#include <map> +#include <memory> #include <set> #define DEBUG_TYPE "libclang-test" @@ -349,21 +352,25 @@ TEST(libclang, ModuleMapDescriptor) { 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,77 @@ public: OS << Contents; assert(OS.good()); } + void MapUnsavedFile(std::string Filename, const std::string &Contents) { + if (!llvm::sys::path::is_absolute(Filename)) { + llvm::SmallString<256> Path(TestDir); + llvm::sys::path::append(Path, Filename); + Filename = Path.str(); + } + auto it = UnsavedFileContents.emplace( + fixed_addr_string(new std::string(Filename)), + 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, AllSkippedRanges) { + std::string Header = "header.h", Main = "main.cpp"; + WriteFile(Header, + "#ifdef MANGOS\n" + "printf(\"mmm\");\n" + "#endif"); + WriteFile(Main, + "#include \"header.h\"\n" + "#ifdef KIWIS\n" + "printf(\"mmm!!\");\n" + "#endif"); + + ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, + nullptr, 0, TUFlags); + + CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU); + EXPECT_EQ(2, Ranges->count); + + CXSourceLocation cxl; + unsigned line; + cxl = clang_getRangeStart(Ranges->ranges[0]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(1, line); + cxl = clang_getRangeEnd(Ranges->ranges[0]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(3, line); + + cxl = clang_getRangeStart(Ranges->ranges[1]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(2, line); + cxl = clang_getRangeEnd(Ranges->ranges[1]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(4, line); + + clang_disposeSourceRangeList(Ranges); +} + +class LibclangReparseTest : public LibclangParseTest { +public: void DisplayDiagnostics() { unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU); for (unsigned i = 0; i < NumDiagnostics; ++i) {