mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-19 11:35:51 -04:00

Partially reverts 0a2be46cfd
as it turned
out to cause redundant module rebuilds in multi-process incremental builds.
When a module was getting out of date, all compilation processes started at the
same time were marking it as `ToBuild`. So each process was building the same
module instead of checking if it was built by someone else and using that
result. In addition to the work duplication, contention on the same .pcm file
wasn't making builds faster.
Note that for a single-process build this change would cause redundant module
reads and validations. But reading a module is faster than building it and
multi-process builds are more common than single-process. So I'm willing to
make such a trade-off.
rdar://problem/54395127
Reviewed By: dexonsmith
Differential Revision: https://reviews.llvm.org/D72860
294 lines
11 KiB
C++
294 lines
11 KiB
C++
//===- unittests/Frontend/FrontendActionTest.cpp - FrontendAction 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/Frontend/FrontendAction.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Basic/LangStandard.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Serialization/InMemoryModuleCache.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
|
|
class TestASTFrontendAction : public ASTFrontendAction {
|
|
public:
|
|
TestASTFrontendAction(bool enableIncrementalProcessing = false,
|
|
bool actOnEndOfTranslationUnit = false)
|
|
: EnableIncrementalProcessing(enableIncrementalProcessing),
|
|
ActOnEndOfTranslationUnit(actOnEndOfTranslationUnit) { }
|
|
|
|
bool EnableIncrementalProcessing;
|
|
bool ActOnEndOfTranslationUnit;
|
|
std::vector<std::string> decl_names;
|
|
|
|
bool BeginSourceFileAction(CompilerInstance &ci) override {
|
|
if (EnableIncrementalProcessing)
|
|
ci.getPreprocessor().enableIncrementalProcessing();
|
|
|
|
return ASTFrontendAction::BeginSourceFileAction(ci);
|
|
}
|
|
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
|
StringRef InFile) override {
|
|
return std::make_unique<Visitor>(CI, ActOnEndOfTranslationUnit,
|
|
decl_names);
|
|
}
|
|
|
|
private:
|
|
class Visitor : public ASTConsumer, public RecursiveASTVisitor<Visitor> {
|
|
public:
|
|
Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit,
|
|
std::vector<std::string> &decl_names) :
|
|
CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit),
|
|
decl_names_(decl_names) {}
|
|
|
|
void HandleTranslationUnit(ASTContext &context) override {
|
|
if (ActOnEndOfTranslationUnit) {
|
|
CI.getSema().ActOnEndOfTranslationUnit();
|
|
}
|
|
TraverseDecl(context.getTranslationUnitDecl());
|
|
}
|
|
|
|
virtual bool VisitNamedDecl(NamedDecl *Decl) {
|
|
decl_names_.push_back(Decl->getQualifiedNameAsString());
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
CompilerInstance &CI;
|
|
bool ActOnEndOfTranslationUnit;
|
|
std::vector<std::string> &decl_names_;
|
|
};
|
|
};
|
|
|
|
TEST(ASTFrontendAction, Sanity) {
|
|
auto invocation = std::make_shared<CompilerInvocation>();
|
|
invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.cc",
|
|
MemoryBuffer::getMemBuffer("int main() { float x; }").release());
|
|
invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.cc", Language::CXX));
|
|
invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
|
|
invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
|
|
CompilerInstance compiler;
|
|
compiler.setInvocation(std::move(invocation));
|
|
compiler.createDiagnostics();
|
|
|
|
TestASTFrontendAction test_action;
|
|
ASSERT_TRUE(compiler.ExecuteAction(test_action));
|
|
ASSERT_EQ(2U, test_action.decl_names.size());
|
|
EXPECT_EQ("main", test_action.decl_names[0]);
|
|
EXPECT_EQ("x", test_action.decl_names[1]);
|
|
}
|
|
|
|
TEST(ASTFrontendAction, IncrementalParsing) {
|
|
auto invocation = std::make_shared<CompilerInvocation>();
|
|
invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.cc",
|
|
MemoryBuffer::getMemBuffer("int main() { float x; }").release());
|
|
invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.cc", Language::CXX));
|
|
invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
|
|
invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
|
|
CompilerInstance compiler;
|
|
compiler.setInvocation(std::move(invocation));
|
|
compiler.createDiagnostics();
|
|
|
|
TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true);
|
|
ASSERT_TRUE(compiler.ExecuteAction(test_action));
|
|
ASSERT_EQ(2U, test_action.decl_names.size());
|
|
EXPECT_EQ("main", test_action.decl_names[0]);
|
|
EXPECT_EQ("x", test_action.decl_names[1]);
|
|
}
|
|
|
|
TEST(ASTFrontendAction, LateTemplateIncrementalParsing) {
|
|
auto invocation = std::make_shared<CompilerInvocation>();
|
|
invocation->getLangOpts()->CPlusPlus = true;
|
|
invocation->getLangOpts()->DelayedTemplateParsing = true;
|
|
invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.cc", MemoryBuffer::getMemBuffer(
|
|
"template<typename T> struct A { A(T); T data; };\n"
|
|
"template<typename T> struct B: public A<T> {\n"
|
|
" B();\n"
|
|
" B(B const& b): A<T>(b.data) {}\n"
|
|
"};\n"
|
|
"B<char> c() { return B<char>(); }\n").release());
|
|
invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.cc", Language::CXX));
|
|
invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
|
|
invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
|
|
CompilerInstance compiler;
|
|
compiler.setInvocation(std::move(invocation));
|
|
compiler.createDiagnostics();
|
|
|
|
TestASTFrontendAction test_action(/*enableIncrementalProcessing=*/true,
|
|
/*actOnEndOfTranslationUnit=*/true);
|
|
ASSERT_TRUE(compiler.ExecuteAction(test_action));
|
|
ASSERT_EQ(13U, test_action.decl_names.size());
|
|
EXPECT_EQ("A", test_action.decl_names[0]);
|
|
EXPECT_EQ("c", test_action.decl_names[12]);
|
|
}
|
|
|
|
struct TestPPCallbacks : public PPCallbacks {
|
|
TestPPCallbacks() : SeenEnd(false) {}
|
|
|
|
void EndOfMainFile() override { SeenEnd = true; }
|
|
|
|
bool SeenEnd;
|
|
};
|
|
|
|
class TestPPCallbacksFrontendAction : public PreprocessorFrontendAction {
|
|
TestPPCallbacks *Callbacks;
|
|
|
|
public:
|
|
TestPPCallbacksFrontendAction(TestPPCallbacks *C)
|
|
: Callbacks(C), SeenEnd(false) {}
|
|
|
|
void ExecuteAction() override {
|
|
Preprocessor &PP = getCompilerInstance().getPreprocessor();
|
|
PP.addPPCallbacks(std::unique_ptr<TestPPCallbacks>(Callbacks));
|
|
PP.EnterMainSourceFile();
|
|
}
|
|
void EndSourceFileAction() override { SeenEnd = Callbacks->SeenEnd; }
|
|
|
|
bool SeenEnd;
|
|
};
|
|
|
|
TEST(PreprocessorFrontendAction, EndSourceFile) {
|
|
auto Invocation = std::make_shared<CompilerInvocation>();
|
|
Invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.cc",
|
|
MemoryBuffer::getMemBuffer("int main() { float x; }").release());
|
|
Invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.cc", Language::CXX));
|
|
Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
|
|
Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
|
|
CompilerInstance Compiler;
|
|
Compiler.setInvocation(std::move(Invocation));
|
|
Compiler.createDiagnostics();
|
|
|
|
TestPPCallbacks *Callbacks = new TestPPCallbacks;
|
|
TestPPCallbacksFrontendAction TestAction(Callbacks);
|
|
ASSERT_FALSE(Callbacks->SeenEnd);
|
|
ASSERT_FALSE(TestAction.SeenEnd);
|
|
ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
|
|
// Check that EndOfMainFile was called before EndSourceFileAction.
|
|
ASSERT_TRUE(TestAction.SeenEnd);
|
|
}
|
|
|
|
class TypoExternalSemaSource : public ExternalSemaSource {
|
|
CompilerInstance &CI;
|
|
|
|
public:
|
|
TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {}
|
|
|
|
TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
|
|
Scope *S, CXXScopeSpec *SS,
|
|
CorrectionCandidateCallback &CCC,
|
|
DeclContext *MemberContext, bool EnteringContext,
|
|
const ObjCObjectPointerType *OPT) override {
|
|
// Generate a fake typo correction with one attached note.
|
|
ASTContext &Ctx = CI.getASTContext();
|
|
TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo")));
|
|
unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
|
|
DiagnosticsEngine::Note, "This is a note");
|
|
TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator()));
|
|
return TC;
|
|
}
|
|
};
|
|
|
|
struct TypoDiagnosticConsumer : public DiagnosticConsumer {
|
|
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
|
const Diagnostic &Info) override {
|
|
// Capture errors and notes. There should be one of each.
|
|
if (DiagLevel == DiagnosticsEngine::Error) {
|
|
assert(Error.empty());
|
|
Info.FormatDiagnostic(Error);
|
|
} else {
|
|
assert(Note.empty());
|
|
Info.FormatDiagnostic(Note);
|
|
}
|
|
}
|
|
SmallString<32> Error;
|
|
SmallString<32> Note;
|
|
};
|
|
|
|
TEST(ASTFrontendAction, ExternalSemaSource) {
|
|
auto Invocation = std::make_shared<CompilerInvocation>();
|
|
Invocation->getLangOpts()->CPlusPlus = true;
|
|
Invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.cc", MemoryBuffer::getMemBuffer("void fooo();\n"
|
|
"int main() { foo(); }")
|
|
.release());
|
|
Invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.cc", Language::CXX));
|
|
Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
|
|
Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
|
|
CompilerInstance Compiler;
|
|
Compiler.setInvocation(std::move(Invocation));
|
|
auto *TDC = new TypoDiagnosticConsumer;
|
|
Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true);
|
|
Compiler.setExternalSemaSource(new TypoExternalSemaSource(Compiler));
|
|
|
|
SyntaxOnlyAction TestAction;
|
|
ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
|
|
// There should be one error correcting to 'moo' and a note attached to it.
|
|
EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?",
|
|
TDC->Error.str().str());
|
|
EXPECT_EQ("This is a note", TDC->Note.str().str());
|
|
}
|
|
|
|
TEST(GeneratePCHFrontendAction, CacheGeneratedPCH) {
|
|
// Create a temporary file for writing out the PCH that will be cleaned up.
|
|
int PCHFD;
|
|
llvm::SmallString<128> PCHFilename;
|
|
ASSERT_FALSE(
|
|
llvm::sys::fs::createTemporaryFile("test.h", "pch", PCHFD, PCHFilename));
|
|
llvm::ToolOutputFile PCHFile(PCHFilename, PCHFD);
|
|
|
|
for (bool ShouldCache : {false, true}) {
|
|
auto Invocation = std::make_shared<CompilerInvocation>();
|
|
Invocation->getLangOpts()->CacheGeneratedPCH = ShouldCache;
|
|
Invocation->getPreprocessorOpts().addRemappedFile(
|
|
"test.h",
|
|
MemoryBuffer::getMemBuffer("int foo(void) { return 1; }\n").release());
|
|
Invocation->getFrontendOpts().Inputs.push_back(
|
|
FrontendInputFile("test.h", Language::C));
|
|
Invocation->getFrontendOpts().OutputFile = StringRef(PCHFilename);
|
|
Invocation->getFrontendOpts().ProgramAction = frontend::GeneratePCH;
|
|
Invocation->getTargetOpts().Triple = "x86_64-apple-darwin19.0.0";
|
|
CompilerInstance Compiler;
|
|
Compiler.setInvocation(std::move(Invocation));
|
|
Compiler.createDiagnostics();
|
|
|
|
GeneratePCHAction TestAction;
|
|
ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
|
|
|
|
// Check whether the PCH was cached.
|
|
if (ShouldCache)
|
|
EXPECT_TRUE(Compiler.getModuleCache().isPCMFinal(PCHFilename));
|
|
else
|
|
EXPECT_EQ(nullptr, Compiler.getModuleCache().lookupPCM(PCHFilename));
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|