diff options
author | 2020-08-03 14:31:31 +0000 | |
---|---|---|
committer | 2020-08-03 14:31:31 +0000 | |
commit | e5dd70708596ae51455a0ffa086a00c5b29f8583 (patch) | |
tree | 5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp')
-rw-r--r-- | gnu/llvm/clang/unittests/Tooling/ToolingTest.cpp | 702 |
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 |