
There is a discrepancy between how clangd processes CDB loaded from JSON file on disk and pushed via LSP. Thus the same CDB pushed via LSP protocol may not work as expected. Some difference between these two paths is expected but we still need to insert driver mode and target from binary name and expand response files. Test Plan: check-clang-tools Differential Revision: https://reviews.llvm.org/D143436
1004 lines
37 KiB
C++
1004 lines
37 KiB
C++
//===- unittest/Tooling/ToolingTest.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/Tooling/Tooling.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclGroup.h"
|
|
#include "clang/Driver/Compilation.h"
|
|
#include "clang/Driver/Driver.h"
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendAction.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Frontend/TextDiagnosticBuffer.h"
|
|
#include "clang/Testing/CommandLineArgs.h"
|
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/TargetParser/Host.h"
|
|
#include "gtest/gtest.h"
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
namespace tooling {
|
|
|
|
namespace {
|
|
/// Takes an ast consumer and returns it from CreateASTConsumer. This only
|
|
/// works with single translation unit compilations.
|
|
class TestAction : public clang::ASTFrontendAction {
|
|
public:
|
|
/// Takes ownership of TestConsumer.
|
|
explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
|
|
: TestConsumer(std::move(TestConsumer)) {}
|
|
|
|
protected:
|
|
std::unique_ptr<clang::ASTConsumer>
|
|
CreateASTConsumer(clang::CompilerInstance &compiler,
|
|
StringRef dummy) override {
|
|
/// TestConsumer will be deleted by the framework calling us.
|
|
return std::move(TestConsumer);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<clang::ASTConsumer> TestConsumer;
|
|
};
|
|
|
|
class FindTopLevelDeclConsumer : public clang::ASTConsumer {
|
|
public:
|
|
explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
|
|
: FoundTopLevelDecl(FoundTopLevelDecl) {}
|
|
bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
|
|
*FoundTopLevelDecl = true;
|
|
return true;
|
|
}
|
|
private:
|
|
bool * const FoundTopLevelDecl;
|
|
};
|
|
} // end namespace
|
|
|
|
TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
|
|
bool FoundTopLevelDecl = false;
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<TestAction>(
|
|
std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)),
|
|
""));
|
|
EXPECT_FALSE(FoundTopLevelDecl);
|
|
}
|
|
|
|
namespace {
|
|
class FindClassDeclXConsumer : public clang::ASTConsumer {
|
|
public:
|
|
FindClassDeclXConsumer(bool *FoundClassDeclX)
|
|
: FoundClassDeclX(FoundClassDeclX) {}
|
|
bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
|
|
if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
|
|
*GroupRef.begin())) {
|
|
if (Record->getName() == "X") {
|
|
*FoundClassDeclX = true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
bool *FoundClassDeclX;
|
|
};
|
|
bool FindClassDeclX(ASTUnit *AST) {
|
|
for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
|
|
e = AST->top_level_end();
|
|
i != e; ++i) {
|
|
if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
|
|
if (Record->getName() == "X") {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct TestDiagnosticConsumer : public DiagnosticConsumer {
|
|
TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
|
|
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
|
const Diagnostic &Info) override {
|
|
++NumDiagnosticsSeen;
|
|
}
|
|
unsigned NumDiagnosticsSeen;
|
|
};
|
|
} // end namespace
|
|
|
|
TEST(runToolOnCode, FindsClassDecl) {
|
|
bool FoundClassDeclX = false;
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<TestAction>(
|
|
std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
|
|
"class X;"));
|
|
EXPECT_TRUE(FoundClassDeclX);
|
|
|
|
FoundClassDeclX = false;
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<TestAction>(
|
|
std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
|
|
"class Y;"));
|
|
EXPECT_FALSE(FoundClassDeclX);
|
|
}
|
|
|
|
TEST(buildASTFromCode, FindsClassDecl) {
|
|
std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
|
|
ASSERT_TRUE(AST.get());
|
|
EXPECT_TRUE(FindClassDeclX(AST.get()));
|
|
|
|
AST = buildASTFromCode("class Y;");
|
|
ASSERT_TRUE(AST.get());
|
|
EXPECT_FALSE(FindClassDeclX(AST.get()));
|
|
}
|
|
|
|
TEST(buildASTFromCode, ReportsErrors) {
|
|
TestDiagnosticConsumer Consumer;
|
|
std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs(
|
|
"int x = \"A\";", {}, "input.cc", "clang-tool",
|
|
std::make_shared<PCHContainerOperations>(),
|
|
getClangStripDependencyFileAdjuster(), FileContentMappings(), &Consumer);
|
|
EXPECT_TRUE(AST.get());
|
|
EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
|
|
}
|
|
|
|
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
|
|
std::unique_ptr<FrontendActionFactory> Factory(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
std::unique_ptr<FrontendAction> Action(Factory->create());
|
|
EXPECT_TRUE(Action.get() != nullptr);
|
|
}
|
|
|
|
struct IndependentFrontendActionCreator {
|
|
std::unique_ptr<ASTConsumer> newASTConsumer() {
|
|
return std::make_unique<FindTopLevelDeclConsumer>(nullptr);
|
|
}
|
|
};
|
|
|
|
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
|
|
IndependentFrontendActionCreator Creator;
|
|
std::unique_ptr<FrontendActionFactory> Factory(
|
|
newFrontendActionFactory(&Creator));
|
|
std::unique_ptr<FrontendAction> Action(Factory->create());
|
|
EXPECT_TRUE(Action.get() != nullptr);
|
|
}
|
|
|
|
TEST(ToolInvocation, TestMapVirtualFile) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
Args.push_back("-Idef");
|
|
Args.push_back("-fsyntax-only");
|
|
Args.push_back("test.cpp");
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile(
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
|
|
InMemoryFileSystem->addFile("def/abc", 0,
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
|
EXPECT_TRUE(Invocation.run());
|
|
}
|
|
|
|
TEST(ToolInvocation, TestVirtualModulesCompilation) {
|
|
// FIXME: Currently, this only tests that we don't exit with an error if a
|
|
// mapped module.map is found on the include path. In the future, expand this
|
|
// test to run a full modules enabled compilation, so we make sure we can
|
|
// rerun modules compilations with a virtual file system.
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
Args.push_back("-Idef");
|
|
Args.push_back("-fsyntax-only");
|
|
Args.push_back("test.cpp");
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile(
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
|
|
InMemoryFileSystem->addFile("def/abc", 0,
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
|
// Add a module.map file in the include directory of our header, so we trigger
|
|
// the module.map header search logic.
|
|
InMemoryFileSystem->addFile("def/module.map", 0,
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
|
EXPECT_TRUE(Invocation.run());
|
|
}
|
|
|
|
TEST(ToolInvocation, DiagnosticsEngineProperlyInitializedForCC1Construction) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
// Unknown warning option will result in a warning.
|
|
Args.push_back("-fexpensive-optimizations");
|
|
// Argument that will suppress the warning above.
|
|
Args.push_back("-Wno-ignored-optimization-argument");
|
|
Args.push_back("-E");
|
|
Args.push_back("test.cpp");
|
|
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile("test.cpp", 0,
|
|
llvm::MemoryBuffer::getMemBuffer(""));
|
|
TextDiagnosticBuffer Consumer;
|
|
Invocation.setDiagnosticConsumer(&Consumer);
|
|
EXPECT_TRUE(Invocation.run());
|
|
// Check that the warning was ignored due to the '-Wno-xxx' argument.
|
|
EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 0u);
|
|
}
|
|
|
|
TEST(ToolInvocation, CustomDiagnosticOptionsOverwriteParsedOnes) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
// Unknown warning option will result in a warning.
|
|
Args.push_back("-fexpensive-optimizations");
|
|
// Argument that will suppress the warning above.
|
|
Args.push_back("-Wno-ignored-optimization-argument");
|
|
Args.push_back("-E");
|
|
Args.push_back("test.cpp");
|
|
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile("test.cpp", 0,
|
|
llvm::MemoryBuffer::getMemBuffer(""));
|
|
TextDiagnosticBuffer Consumer;
|
|
Invocation.setDiagnosticConsumer(&Consumer);
|
|
|
|
// Inject custom `DiagnosticOptions` for command-line parsing.
|
|
auto DiagOpts = llvm::makeIntrusiveRefCnt<DiagnosticOptions>();
|
|
Invocation.setDiagnosticOptions(&*DiagOpts);
|
|
|
|
EXPECT_TRUE(Invocation.run());
|
|
// Check that the warning was issued during command-line parsing due to the
|
|
// custom `DiagnosticOptions` without '-Wno-xxx'.
|
|
EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 1u);
|
|
}
|
|
|
|
struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer {
|
|
bool SawSourceManager;
|
|
|
|
DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {}
|
|
|
|
void HandleDiagnostic(clang::DiagnosticsEngine::Level,
|
|
const clang::Diagnostic &info) override {
|
|
SawSourceManager = info.hasSourceManager();
|
|
}
|
|
};
|
|
|
|
TEST(ToolInvocation, DiagConsumerExpectingSourceManager) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
// Note: intentional error; user probably meant -ferror-limit=0.
|
|
Args.push_back("-ferror-limit=-1");
|
|
Args.push_back("-fsyntax-only");
|
|
Args.push_back("test.cpp");
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile(
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n"));
|
|
|
|
DiagnosticConsumerExpectingSourceManager Consumer;
|
|
Invocation.setDiagnosticConsumer(&Consumer);
|
|
|
|
EXPECT_TRUE(Invocation.run());
|
|
EXPECT_TRUE(Consumer.SawSourceManager);
|
|
}
|
|
|
|
TEST(ToolInvocation, CC1Args) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
Args.push_back("-cc1");
|
|
Args.push_back("-fsyntax-only");
|
|
Args.push_back("test.cpp");
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile(
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
|
|
EXPECT_TRUE(Invocation.run());
|
|
}
|
|
|
|
TEST(ToolInvocation, CC1ArgsInvalid) {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("tool-executable");
|
|
Args.push_back("-cc1");
|
|
Args.push_back("-invalid-arg");
|
|
Args.push_back("test.cpp");
|
|
clang::tooling::ToolInvocation Invocation(
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
|
InMemoryFileSystem->addFile(
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
|
|
EXPECT_FALSE(Invocation.run());
|
|
}
|
|
|
|
namespace {
|
|
/// Overlays the real filesystem with the given VFS and returns the result.
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
|
|
overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
|
|
auto RFS = llvm::vfs::getRealFileSystem();
|
|
auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS);
|
|
OverlayFS->pushOverlay(VFS);
|
|
return OverlayFS;
|
|
}
|
|
|
|
struct CommandLineExtractorTest : public ::testing::Test {
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
|
|
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
|
|
driver::Driver Driver;
|
|
|
|
public:
|
|
CommandLineExtractorTest()
|
|
: InMemoryFS(new llvm::vfs::InMemoryFileSystem),
|
|
Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)),
|
|
Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags,
|
|
"clang LLVM compiler", overlayRealFS(InMemoryFS)) {}
|
|
|
|
void addFile(StringRef Name, StringRef Content) {
|
|
InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content));
|
|
}
|
|
|
|
const llvm::opt::ArgStringList *
|
|
extractCC1Arguments(llvm::ArrayRef<const char *> Argv) {
|
|
const std::unique_ptr<driver::Compilation> Compilation(
|
|
Driver.BuildCompilation(llvm::ArrayRef(Argv)));
|
|
|
|
return getCC1Arguments(Diags.get(), Compilation.get());
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptOffloading) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-x", "hip", "test.c",
|
|
"-nogpulib", "-nogpuinc"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-c", "-x", "hip",
|
|
"test.c", "-nogpulib", "-nogpuinc"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {
|
|
"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-fsyntax-only", "-x", "hip",
|
|
"test.c", "-nogpulib", "-nogpuinc"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {
|
|
"clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as",
|
|
"-c", "test.c"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-c", "-fembed-bitcode", "test.c"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, AcceptSaveTemps) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-c", "-save-temps", "test.c"};
|
|
EXPECT_NE(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) {
|
|
addFile("test.c", "int main() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-arch", "x86_64", "-arch",
|
|
"arm64", "-c", "test.c"};
|
|
EXPECT_EQ(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) {
|
|
addFile("one.c", "void one() {}\n");
|
|
addFile("two.c", "void two() {}\n");
|
|
const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
|
|
"-c", "one.c", "two.c"};
|
|
EXPECT_EQ(extractCC1Arguments(Args), nullptr);
|
|
}
|
|
|
|
struct VerifyEndCallback : public SourceFileCallbacks {
|
|
VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
|
|
bool handleBeginSource(CompilerInstance &CI) override {
|
|
++BeginCalled;
|
|
return true;
|
|
}
|
|
void handleEndSource() override { ++EndCalled; }
|
|
std::unique_ptr<ASTConsumer> newASTConsumer() {
|
|
return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
|
|
}
|
|
unsigned BeginCalled;
|
|
unsigned EndCalled;
|
|
bool Matched;
|
|
};
|
|
|
|
#if !defined(_WIN32)
|
|
TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
|
|
VerifyEndCallback EndCallback;
|
|
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
std::vector<std::string> Sources;
|
|
Sources.push_back("/a.cc");
|
|
Sources.push_back("/b.cc");
|
|
ClangTool Tool(Compilations, Sources);
|
|
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
Tool.mapVirtualFile("/b.cc", "void b() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory(&EndCallback, &EndCallback));
|
|
Tool.run(Action.get());
|
|
|
|
EXPECT_TRUE(EndCallback.Matched);
|
|
EXPECT_EQ(2u, EndCallback.BeginCalled);
|
|
EXPECT_EQ(2u, EndCallback.EndCalled);
|
|
}
|
|
#endif
|
|
|
|
struct SkipBodyConsumer : public clang::ASTConsumer {
|
|
/// Skip the 'skipMe' function.
|
|
bool shouldSkipFunctionBody(Decl *D) override {
|
|
NamedDecl *F = dyn_cast<NamedDecl>(D);
|
|
return F && F->getNameAsString() == "skipMe";
|
|
}
|
|
};
|
|
|
|
struct SkipBodyAction : public clang::ASTFrontendAction {
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
|
StringRef) override {
|
|
Compiler.getFrontendOpts().SkipFunctionBodies = true;
|
|
return std::make_unique<SkipBodyConsumer>();
|
|
}
|
|
};
|
|
|
|
TEST(runToolOnCode, TestSkipFunctionBody) {
|
|
std::vector<std::string> Args = {"-std=c++11"};
|
|
std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
|
|
|
|
EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(),
|
|
"int skipMe() { an_error_here }"));
|
|
EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(),
|
|
"int skipMeNot() { an_error_here }"));
|
|
|
|
// Test constructors with initializers
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"struct skipMe { skipMe() : an_error() { more error } };", Args));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
|
|
"skipMe::skipMe() : an_error([](){;}) { more error }",
|
|
Args));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
|
|
"skipMe::skipMe() : an_error{[](){;}} { more error }",
|
|
Args));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"struct skipMe { skipMe(); };"
|
|
"skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
|
|
Args));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };",
|
|
Args));
|
|
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
|
|
Args));
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(),
|
|
"struct skipMeNot { skipMeNot(); };"
|
|
"skipMeNot::skipMeNot() : an_error() { }",
|
|
Args));
|
|
|
|
// Try/catch
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"void skipMe() try { an_error() } catch(error) { error };"));
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"struct S { void skipMe() try { an_error() } catch(error) { error } };"));
|
|
EXPECT_TRUE(
|
|
runToolOnCode(std::make_unique<SkipBodyAction>(),
|
|
"void skipMe() try { an_error() } catch(error) { error; }"
|
|
"catch(error) { error } catch (error) { }"));
|
|
EXPECT_FALSE(runToolOnCode(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"void skipMe() try something;")); // don't crash while parsing
|
|
|
|
// Template
|
|
EXPECT_TRUE(runToolOnCode(
|
|
std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }"
|
|
"int x = skipMe<int>();"));
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
|
std::make_unique<SkipBodyAction>(),
|
|
"template<typename T> int skipMeNot() { an_error_here }", Args2));
|
|
}
|
|
|
|
TEST(runToolOnCodeWithArgs, TestNoDepFile) {
|
|
llvm::SmallString<32> DepFilePath;
|
|
ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
|
|
DepFilePath));
|
|
std::vector<std::string> Args;
|
|
Args.push_back("-MMD");
|
|
Args.push_back("-MT");
|
|
Args.push_back(std::string(DepFilePath.str()));
|
|
Args.push_back("-MF");
|
|
Args.push_back(std::string(DepFilePath.str()));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args));
|
|
EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
|
|
EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
|
|
}
|
|
|
|
struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
|
|
CheckColoredDiagnosticsAction(bool ShouldShowColor)
|
|
: ShouldShowColor(ShouldShowColor) {}
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
|
StringRef) override {
|
|
if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
|
|
Compiler.getDiagnostics().Report(
|
|
Compiler.getDiagnostics().getCustomDiagID(
|
|
DiagnosticsEngine::Fatal,
|
|
"getDiagnosticOpts().ShowColors != ShouldShowColor"));
|
|
return std::make_unique<ASTConsumer>();
|
|
}
|
|
|
|
private:
|
|
bool ShouldShowColor = true;
|
|
};
|
|
|
|
TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(true), "",
|
|
{"-fcolor-diagnostics"}));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(false), "",
|
|
{"-fno-color-diagnostics"}));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(true), "",
|
|
{"-fno-color-diagnostics", "-fcolor-diagnostics"}));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(false), "",
|
|
{"-fcolor-diagnostics", "-fno-color-diagnostics"}));
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(true), "",
|
|
{"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
|
|
|
|
// Check that this test would fail if ShowColors is not what it should.
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
|
std::make_unique<CheckColoredDiagnosticsAction>(false), "",
|
|
{"-fcolor-diagnostics"}));
|
|
}
|
|
|
|
TEST(ClangToolTest, ArgumentAdjusters) {
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
bool Found = false;
|
|
bool Ran = false;
|
|
ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
|
|
[&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
|
|
Ran = true;
|
|
if (llvm::is_contained(Args, "-fsyntax-only"))
|
|
Found = true;
|
|
return Args;
|
|
};
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
|
Tool.run(Action.get());
|
|
EXPECT_TRUE(Ran);
|
|
EXPECT_TRUE(Found);
|
|
|
|
Ran = Found = false;
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
|
Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
|
|
Tool.run(Action.get());
|
|
EXPECT_TRUE(Ran);
|
|
EXPECT_FALSE(Found);
|
|
}
|
|
|
|
TEST(ClangToolTest, NoDoubleSyntaxOnly) {
|
|
FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
size_t SyntaxOnlyCount = 0;
|
|
ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
|
|
[&SyntaxOnlyCount](const CommandLineArguments &Args,
|
|
StringRef /*unused*/) {
|
|
for (llvm::StringRef Arg : Args) {
|
|
if (Arg == "-fsyntax-only")
|
|
++SyntaxOnlyCount;
|
|
}
|
|
return Args;
|
|
};
|
|
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
|
Tool.run(Action.get());
|
|
EXPECT_EQ(SyntaxOnlyCount, 1U);
|
|
}
|
|
|
|
TEST(ClangToolTest, NoOutputCommands) {
|
|
FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd",
|
|
"--save-temps",
|
|
"--save-temps=somedir"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"};
|
|
bool Ran = false;
|
|
ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
|
|
[&OutputCommands, &Ran](const CommandLineArguments &Args,
|
|
StringRef /*unused*/) {
|
|
for (llvm::StringRef Arg : Args) {
|
|
for (llvm::StringRef OutputCommand : OutputCommands)
|
|
EXPECT_FALSE(Arg.contains(OutputCommand));
|
|
}
|
|
Ran = true;
|
|
return Args;
|
|
};
|
|
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
|
Tool.run(Action.get());
|
|
EXPECT_TRUE(Ran);
|
|
}
|
|
|
|
TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
new llvm::vfs::InMemoryFileSystem);
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
|
|
InMemoryFileSystem->addFile(
|
|
"a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"),
|
|
std::make_shared<PCHContainerOperations>(), OverlayFileSystem);
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
EXPECT_EQ(0, Tool.run(Action.get()));
|
|
}
|
|
|
|
// Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
|
|
TEST(ClangToolTest, StripDependencyFileAdjuster) {
|
|
FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
CommandLineArguments FinalArgs;
|
|
ArgumentsAdjuster CheckFlagsAdjuster =
|
|
[&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
|
|
FinalArgs = Args;
|
|
return Args;
|
|
};
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
|
|
Tool.run(Action.get());
|
|
|
|
auto HasFlag = [&FinalArgs](const std::string &Flag) {
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
|
};
|
|
EXPECT_FALSE(HasFlag("-MD"));
|
|
EXPECT_FALSE(HasFlag("-MMD"));
|
|
EXPECT_TRUE(HasFlag("-c"));
|
|
EXPECT_TRUE(HasFlag("-w"));
|
|
}
|
|
|
|
// Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
|
|
TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
|
|
FixedCompilationDatabase Compilations(
|
|
"/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
|
|
"-showIncludes:user", "-c"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
CommandLineArguments FinalArgs;
|
|
ArgumentsAdjuster CheckFlagsAdjuster =
|
|
[&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
|
|
FinalArgs = Args;
|
|
return Args;
|
|
};
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
|
|
Tool.run(Action.get());
|
|
|
|
auto HasFlag = [&FinalArgs](const std::string &Flag) {
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
|
};
|
|
EXPECT_FALSE(HasFlag("/showIncludes"));
|
|
EXPECT_FALSE(HasFlag("/showIncludes:user"));
|
|
EXPECT_FALSE(HasFlag("-showIncludes"));
|
|
EXPECT_FALSE(HasFlag("-showIncludes:user"));
|
|
EXPECT_TRUE(HasFlag("-c"));
|
|
}
|
|
|
|
// Check getClangStripDependencyFileAdjuster doesn't strip args when using the
|
|
// MSVC cl.exe driver
|
|
TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) {
|
|
FixedCompilationDatabase Compilations(
|
|
"/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
CommandLineArguments FinalArgs;
|
|
ArgumentsAdjuster CheckFlagsAdjuster =
|
|
[&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
|
|
FinalArgs = Args;
|
|
return Args;
|
|
};
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
|
|
Tool.run(Action.get());
|
|
|
|
auto HasFlag = [&FinalArgs](const std::string &Flag) {
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
|
};
|
|
EXPECT_TRUE(HasFlag("-MD"));
|
|
EXPECT_TRUE(HasFlag("-MDd"));
|
|
EXPECT_TRUE(HasFlag("-MT"));
|
|
EXPECT_TRUE(HasFlag("-O1"));
|
|
EXPECT_TRUE(HasFlag("-MTd"));
|
|
EXPECT_TRUE(HasFlag("-MP"));
|
|
}
|
|
|
|
// Check getClangStripPluginsAdjuster strips plugin related args.
|
|
TEST(ClangToolTest, StripPluginsAdjuster) {
|
|
FixedCompilationDatabase Compilations(
|
|
"/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
|
|
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
CommandLineArguments FinalArgs;
|
|
ArgumentsAdjuster CheckFlagsAdjuster =
|
|
[&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
|
|
FinalArgs = Args;
|
|
return Args;
|
|
};
|
|
Tool.clearArgumentsAdjusters();
|
|
Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
|
|
Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
|
|
Tool.run(Action.get());
|
|
|
|
auto HasFlag = [&FinalArgs](const std::string &Flag) {
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
|
};
|
|
EXPECT_FALSE(HasFlag("-Xclang"));
|
|
EXPECT_FALSE(HasFlag("-add-plugin"));
|
|
EXPECT_FALSE(HasFlag("-random-plugin"));
|
|
}
|
|
|
|
TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
|
|
llvm::InitializeAllTargets();
|
|
|
|
std::string Target = getAnyTargetForTesting();
|
|
ASSERT_FALSE(Target.empty());
|
|
|
|
std::vector<std::string> Args = {"clang", "-foo"};
|
|
addTargetAndModeForProgramName(Args, "");
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
|
|
addTargetAndModeForProgramName(Args, Target + "-g++");
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
|
|
"--driver-mode=g++", "-foo"}),
|
|
Args);
|
|
}
|
|
|
|
TEST(addTargetAndModeForProgramName, PathIgnored) {
|
|
llvm::InitializeAllTargets();
|
|
std::string Target = getAnyTargetForTesting();
|
|
ASSERT_FALSE(Target.empty());
|
|
|
|
SmallString<32> ToolPath;
|
|
llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
|
|
|
|
std::vector<std::string> Args = {"clang", "-foo"};
|
|
addTargetAndModeForProgramName(Args, ToolPath);
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
|
|
"--driver-mode=g++", "-foo"}),
|
|
Args);
|
|
}
|
|
|
|
TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
|
|
llvm::InitializeAllTargets();
|
|
std::string Target = getAnyTargetForTesting();
|
|
ASSERT_FALSE(Target.empty());
|
|
|
|
std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
|
|
addTargetAndModeForProgramName(Args, Target + "-g++");
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
|
|
"-target", "something"}),
|
|
Args);
|
|
|
|
std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"};
|
|
addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
|
|
"--target=something"}),
|
|
ArgsAlt);
|
|
}
|
|
|
|
TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
|
|
llvm::InitializeAllTargets();
|
|
std::string Target = getAnyTargetForTesting();
|
|
ASSERT_FALSE(Target.empty());
|
|
|
|
std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
|
|
addTargetAndModeForProgramName(Args, Target + "-g++");
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
|
|
"--driver-mode=abc"}),
|
|
Args);
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
TEST(ClangToolTest, BuildASTs) {
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
|
|
std::vector<std::string> Sources;
|
|
Sources.push_back("/a.cc");
|
|
Sources.push_back("/b.cc");
|
|
ClangTool Tool(Compilations, Sources);
|
|
|
|
Tool.mapVirtualFile("/a.cc", "void a() {}");
|
|
Tool.mapVirtualFile("/b.cc", "void b() {}");
|
|
|
|
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
|
EXPECT_EQ(0, Tool.buildASTs(ASTs));
|
|
EXPECT_EQ(2u, ASTs.size());
|
|
}
|
|
|
|
TEST(ClangToolTest, InjectDiagnosticConsumer) {
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
|
|
TestDiagnosticConsumer Consumer;
|
|
Tool.setDiagnosticConsumer(&Consumer);
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
Tool.run(Action.get());
|
|
EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
|
|
}
|
|
|
|
TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
|
ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
|
|
Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
|
|
TestDiagnosticConsumer Consumer;
|
|
Tool.setDiagnosticConsumer(&Consumer);
|
|
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
|
Tool.buildASTs(ASTs);
|
|
EXPECT_EQ(1u, ASTs.size());
|
|
EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
|
|
}
|
|
#endif
|
|
|
|
TEST(runToolOnCode, TestResetDiagnostics) {
|
|
// This is a tool that resets the diagnostic during the compilation.
|
|
struct ResetDiagnosticAction : public clang::ASTFrontendAction {
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
|
StringRef) override {
|
|
struct Consumer : public clang::ASTConsumer {
|
|
bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
|
|
auto &Diags = (*D.begin())->getASTContext().getDiagnostics();
|
|
// Ignore any error
|
|
Diags.Reset();
|
|
// Disable warnings because computing the CFG might crash.
|
|
Diags.setIgnoreAllWarnings(true);
|
|
return true;
|
|
}
|
|
};
|
|
return std::make_unique<Consumer>();
|
|
}
|
|
};
|
|
|
|
// Should not crash
|
|
EXPECT_FALSE(
|
|
runToolOnCode(std::make_unique<ResetDiagnosticAction>(),
|
|
"struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
|
|
"void func() { long x; Foo f(x); }"));
|
|
}
|
|
|
|
} // end namespace tooling
|
|
} // end namespace clang
|