forked from OSchip/llvm-project
198 lines
6.9 KiB
C++
198 lines
6.9 KiB
C++
//===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/CrossTU/CrossTranslationUnit.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/Frontend/FrontendAction.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include "gtest/gtest.h"
|
|
#include <cassert>
|
|
|
|
namespace clang {
|
|
namespace cross_tu {
|
|
|
|
namespace {
|
|
|
|
class CTUASTConsumer : public clang::ASTConsumer {
|
|
public:
|
|
explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)
|
|
: CTU(CI), Success(Success) {}
|
|
|
|
void HandleTranslationUnit(ASTContext &Ctx) {
|
|
auto FindFInTU = [](const TranslationUnitDecl *TU) {
|
|
const FunctionDecl *FD = nullptr;
|
|
for (const Decl *D : TU->decls()) {
|
|
FD = dyn_cast<FunctionDecl>(D);
|
|
if (FD && FD->getName() == "f")
|
|
break;
|
|
}
|
|
return FD;
|
|
};
|
|
|
|
const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
|
|
const FunctionDecl *FD = FindFInTU(TU);
|
|
assert(FD && FD->getName() == "f");
|
|
bool OrigFDHasBody = FD->hasBody();
|
|
|
|
// Prepare the index file and the AST file.
|
|
int ASTFD;
|
|
llvm::SmallString<256> ASTFileName;
|
|
ASSERT_FALSE(
|
|
llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName));
|
|
llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
|
|
|
|
int IndexFD;
|
|
llvm::SmallString<256> IndexFileName;
|
|
ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
|
|
IndexFileName));
|
|
llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
|
|
IndexFile.os() << "c:@F@f#I# " << ASTFileName << "\n";
|
|
IndexFile.os().flush();
|
|
EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
|
|
|
|
StringRef SourceText = "int f(int) { return 0; }\n";
|
|
// This file must exist since the saved ASTFile will reference it.
|
|
int SourceFD;
|
|
llvm::SmallString<256> SourceFileName;
|
|
ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD,
|
|
SourceFileName));
|
|
llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
|
|
SourceFile.os() << SourceText;
|
|
SourceFile.os().flush();
|
|
EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));
|
|
|
|
std::unique_ptr<ASTUnit> ASTWithDefinition =
|
|
tooling::buildASTFromCode(SourceText, SourceFileName);
|
|
ASTWithDefinition->Save(ASTFileName.str());
|
|
EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
|
|
|
|
// Load the definition from the AST file.
|
|
llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
|
|
CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
|
|
[]() { return nullptr; }, [](IndexError &) {});
|
|
|
|
if (NewFDorError) {
|
|
const FunctionDecl *NewFD = *NewFDorError;
|
|
*Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
|
|
|
|
if (NewFD) {
|
|
// Check GetImportedFromSourceLocation.
|
|
llvm::Optional<std::pair<SourceLocation, ASTUnit *>> SLocResult =
|
|
CTU.getImportedFromSourceLocation(NewFD->getLocation());
|
|
EXPECT_TRUE(SLocResult);
|
|
if (SLocResult) {
|
|
SourceLocation OrigSLoc = (*SLocResult).first;
|
|
ASTUnit *OrigUnit = (*SLocResult).second;
|
|
// OrigUnit is created internally by CTU (is not the
|
|
// ASTWithDefinition).
|
|
TranslationUnitDecl *OrigTU =
|
|
OrigUnit->getASTContext().getTranslationUnitDecl();
|
|
const FunctionDecl *FDWithDefinition = FindFInTU(OrigTU);
|
|
EXPECT_TRUE(FDWithDefinition);
|
|
if (FDWithDefinition) {
|
|
EXPECT_EQ(FDWithDefinition->getName(), "f");
|
|
EXPECT_TRUE(FDWithDefinition->isThisDeclarationADefinition());
|
|
EXPECT_EQ(OrigSLoc, FDWithDefinition->getLocation());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
CrossTranslationUnitContext CTU;
|
|
bool *Success;
|
|
};
|
|
|
|
class CTUAction : public clang::ASTFrontendAction {
|
|
public:
|
|
CTUAction(bool *Success, unsigned OverrideLimit)
|
|
: Success(Success), OverrideLimit(OverrideLimit) {}
|
|
|
|
protected:
|
|
std::unique_ptr<clang::ASTConsumer>
|
|
CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
|
|
CI.getAnalyzerOpts()->CTUImportThreshold = OverrideLimit;
|
|
return std::make_unique<CTUASTConsumer>(CI, Success);
|
|
}
|
|
|
|
private:
|
|
bool *Success;
|
|
const unsigned OverrideLimit;
|
|
};
|
|
|
|
} // end namespace
|
|
|
|
TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
|
|
bool Success = false;
|
|
EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),
|
|
"int f(int);"));
|
|
EXPECT_TRUE(Success);
|
|
}
|
|
|
|
TEST(CrossTranslationUnit, RespectsLoadThreshold) {
|
|
bool Success = false;
|
|
EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),
|
|
"int f(int);"));
|
|
EXPECT_FALSE(Success);
|
|
}
|
|
|
|
TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
|
|
llvm::StringMap<std::string> Index;
|
|
Index["a"] = "/b/f1";
|
|
Index["c"] = "/d/f2";
|
|
Index["e"] = "/f/f3";
|
|
std::string IndexText = createCrossTUIndexString(Index);
|
|
|
|
int IndexFD;
|
|
llvm::SmallString<256> IndexFileName;
|
|
ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
|
|
IndexFileName));
|
|
llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
|
|
IndexFile.os() << IndexText;
|
|
IndexFile.os().flush();
|
|
EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
|
|
llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
|
|
parseCrossTUIndex(IndexFileName, "");
|
|
EXPECT_TRUE((bool)IndexOrErr);
|
|
llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
|
|
for (const auto &E : Index) {
|
|
EXPECT_TRUE(ParsedIndex.count(E.getKey()));
|
|
EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());
|
|
}
|
|
for (const auto &E : ParsedIndex)
|
|
EXPECT_TRUE(Index.count(E.getKey()));
|
|
}
|
|
|
|
TEST(CrossTranslationUnit, CTUDirIsHandledCorrectly) {
|
|
llvm::StringMap<std::string> Index;
|
|
Index["a"] = "/b/c/d";
|
|
std::string IndexText = createCrossTUIndexString(Index);
|
|
|
|
int IndexFD;
|
|
llvm::SmallString<256> IndexFileName;
|
|
ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
|
|
IndexFileName));
|
|
llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
|
|
IndexFile.os() << IndexText;
|
|
IndexFile.os().flush();
|
|
EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
|
|
llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
|
|
parseCrossTUIndex(IndexFileName, "/ctudir");
|
|
EXPECT_TRUE((bool)IndexOrErr);
|
|
llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
|
|
EXPECT_EQ(ParsedIndex["a"], "/ctudir/b/c/d");
|
|
}
|
|
|
|
} // end namespace cross_tu
|
|
} // end namespace clang
|