summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp')
-rw-r--r--gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp702
1 files changed, 702 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp b/gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp
new file mode 100644
index 00000000000..13581342fc1
--- /dev/null
+++ b/gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp
@@ -0,0 +1,702 @@
+//===- 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/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.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;
+}
+} // 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(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());
+}
+
+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(DepFilePath.str());
+ Args.push_back("-MF");
+ Args.push_back(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 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"));
+}
+
+namespace {
+/// Find a target name such that looking for it in TargetRegistry by that name
+/// returns the same target. We expect that there is at least one target
+/// configured with this property.
+std::string getAnyTarget() {
+ llvm::InitializeAllTargets();
+ for (const auto &Target : llvm::TargetRegistry::targets()) {
+ std::string Error;
+ StringRef TargetName(Target.getName());
+ if (TargetName == "x86-64")
+ TargetName = "x86_64";
+ if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
+ return TargetName;
+ }
+ }
+ return "";
+}
+}
+
+TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
+ std::string Target = getAnyTarget();
+ 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) {
+ std::string Target = getAnyTarget();
+ 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) {
+ std::string Target = getAnyTarget();
+ 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) {
+ std::string Target = getAnyTarget();
+ 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);
+
+ std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"};
+ addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
+ "--driver-mode", "abc"}),
+ ArgsAlt);
+}
+
+#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());
+}
+
+struct TestDiagnosticConsumer : public DiagnosticConsumer {
+ TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override {
+ ++NumDiagnosticsSeen;
+ }
+ unsigned NumDiagnosticsSeen;
+};
+
+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