Skip to content

Commit

Permalink
[libclang] Add clang_getAllSkippedRanges function
Browse files Browse the repository at this point in the history
This complements the clang_getSkippedRanges function which returns skipped ranges filtered by a specific file.

This function is useful when all the ranges are desired (and a lot more efficient than the equivalent of asking for the ranges file by file, since the implementation of clang_getSkippedRanges iterates over all ranges anyway).

Differential Revision: https://reviews.llvm.org/D20132

llvm-svn: 279076
  • Loading branch information
cameron314 committed Aug 18, 2016
1 parent ccdceda commit d809128
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
9 changes: 9 additions & 0 deletions clang/include/clang-c/Index.h
Expand Up @@ -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.
*/
Expand Down
27 changes: 27 additions & 0 deletions clang/tools/libclang/CIndex.cpp
Expand Up @@ -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;
Expand Down
82 changes: 80 additions & 2 deletions clang/unittests/libclang/LibclangTest.cpp
Expand Up @@ -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"

Expand Down Expand Up @@ -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);
Expand All @@ -384,6 +391,77 @@ class LibclangReparseTest : public ::testing::Test {
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) {
Expand Down

0 comments on commit d809128

Please sign in to comment.