2012-04-04 20:07:46 +08:00
|
|
|
//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2012-04-04 20:07:46 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/AST/ASTConsumer.h"
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
|
|
#include "clang/AST/DeclGroup.h"
|
2013-11-07 04:12:45 +08:00
|
|
|
#include "clang/Frontend/ASTUnit.h"
|
2012-11-28 05:31:01 +08:00
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
2012-04-04 20:07:46 +08:00
|
|
|
#include "clang/Frontend/FrontendAction.h"
|
|
|
|
#include "clang/Frontend/FrontendActions.h"
|
2019-11-13 20:44:40 +08:00
|
|
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
2012-04-04 20:07:46 +08:00
|
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
|
|
|
#include "clang/Tooling/Tooling.h"
|
2013-11-07 04:12:45 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2019-11-13 20:44:40 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
2016-07-19 03:02:11 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
2015-10-06 18:45:03 +08:00
|
|
|
#include "llvm/Support/TargetRegistry.h"
|
2016-07-19 03:02:11 +08:00
|
|
|
#include "llvm/Support/TargetSelect.h"
|
2014-01-07 19:51:46 +08:00
|
|
|
#include "gtest/gtest.h"
|
2014-12-04 01:53:02 +08:00
|
|
|
#include <algorithm>
|
2012-06-01 22:50:43 +08:00
|
|
|
#include <string>
|
2019-11-13 20:44:40 +08:00
|
|
|
#include <vector>
|
2012-04-04 20:07:46 +08:00
|
|
|
|
|
|
|
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 {
|
2014-08-11 03:56:51 +08:00
|
|
|
public:
|
2012-04-04 20:07:46 +08:00
|
|
|
/// Takes ownership of TestConsumer.
|
2014-08-11 03:56:51 +08:00
|
|
|
explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
|
|
|
|
: TestConsumer(std::move(TestConsumer)) {}
|
2012-04-04 20:07:46 +08:00
|
|
|
|
2014-08-11 03:56:51 +08:00
|
|
|
protected:
|
2015-04-11 10:00:23 +08:00
|
|
|
std::unique_ptr<clang::ASTConsumer>
|
|
|
|
CreateASTConsumer(clang::CompilerInstance &compiler,
|
|
|
|
StringRef dummy) override {
|
2012-04-04 20:07:46 +08:00
|
|
|
/// TestConsumer will be deleted by the framework calling us.
|
2014-08-11 03:56:51 +08:00
|
|
|
return std::move(TestConsumer);
|
2012-04-04 20:07:46 +08:00
|
|
|
}
|
|
|
|
|
2014-08-11 03:56:51 +08:00
|
|
|
private:
|
|
|
|
std::unique_ptr<clang::ASTConsumer> TestConsumer;
|
2012-04-04 20:07:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class FindTopLevelDeclConsumer : public clang::ASTConsumer {
|
|
|
|
public:
|
|
|
|
explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
|
|
|
|
: FoundTopLevelDecl(FoundTopLevelDecl) {}
|
2015-04-11 10:00:23 +08:00
|
|
|
bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
|
2012-04-04 20:07:46 +08:00
|
|
|
*FoundTopLevelDecl = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
bool * const FoundTopLevelDecl;
|
|
|
|
};
|
|
|
|
} // end namespace
|
|
|
|
|
2012-06-16 11:34:49 +08:00
|
|
|
TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
|
2012-04-04 20:07:46 +08:00
|
|
|
bool FoundTopLevelDecl = false;
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_TRUE(runToolOnCode(
|
|
|
|
std::make_unique<TestAction>(
|
|
|
|
std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)),
|
|
|
|
""));
|
2012-06-16 11:34:49 +08:00
|
|
|
EXPECT_FALSE(FoundTopLevelDecl);
|
2012-04-04 20:07:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class FindClassDeclXConsumer : public clang::ASTConsumer {
|
|
|
|
public:
|
|
|
|
FindClassDeclXConsumer(bool *FoundClassDeclX)
|
|
|
|
: FoundClassDeclX(FoundClassDeclX) {}
|
2015-04-11 10:00:23 +08:00
|
|
|
bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
|
2012-04-04 20:07:46 +08:00
|
|
|
if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
|
|
|
|
*GroupRef.begin())) {
|
|
|
|
if (Record->getName() == "X") {
|
|
|
|
*FoundClassDeclX = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
bool *FoundClassDeclX;
|
|
|
|
};
|
2013-11-07 04:12:45 +08:00
|
|
|
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;
|
|
|
|
}
|
2020-02-18 22:04:44 +08:00
|
|
|
|
|
|
|
struct TestDiagnosticConsumer : public DiagnosticConsumer {
|
|
|
|
TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
|
|
|
|
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
|
|
|
const Diagnostic &Info) override {
|
|
|
|
++NumDiagnosticsSeen;
|
|
|
|
}
|
|
|
|
unsigned NumDiagnosticsSeen;
|
|
|
|
};
|
2012-04-04 20:07:46 +08:00
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
TEST(runToolOnCode, FindsClassDecl) {
|
|
|
|
bool FoundClassDeclX = false;
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_TRUE(runToolOnCode(
|
|
|
|
std::make_unique<TestAction>(
|
|
|
|
std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
|
|
|
|
"class X;"));
|
2012-04-04 20:07:46 +08:00
|
|
|
EXPECT_TRUE(FoundClassDeclX);
|
|
|
|
|
|
|
|
FoundClassDeclX = false;
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_TRUE(runToolOnCode(
|
|
|
|
std::make_unique<TestAction>(
|
|
|
|
std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
|
|
|
|
"class Y;"));
|
2012-04-04 20:07:46 +08:00
|
|
|
EXPECT_FALSE(FoundClassDeclX);
|
|
|
|
}
|
|
|
|
|
2013-11-07 04:12:45 +08:00
|
|
|
TEST(buildASTFromCode, FindsClassDecl) {
|
2014-04-26 01:01:33 +08:00
|
|
|
std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
|
2013-11-07 04:12:45 +08:00
|
|
|
ASSERT_TRUE(AST.get());
|
|
|
|
EXPECT_TRUE(FindClassDeclX(AST.get()));
|
|
|
|
|
2014-04-26 01:01:33 +08:00
|
|
|
AST = buildASTFromCode("class Y;");
|
2013-11-07 04:12:45 +08:00
|
|
|
ASSERT_TRUE(AST.get());
|
|
|
|
EXPECT_FALSE(FindClassDeclX(AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-02-18 22:04:44 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FrontendActionFactory> Factory(
|
2013-01-13 03:30:44 +08:00
|
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FrontendAction> Action(Factory->create());
|
2014-06-08 16:38:12 +08:00
|
|
|
EXPECT_TRUE(Action.get() != nullptr);
|
2012-04-04 20:07:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct IndependentFrontendActionCreator {
|
2014-08-11 03:56:51 +08:00
|
|
|
std::unique_ptr<ASTConsumer> newASTConsumer() {
|
2019-08-15 07:04:18 +08:00
|
|
|
return std::make_unique<FindTopLevelDeclConsumer>(nullptr);
|
2012-07-06 02:13:01 +08:00
|
|
|
}
|
2012-04-04 20:07:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
|
|
|
|
IndependentFrontendActionCreator Creator;
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FrontendActionFactory> Factory(
|
2013-01-13 03:30:44 +08:00
|
|
|
newFrontendActionFactory(&Creator));
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FrontendAction> Action(Factory->create());
|
2014-06-08 16:38:12 +08:00
|
|
|
EXPECT_TRUE(Action.get() != nullptr);
|
2012-04-04 20:07:46 +08:00
|
|
|
}
|
|
|
|
|
2012-06-01 22:50:43 +08:00
|
|
|
TEST(ToolInvocation, TestMapVirtualFile) {
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
|
|
new llvm::vfs::InMemoryFileSystem);
|
2015-10-09 17:54:37 +08:00
|
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
2012-06-01 22:50:43 +08:00
|
|
|
std::vector<std::string> Args;
|
|
|
|
Args.push_back("tool-executable");
|
|
|
|
Args.push_back("-Idef");
|
|
|
|
Args.push_back("-fsyntax-only");
|
|
|
|
Args.push_back("test.cpp");
|
2019-08-30 17:29:34 +08:00
|
|
|
clang::tooling::ToolInvocation Invocation(
|
|
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
2015-10-09 17:54:37 +08:00
|
|
|
InMemoryFileSystem->addFile(
|
|
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
|
|
|
|
InMemoryFileSystem->addFile("def/abc", 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
2012-06-01 22:50:43 +08:00
|
|
|
EXPECT_TRUE(Invocation.run());
|
|
|
|
}
|
|
|
|
|
Use the same SourceManager for ModuleMaps and compilations.
This allows using virtual file mappings on the original SourceManager to
map in virtual module.map files. Without this patch, the ModuleMap
search will find a module.map file (as the FileEntry exists in the
FileManager), but will be unable to get the content from the
SourceManager (as ModuleMap previously created its own SourceManager).
Two problems needed to be fixed which this patch exposed:
1. Storing the inferred module map
When writing out a module, the ASTWriter stores the names of the files
in the main source manager; when loading the AST again, the ASTReader
errs out if such a file is found missing, unless it is overridden.
Previously CompilerInstance's compileModule method would store the
inferred module map to a temporary file; the problem with this approach
is that now that the module map is handled by the main source manager,
the ASTWriter stores the name of the temporary module map as source to
the compilation; later, when the module is loaded, the temporary file
has already been deleted, which leads to a compilation error. This patch
changes the inferred module map to instead inject a virtual file into
the source manager. This both saves some disk IO, and works with how the
ASTWriter/ASTReader handle overridden source files.
2. Changing test input in test/Modules/Inputs/*
Now that the module map file is handled by the main source manager, the
VerifyDiagnosticConsumer will not ignore diagnostics created while
parsing the module map file. The module test test/Modules/renamed.m uses
-I test/Modules/Inputs and triggers recursive loading of all module maps
in test/Modules/Inputs, some of which had conflicting names, thus
leading errors while parsing the module maps. Those diagnostics already
occur on trunk, but before this patch they would not break the test, as
they were ignored by the VerifyDiagnosticConsumer. This patch thus
changes the module maps that have been recently introduced which broke
the invariant of compatible modules maps in test/Modules/Inputs.
llvm-svn: 193314
2013-10-24 15:51:24 +08:00
|
|
|
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.
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
|
|
new llvm::vfs::InMemoryFileSystem);
|
2015-10-09 17:54:37 +08:00
|
|
|
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
|
|
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
|
|
|
new FileManager(FileSystemOptions(), OverlayFileSystem));
|
Use the same SourceManager for ModuleMaps and compilations.
This allows using virtual file mappings on the original SourceManager to
map in virtual module.map files. Without this patch, the ModuleMap
search will find a module.map file (as the FileEntry exists in the
FileManager), but will be unable to get the content from the
SourceManager (as ModuleMap previously created its own SourceManager).
Two problems needed to be fixed which this patch exposed:
1. Storing the inferred module map
When writing out a module, the ASTWriter stores the names of the files
in the main source manager; when loading the AST again, the ASTReader
errs out if such a file is found missing, unless it is overridden.
Previously CompilerInstance's compileModule method would store the
inferred module map to a temporary file; the problem with this approach
is that now that the module map is handled by the main source manager,
the ASTWriter stores the name of the temporary module map as source to
the compilation; later, when the module is loaded, the temporary file
has already been deleted, which leads to a compilation error. This patch
changes the inferred module map to instead inject a virtual file into
the source manager. This both saves some disk IO, and works with how the
ASTWriter/ASTReader handle overridden source files.
2. Changing test input in test/Modules/Inputs/*
Now that the module map file is handled by the main source manager, the
VerifyDiagnosticConsumer will not ignore diagnostics created while
parsing the module map file. The module test test/Modules/renamed.m uses
-I test/Modules/Inputs and triggers recursive loading of all module maps
in test/Modules/Inputs, some of which had conflicting names, thus
leading errors while parsing the module maps. Those diagnostics already
occur on trunk, but before this patch they would not break the test, as
they were ignored by the VerifyDiagnosticConsumer. This patch thus
changes the module maps that have been recently introduced which broke
the invariant of compatible modules maps in test/Modules/Inputs.
llvm-svn: 193314
2013-10-24 15:51:24 +08:00
|
|
|
std::vector<std::string> Args;
|
|
|
|
Args.push_back("tool-executable");
|
|
|
|
Args.push_back("-Idef");
|
|
|
|
Args.push_back("-fsyntax-only");
|
|
|
|
Args.push_back("test.cpp");
|
2019-08-30 17:29:34 +08:00
|
|
|
clang::tooling::ToolInvocation Invocation(
|
|
|
|
Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
|
2015-10-09 17:54:37 +08:00
|
|
|
InMemoryFileSystem->addFile(
|
|
|
|
"test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
|
|
|
|
InMemoryFileSystem->addFile("def/abc", 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
Use the same SourceManager for ModuleMaps and compilations.
This allows using virtual file mappings on the original SourceManager to
map in virtual module.map files. Without this patch, the ModuleMap
search will find a module.map file (as the FileEntry exists in the
FileManager), but will be unable to get the content from the
SourceManager (as ModuleMap previously created its own SourceManager).
Two problems needed to be fixed which this patch exposed:
1. Storing the inferred module map
When writing out a module, the ASTWriter stores the names of the files
in the main source manager; when loading the AST again, the ASTReader
errs out if such a file is found missing, unless it is overridden.
Previously CompilerInstance's compileModule method would store the
inferred module map to a temporary file; the problem with this approach
is that now that the module map is handled by the main source manager,
the ASTWriter stores the name of the temporary module map as source to
the compilation; later, when the module is loaded, the temporary file
has already been deleted, which leads to a compilation error. This patch
changes the inferred module map to instead inject a virtual file into
the source manager. This both saves some disk IO, and works with how the
ASTWriter/ASTReader handle overridden source files.
2. Changing test input in test/Modules/Inputs/*
Now that the module map file is handled by the main source manager, the
VerifyDiagnosticConsumer will not ignore diagnostics created while
parsing the module map file. The module test test/Modules/renamed.m uses
-I test/Modules/Inputs and triggers recursive loading of all module maps
in test/Modules/Inputs, some of which had conflicting names, thus
leading errors while parsing the module maps. Those diagnostics already
occur on trunk, but before this patch they would not break the test, as
they were ignored by the VerifyDiagnosticConsumer. This patch thus
changes the module maps that have been recently introduced which broke
the invariant of compatible modules maps in test/Modules/Inputs.
llvm-svn: 193314
2013-10-24 15:51:24 +08:00
|
|
|
// Add a module.map file in the include directory of our header, so we trigger
|
|
|
|
// the module.map header search logic.
|
2015-10-09 17:54:37 +08:00
|
|
|
InMemoryFileSystem->addFile("def/module.map", 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer("\n"));
|
Use the same SourceManager for ModuleMaps and compilations.
This allows using virtual file mappings on the original SourceManager to
map in virtual module.map files. Without this patch, the ModuleMap
search will find a module.map file (as the FileEntry exists in the
FileManager), but will be unable to get the content from the
SourceManager (as ModuleMap previously created its own SourceManager).
Two problems needed to be fixed which this patch exposed:
1. Storing the inferred module map
When writing out a module, the ASTWriter stores the names of the files
in the main source manager; when loading the AST again, the ASTReader
errs out if such a file is found missing, unless it is overridden.
Previously CompilerInstance's compileModule method would store the
inferred module map to a temporary file; the problem with this approach
is that now that the module map is handled by the main source manager,
the ASTWriter stores the name of the temporary module map as source to
the compilation; later, when the module is loaded, the temporary file
has already been deleted, which leads to a compilation error. This patch
changes the inferred module map to instead inject a virtual file into
the source manager. This both saves some disk IO, and works with how the
ASTWriter/ASTReader handle overridden source files.
2. Changing test input in test/Modules/Inputs/*
Now that the module map file is handled by the main source manager, the
VerifyDiagnosticConsumer will not ignore diagnostics created while
parsing the module map file. The module test test/Modules/renamed.m uses
-I test/Modules/Inputs and triggers recursive loading of all module maps
in test/Modules/Inputs, some of which had conflicting names, thus
leading errors while parsing the module maps. Those diagnostics already
occur on trunk, but before this patch they would not break the test, as
they were ignored by the VerifyDiagnosticConsumer. This patch thus
changes the module maps that have been recently introduced which broke
the invariant of compatible modules maps in test/Modules/Inputs.
llvm-svn: 193314
2013-10-24 15:51:24 +08:00
|
|
|
EXPECT_TRUE(Invocation.run());
|
|
|
|
}
|
|
|
|
|
2021-04-06 16:38:19 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-05-30 00:01:10 +08:00
|
|
|
struct VerifyEndCallback : public SourceFileCallbacks {
|
|
|
|
VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
|
2017-06-09 09:36:10 +08:00
|
|
|
bool handleBeginSource(CompilerInstance &CI) override {
|
2013-05-30 00:01:10 +08:00
|
|
|
++BeginCalled;
|
|
|
|
return true;
|
|
|
|
}
|
2015-04-11 10:00:23 +08:00
|
|
|
void handleEndSource() override { ++EndCalled; }
|
2014-08-11 03:56:51 +08:00
|
|
|
std::unique_ptr<ASTConsumer> newASTConsumer() {
|
2019-08-15 07:04:18 +08:00
|
|
|
return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
|
2012-10-25 16:49:11 +08:00
|
|
|
}
|
2013-05-30 00:01:10 +08:00
|
|
|
unsigned BeginCalled;
|
|
|
|
unsigned EndCalled;
|
2012-10-25 16:49:11 +08:00
|
|
|
bool Matched;
|
|
|
|
};
|
|
|
|
|
2018-04-28 03:11:14 +08:00
|
|
|
#if !defined(_WIN32)
|
2013-05-30 00:01:10 +08:00
|
|
|
TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
|
2012-10-25 16:49:11 +08:00
|
|
|
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() {}");
|
|
|
|
|
2014-04-24 11:48:09 +08:00
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
|
|
newFrontendActionFactory(&EndCallback, &EndCallback));
|
|
|
|
Tool.run(Action.get());
|
2012-10-25 16:49:11 +08:00
|
|
|
|
|
|
|
EXPECT_TRUE(EndCallback.Matched);
|
2013-05-30 00:01:10 +08:00
|
|
|
EXPECT_EQ(2u, EndCallback.BeginCalled);
|
|
|
|
EXPECT_EQ(2u, EndCallback.EndCalled);
|
2012-10-25 16:49:11 +08:00
|
|
|
}
|
2012-10-25 17:38:41 +08:00
|
|
|
#endif
|
2012-10-25 16:49:11 +08:00
|
|
|
|
2012-11-28 05:31:01 +08:00
|
|
|
struct SkipBodyConsumer : public clang::ASTConsumer {
|
|
|
|
/// Skip the 'skipMe' function.
|
2015-04-11 10:00:23 +08:00
|
|
|
bool shouldSkipFunctionBody(Decl *D) override {
|
2016-06-17 05:40:06 +08:00
|
|
|
NamedDecl *F = dyn_cast<NamedDecl>(D);
|
2012-11-28 05:31:01 +08:00
|
|
|
return F && F->getNameAsString() == "skipMe";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SkipBodyAction : public clang::ASTFrontendAction {
|
2015-04-11 10:00:23 +08:00
|
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
|
|
|
StringRef) override {
|
2012-11-28 05:31:01 +08:00
|
|
|
Compiler.getFrontendOpts().SkipFunctionBodies = true;
|
2019-08-15 07:04:18 +08:00
|
|
|
return std::make_unique<SkipBodyConsumer>();
|
2012-11-28 05:31:01 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-28 12:37:38 +08:00
|
|
|
TEST(runToolOnCode, TestSkipFunctionBody) {
|
2016-06-17 05:40:06 +08:00
|
|
|
std::vector<std::string> Args = {"-std=c++11"};
|
2016-06-17 10:04:51 +08:00
|
|
|
std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
|
2016-06-17 05:40:06 +08:00
|
|
|
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(),
|
2012-11-28 05:31:01 +08:00
|
|
|
"int skipMe() { an_error_here }"));
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(),
|
2012-11-28 05:31:01 +08:00
|
|
|
"int skipMeNot() { an_error_here }"));
|
2016-06-17 05:40:06 +08:00
|
|
|
|
|
|
|
// Test constructors with initializers
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"struct skipMe { skipMe() : an_error() { more error } };", Args));
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
|
2018-02-27 23:54:55 +08:00
|
|
|
"skipMe::skipMe() : an_error([](){;}) { more error }",
|
2016-06-17 05:40:06 +08:00
|
|
|
Args));
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
|
2018-02-27 23:54:55 +08:00
|
|
|
"skipMe::skipMe() : an_error{[](){;}} { more error }",
|
2016-06-17 05:40:06 +08:00
|
|
|
Args));
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"struct skipMe { skipMe(); };"
|
|
|
|
"skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
|
|
|
|
Args));
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };",
|
2018-02-27 23:54:55 +08:00
|
|
|
Args));
|
2016-06-17 05:40:06 +08:00
|
|
|
|
|
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
|
2018-02-27 23:54:55 +08:00
|
|
|
Args));
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"struct skipMeNot { skipMeNot(); };"
|
|
|
|
"skipMeNot::skipMeNot() : an_error() { }",
|
|
|
|
Args));
|
|
|
|
|
|
|
|
// Try/catch
|
|
|
|
EXPECT_TRUE(runToolOnCode(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"void skipMe() try { an_error() } catch(error) { error };"));
|
|
|
|
EXPECT_TRUE(runToolOnCode(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"struct S { void skipMe() try { an_error() } catch(error) { error } };"));
|
|
|
|
EXPECT_TRUE(
|
2019-08-30 17:29:34 +08:00
|
|
|
runToolOnCode(std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"void skipMe() try { an_error() } catch(error) { error; }"
|
|
|
|
"catch(error) { error } catch (error) { }"));
|
|
|
|
EXPECT_FALSE(runToolOnCode(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 05:40:06 +08:00
|
|
|
"void skipMe() try something;")); // don't crash while parsing
|
|
|
|
|
|
|
|
// Template
|
2018-02-27 23:54:55 +08:00
|
|
|
EXPECT_TRUE(runToolOnCode(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }"
|
2018-02-27 23:54:55 +08:00
|
|
|
"int x = skipMe<int>();"));
|
2016-06-17 10:04:51 +08:00
|
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
std::make_unique<SkipBodyAction>(),
|
2016-06-17 10:04:51 +08:00
|
|
|
"template<typename T> int skipMeNot() { an_error_here }", Args2));
|
2012-11-28 05:31:01 +08:00
|
|
|
}
|
|
|
|
|
2014-03-03 07:37:26 +08:00
|
|
|
TEST(runToolOnCodeWithArgs, TestNoDepFile) {
|
|
|
|
llvm::SmallString<32> DepFilePath;
|
2018-03-19 22:20:25 +08:00
|
|
|
ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
|
|
|
|
DepFilePath));
|
2014-03-03 16:13:06 +08:00
|
|
|
std::vector<std::string> Args;
|
|
|
|
Args.push_back("-MMD");
|
|
|
|
Args.push_back("-MT");
|
2020-01-29 03:23:46 +08:00
|
|
|
Args.push_back(std::string(DepFilePath.str()));
|
2014-03-03 16:13:06 +08:00
|
|
|
Args.push_back("-MF");
|
2020-01-29 03:23:46 +08:00
|
|
|
Args.push_back(std::string(DepFilePath.str()));
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args));
|
2014-03-03 07:37:26 +08:00
|
|
|
EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
|
|
|
|
EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
|
|
|
|
}
|
|
|
|
|
2016-08-31 01:42:29 +08:00
|
|
|
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"));
|
2019-08-15 07:04:18 +08:00
|
|
|
return std::make_unique<ASTConsumer>();
|
2016-08-31 01:42:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool ShouldShowColor = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
|
|
|
|
EXPECT_TRUE(runToolOnCodeWithArgs(
|
2019-08-30 17:29:34 +08:00
|
|
|
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), "",
|
2016-08-31 01:42:29 +08:00
|
|
|
{"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
|
|
|
|
|
|
|
|
// Check that this test would fail if ShowColors is not what it should.
|
2019-08-30 17:29:34 +08:00
|
|
|
EXPECT_FALSE(runToolOnCodeWithArgs(
|
|
|
|
std::make_unique<CheckColoredDiagnosticsAction>(false), "",
|
|
|
|
{"-fcolor-diagnostics"}));
|
2016-08-31 01:42:29 +08:00
|
|
|
}
|
|
|
|
|
2013-06-04 22:44:44 +08:00
|
|
|
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() {}");
|
|
|
|
|
2014-04-24 11:48:09 +08:00
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
|
2013-06-04 22:44:44 +08:00
|
|
|
bool Found = false;
|
|
|
|
bool Ran = false;
|
2014-12-04 01:53:02 +08:00
|
|
|
ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
|
2015-11-05 10:19:53 +08:00
|
|
|
[&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
|
2014-12-04 01:53:02 +08:00
|
|
|
Ran = true;
|
2019-02-10 13:54:57 +08:00
|
|
|
if (llvm::is_contained(Args, "-fsyntax-only"))
|
2014-12-04 01:53:02 +08:00
|
|
|
Found = true;
|
|
|
|
return Args;
|
|
|
|
};
|
|
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
2014-04-24 11:48:09 +08:00
|
|
|
Tool.run(Action.get());
|
2013-06-04 22:44:44 +08:00
|
|
|
EXPECT_TRUE(Ran);
|
|
|
|
EXPECT_TRUE(Found);
|
|
|
|
|
|
|
|
Ran = Found = false;
|
|
|
|
Tool.clearArgumentsAdjusters();
|
2014-12-04 01:53:02 +08:00
|
|
|
Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
|
|
|
|
Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
|
2014-04-24 11:48:09 +08:00
|
|
|
Tool.run(Action.get());
|
2013-06-04 22:44:44 +08:00
|
|
|
EXPECT_TRUE(Ran);
|
|
|
|
EXPECT_FALSE(Found);
|
|
|
|
}
|
|
|
|
|
2019-07-02 18:45:53 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-11-13 20:44:40 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-01-23 20:30:02 +08:00
|
|
|
TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
|
|
|
|
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
|
|
|
|
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
|
|
new llvm::vfs::InMemoryFileSystem);
|
2018-01-23 20:30:02 +08:00
|
|
|
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()));
|
|
|
|
}
|
|
|
|
|
2017-11-18 00:27:21 +08:00
|
|
|
// 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) {
|
2019-03-31 16:48:19 +08:00
|
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
2017-11-18 00:27:21 +08:00
|
|
|
};
|
|
|
|
EXPECT_FALSE(HasFlag("-MD"));
|
|
|
|
EXPECT_FALSE(HasFlag("-MMD"));
|
|
|
|
EXPECT_TRUE(HasFlag("-c"));
|
|
|
|
EXPECT_TRUE(HasFlag("-w"));
|
|
|
|
}
|
|
|
|
|
2020-04-28 00:14:11 +08:00
|
|
|
// Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
|
2020-04-25 07:08:36 +08:00
|
|
|
TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
|
2020-04-28 00:14:11 +08:00
|
|
|
FixedCompilationDatabase Compilations(
|
|
|
|
"/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
|
|
|
|
"-showIncludes:user", "-c"});
|
2020-04-25 07:08:36 +08:00
|
|
|
|
|
|
|
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"));
|
2020-04-28 00:14:11 +08:00
|
|
|
EXPECT_FALSE(HasFlag("-showIncludes"));
|
|
|
|
EXPECT_FALSE(HasFlag("-showIncludes:user"));
|
2020-04-25 07:08:36 +08:00
|
|
|
EXPECT_TRUE(HasFlag("-c"));
|
|
|
|
}
|
|
|
|
|
2020-09-08 16:17:05 +08:00
|
|
|
// 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"));
|
|
|
|
}
|
|
|
|
|
2019-01-18 17:00:31 +08:00
|
|
|
// 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) {
|
2019-03-31 16:48:19 +08:00
|
|
|
return llvm::find(FinalArgs, Flag) != FinalArgs.end();
|
2019-01-18 17:00:31 +08:00
|
|
|
};
|
|
|
|
EXPECT_FALSE(HasFlag("-Xclang"));
|
|
|
|
EXPECT_FALSE(HasFlag("-add-plugin"));
|
|
|
|
EXPECT_FALSE(HasFlag("-random-plugin"));
|
|
|
|
}
|
|
|
|
|
2015-10-06 18:45:03 +08:00
|
|
|
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;
|
2015-10-06 21:58:13 +08:00
|
|
|
StringRef TargetName(Target.getName());
|
|
|
|
if (TargetName == "x86-64")
|
|
|
|
TargetName = "x86_64";
|
2020-01-29 03:23:46 +08:00
|
|
|
if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) ==
|
|
|
|
&Target) {
|
|
|
|
return std::string(TargetName);
|
2015-10-06 18:45:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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++");
|
2020-08-02 00:55:30 +08:00
|
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
|
2015-10-06 18:45:03 +08:00
|
|
|
"--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);
|
2020-08-02 00:55:30 +08:00
|
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
|
2015-10-06 18:45:03 +08:00
|
|
|
"--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);
|
|
|
|
|
2020-08-02 00:55:30 +08:00
|
|
|
std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"};
|
2015-10-06 18:45:03 +08:00
|
|
|
addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
|
|
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
|
2020-08-02 00:55:30 +08:00
|
|
|
"--target=something"}),
|
2015-10-06 18:45:03 +08:00
|
|
|
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++");
|
2020-08-02 00:55:30 +08:00
|
|
|
EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
|
2015-10-06 18:45:03 +08:00
|
|
|
"--driver-mode=abc"}),
|
|
|
|
Args);
|
|
|
|
}
|
|
|
|
|
2018-04-28 03:11:14 +08:00
|
|
|
#ifndef _WIN32
|
2013-11-07 04:12:45 +08:00
|
|
|
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() {}");
|
|
|
|
|
2014-04-25 22:49:37 +08:00
|
|
|
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
2013-11-07 04:12:45 +08:00
|
|
|
EXPECT_EQ(0, Tool.buildASTs(ASTs));
|
|
|
|
EXPECT_EQ(2u, ASTs.size());
|
|
|
|
}
|
|
|
|
|
2013-11-08 07:18:05 +08:00
|
|
|
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);
|
2014-04-24 11:48:09 +08:00
|
|
|
std::unique_ptr<FrontendActionFactory> Action(
|
|
|
|
newFrontendActionFactory<SyntaxOnlyAction>());
|
|
|
|
Tool.run(Action.get());
|
2013-11-08 07:18:05 +08:00
|
|
|
EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
|
|
|
|
}
|
|
|
|
|
2013-11-13 01:53:18 +08:00
|
|
|
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);
|
2014-04-25 22:49:37 +08:00
|
|
|
std::vector<std::unique_ptr<ASTUnit>> ASTs;
|
2013-11-13 01:53:18 +08:00
|
|
|
Tool.buildASTs(ASTs);
|
|
|
|
EXPECT_EQ(1u, ASTs.size());
|
|
|
|
EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
|
|
|
|
}
|
2013-11-13 08:18:50 +08:00
|
|
|
#endif
|
2013-11-13 01:53:18 +08:00
|
|
|
|
2017-11-23 16:15:22 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
2019-08-15 07:04:18 +08:00
|
|
|
return std::make_unique<Consumer>();
|
2017-11-23 16:15:22 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Should not crash
|
|
|
|
EXPECT_FALSE(
|
2019-08-30 17:29:34 +08:00
|
|
|
runToolOnCode(std::make_unique<ResetDiagnosticAction>(),
|
2017-11-23 16:15:22 +08:00
|
|
|
"struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
|
|
|
|
"void func() { long x; Foo f(x); }"));
|
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
} // end namespace tooling
|
|
|
|
} // end namespace clang
|