Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,37 @@
set(LLVM_LINK_COMPONENTS
support
)
get_filename_component(CLANG_LINT_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy REALPATH)
include_directories(${CLANG_LINT_SOURCE_DIR})
add_extra_unittest(ClangTidyTests
ClangTidyDiagnosticConsumerTest.cpp
ClangTidyOptionsTest.cpp
IncludeInserterTest.cpp
GoogleModuleTest.cpp
LLVMModuleTest.cpp
NamespaceAliaserTest.cpp
ObjCModuleTest.cpp
OverlappingReplacementsTest.cpp
UsingInserterTest.cpp
ReadabilityModuleTest.cpp)
target_link_libraries(ClangTidyTests
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangLex
clangTidy
clangTidyAndroidModule
clangTidyGoogleModule
clangTidyLLVMModule
clangTidyObjCModule
clangTidyReadabilityModule
clangTidyUtils
clangTooling
clangToolingCore
)

View File

@@ -0,0 +1,94 @@
#include "ClangTidy.h"
#include "ClangTidyTest.h"
#include "gtest/gtest.h"
namespace clang {
namespace tidy {
namespace test {
class TestCheck : public ClangTidyCheck {
public:
TestCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override {
Finder->addMatcher(ast_matchers::varDecl().bind("var"), this);
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var");
// Add diagnostics in the wrong order.
diag(Var->getLocation(), "variable");
diag(Var->getTypeSpecStartLoc(), "type specifier");
}
};
TEST(ClangTidyDiagnosticConsumer, SortsErrors) {
std::vector<ClangTidyError> Errors;
runCheckOnCode<TestCheck>("int a;", &Errors);
EXPECT_EQ(2ul, Errors.size());
EXPECT_EQ("type specifier", Errors[0].Message.Message);
EXPECT_EQ("variable", Errors[1].Message.Message);
}
TEST(GlobList, Empty) {
GlobList Filter("");
EXPECT_TRUE(Filter.contains(""));
EXPECT_FALSE(Filter.contains("aaa"));
}
TEST(GlobList, Nothing) {
GlobList Filter("-*");
EXPECT_FALSE(Filter.contains(""));
EXPECT_FALSE(Filter.contains("a"));
EXPECT_FALSE(Filter.contains("-*"));
EXPECT_FALSE(Filter.contains("-"));
EXPECT_FALSE(Filter.contains("*"));
}
TEST(GlobList, Everything) {
GlobList Filter("*");
EXPECT_TRUE(Filter.contains(""));
EXPECT_TRUE(Filter.contains("aaaa"));
EXPECT_TRUE(Filter.contains("-*"));
EXPECT_TRUE(Filter.contains("-"));
EXPECT_TRUE(Filter.contains("*"));
}
TEST(GlobList, Simple) {
GlobList Filter("aaa");
EXPECT_TRUE(Filter.contains("aaa"));
EXPECT_FALSE(Filter.contains(""));
EXPECT_FALSE(Filter.contains("aa"));
EXPECT_FALSE(Filter.contains("aaaa"));
EXPECT_FALSE(Filter.contains("bbb"));
}
TEST(GlobList, WhitespacesAtBegin) {
GlobList Filter("-*, a.b.*");
EXPECT_TRUE(Filter.contains("a.b.c"));
EXPECT_FALSE(Filter.contains("b.c"));
}
TEST(GlobList, Complex) {
GlobList Filter("*,-a.*, -b.*, \r \n a.1.* ,-a.1.A.*,-..,-...,-..+,-*$, -*qwe* ");
EXPECT_TRUE(Filter.contains("aaa"));
EXPECT_TRUE(Filter.contains("qqq"));
EXPECT_FALSE(Filter.contains("a."));
EXPECT_FALSE(Filter.contains("a.b"));
EXPECT_FALSE(Filter.contains("b."));
EXPECT_FALSE(Filter.contains("b.b"));
EXPECT_TRUE(Filter.contains("a.1.b"));
EXPECT_FALSE(Filter.contains("a.1.A.a"));
EXPECT_FALSE(Filter.contains("qwe"));
EXPECT_FALSE(Filter.contains("asdfqweasdf"));
EXPECT_TRUE(Filter.contains("asdfqwEasdf"));
}
} // namespace test
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,104 @@
#include "ClangTidyOptions.h"
#include "gtest/gtest.h"
#include "llvm/ADT/StringExtras.h"
namespace clang {
namespace tidy {
namespace test {
TEST(ParseLineFilter, EmptyFilter) {
ClangTidyGlobalOptions Options;
EXPECT_FALSE(parseLineFilter("", Options));
EXPECT_TRUE(Options.LineFilter.empty());
EXPECT_FALSE(parseLineFilter("[]", Options));
EXPECT_TRUE(Options.LineFilter.empty());
}
TEST(ParseLineFilter, InvalidFilter) {
ClangTidyGlobalOptions Options;
EXPECT_TRUE(!!parseLineFilter("asdf", Options));
EXPECT_TRUE(Options.LineFilter.empty());
EXPECT_TRUE(!!parseLineFilter("[{}]", Options));
EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options));
EXPECT_TRUE(
!!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options));
EXPECT_TRUE(
!!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options));
EXPECT_TRUE(
!!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options));
}
TEST(ParseLineFilter, ValidFilter) {
ClangTidyGlobalOptions Options;
std::error_code Error = parseLineFilter(
"[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"
"{\"name\":\"file2.h\"},"
"{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]",
Options);
EXPECT_FALSE(Error);
EXPECT_EQ(3u, Options.LineFilter.size());
EXPECT_EQ("file1.cpp", Options.LineFilter[0].Name);
EXPECT_EQ(3u, Options.LineFilter[0].LineRanges.size());
EXPECT_EQ(3u, Options.LineFilter[0].LineRanges[0].first);
EXPECT_EQ(15u, Options.LineFilter[0].LineRanges[0].second);
EXPECT_EQ(20u, Options.LineFilter[0].LineRanges[1].first);
EXPECT_EQ(30u, Options.LineFilter[0].LineRanges[1].second);
EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].first);
EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].second);
EXPECT_EQ("file2.h", Options.LineFilter[1].Name);
EXPECT_EQ(0u, Options.LineFilter[1].LineRanges.size());
EXPECT_EQ("file3.cc", Options.LineFilter[2].Name);
EXPECT_EQ(1u, Options.LineFilter[2].LineRanges.size());
EXPECT_EQ(100u, Options.LineFilter[2].LineRanges[0].first);
EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second);
}
TEST(ParseConfiguration, ValidConfiguration) {
llvm::ErrorOr<ClangTidyOptions> Options =
parseConfiguration("Checks: \"-*,misc-*\"\n"
"HeaderFilterRegex: \".*\"\n"
"AnalyzeTemporaryDtors: true\n"
"User: some.user");
EXPECT_TRUE(!!Options);
EXPECT_EQ("-*,misc-*", *Options->Checks);
EXPECT_EQ(".*", *Options->HeaderFilterRegex);
EXPECT_TRUE(*Options->AnalyzeTemporaryDtors);
EXPECT_EQ("some.user", *Options->User);
}
TEST(ParseConfiguration, MergeConfigurations) {
llvm::ErrorOr<ClangTidyOptions> Options1 = parseConfiguration(R"(
Checks: "check1,check2"
HeaderFilterRegex: "filter1"
AnalyzeTemporaryDtors: true
User: user1
ExtraArgs: ['arg1', 'arg2']
ExtraArgsBefore: ['arg-before1', 'arg-before2']
)");
ASSERT_TRUE(!!Options1);
llvm::ErrorOr<ClangTidyOptions> Options2 = parseConfiguration(R"(
Checks: "check3,check4"
HeaderFilterRegex: "filter2"
AnalyzeTemporaryDtors: false
User: user2
ExtraArgs: ['arg3', 'arg4']
ExtraArgsBefore: ['arg-before3', 'arg-before4']
)");
ASSERT_TRUE(!!Options2);
ClangTidyOptions Options = Options1->mergeWith(*Options2);
EXPECT_EQ("check1,check2,check3,check4", *Options.Checks);
EXPECT_EQ("filter2", *Options.HeaderFilterRegex);
EXPECT_FALSE(*Options.AnalyzeTemporaryDtors);
EXPECT_EQ("user2", *Options.User);
ASSERT_TRUE(Options.ExtraArgs.hasValue());
EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(),
Options.ExtraArgs->end(), ","));
ASSERT_TRUE(Options.ExtraArgsBefore.hasValue());
EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",
llvm::join(Options.ExtraArgsBefore->begin(),
Options.ExtraArgsBefore->end(), ","));
}
} // namespace test
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,159 @@
//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
#include "ClangTidy.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Path.h"
#include <map>
#include <memory>
namespace clang {
namespace tidy {
namespace test {
class TestClangTidyAction : public ASTFrontendAction {
public:
TestClangTidyAction(SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks,
ast_matchers::MatchFinder &Finder,
ClangTidyContext &Context)
: Checks(Checks), Finder(Finder), Context(Context) {}
private:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
StringRef File) override {
Context.setSourceManager(&Compiler.getSourceManager());
Context.setCurrentFile(File);
Context.setASTContext(&Compiler.getASTContext());
for (auto &Check : Checks) {
Check->registerMatchers(&Finder);
Check->registerPPCallbacks(Compiler);
}
return Finder.newASTConsumer();
}
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks;
ast_matchers::MatchFinder &Finder;
ClangTidyContext &Context;
};
template <typename Check, typename... Checks> struct CheckFactory {
static void
createChecks(ClangTidyContext *Context,
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
CheckFactory<Check>::createChecks(Context, Result);
CheckFactory<Checks...>::createChecks(Context, Result);
}
};
template <typename Check> struct CheckFactory<Check> {
static void
createChecks(ClangTidyContext *Context,
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
Result.emplace_back(llvm::make_unique<Check>(
"test-check-" + std::to_string(Result.size()), Context));
}
};
template <typename... CheckList>
std::string
runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
const Twine &Filename = "input.cc",
ArrayRef<std::string> ExtraArgs = None,
const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),
std::map<StringRef, StringRef> PathsToContent =
std::map<StringRef, StringRef>()) {
ClangTidyOptions Options = ExtraOptions;
Options.Checks = "*";
ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
ClangTidyGlobalOptions(), Options));
ClangTidyDiagnosticConsumer DiagConsumer(Context);
std::vector<std::string> Args(1, "clang-tidy");
Args.push_back("-fsyntax-only");
std::string extension(llvm::sys::path::extension(Filename.str()));
if (extension == ".m" || extension == ".mm") {
Args.push_back("-fobjc-abi-version=2");
Args.push_back("-fobjc-arc");
}
if (extension == ".cc" || extension == ".cpp" || extension == ".mm") {
Args.push_back("-std=c++11");
}
Args.push_back("-Iinclude");
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
Args.push_back(Filename.str());
ast_matchers::MatchFinder Finder;
llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
new vfs::InMemoryFileSystem);
llvm::IntrusiveRefCntPtr<FileManager> Files(
new FileManager(FileSystemOptions(), InMemoryFileSystem));
SmallVector<std::unique_ptr<ClangTidyCheck>, 1> Checks;
CheckFactory<CheckList...>::createChecks(&Context, Checks);
tooling::ToolInvocation Invocation(
Args, new TestClangTidyAction(Checks, Finder, Context), Files.get());
InMemoryFileSystem->addFile(Filename, 0,
llvm::MemoryBuffer::getMemBuffer(Code));
for (const auto &FileContent : PathsToContent) {
InMemoryFileSystem->addFile(
Twine("include/") + FileContent.first, 0,
llvm::MemoryBuffer::getMemBuffer(FileContent.second));
}
Invocation.setDiagnosticConsumer(&DiagConsumer);
if (!Invocation.run()) {
std::string ErrorText;
for (const auto &Error : Context.getErrors()) {
ErrorText += Error.Message.Message + "\n";
}
llvm::report_fatal_error(ErrorText);
}
DiagConsumer.finish();
tooling::Replacements Fixes;
for (const ClangTidyError &Error : Context.getErrors()) {
for (const auto &FileAndFixes : Error.Fix) {
for (const auto &Fix : FileAndFixes.second) {
auto Err = Fixes.add(Fix);
// FIXME: better error handling. Keep the behavior for now.
if (Err) {
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
return "";
}
}
}
}
if (Errors)
*Errors = Context.getErrors();
auto Result = tooling::applyAllReplacements(Code, Fixes);
if (!Result) {
// FIXME: propogate the error.
llvm::consumeError(Result.takeError());
return "";
}
return *Result;
}
#define EXPECT_NO_CHANGES(Check, Code) \
EXPECT_EQ(Code, runCheckOnCode<Check>(Code))
} // namespace test
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H

View File

@@ -0,0 +1,110 @@
#include "ClangTidyTest.h"
#include "google/ExplicitConstructorCheck.h"
#include "google/GlobalNamesInHeadersCheck.h"
#include "gtest/gtest.h"
using namespace clang::tidy::google;
namespace clang {
namespace tidy {
namespace test {
TEST(ExplicitConstructorCheckTest, SingleArgumentConstructorsOnly) {
EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(); };");
EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(int i, int j); };");
EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(const C&); };");
EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(C&&); };");
EXPECT_NO_CHANGES(ExplicitConstructorCheck,
"class C { C(const C&) = delete; };");
EXPECT_NO_CHANGES(ExplicitConstructorCheck,
"class C { C(int) = delete; };");
}
TEST(ExplicitConstructorCheckTest, Basic) {
EXPECT_EQ("class C { explicit C(int i); };",
runCheckOnCode<ExplicitConstructorCheck>("class C { C(int i); };"));
}
TEST(ExplicitConstructorCheckTest, DefaultParameters) {
EXPECT_EQ("class C { explicit C(int i, int j = 0); };",
runCheckOnCode<ExplicitConstructorCheck>(
"class C { C(int i, int j = 0); };"));
}
TEST(ExplicitConstructorCheckTest, OutOfLineDefinitions) {
EXPECT_EQ("class C { explicit C(int i); }; C::C(int i) {}",
runCheckOnCode<ExplicitConstructorCheck>(
"class C { C(int i); }; C::C(int i) {}"));
}
TEST(ExplicitConstructorCheckTest, RemoveExplicit) {
EXPECT_EQ("class A { A(const A&); };\n"
"class B { /*asdf*/ B(B&&); };\n"
"class C { /*asdf*/ C(const C&, int i = 0); };",
runCheckOnCode<ExplicitConstructorCheck>(
"class A { explicit A(const A&); };\n"
"class B { explicit /*asdf*/ B(B&&); };\n"
"class C { explicit/*asdf*/ C(const C&, int i = 0); };"));
}
TEST(ExplicitConstructorCheckTest, RemoveExplicitWithMacros) {
EXPECT_EQ(
"#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
"A(Foo);",
runCheckOnCode<ExplicitConstructorCheck>(
"#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
"A(Foo);"));
}
class GlobalNamesInHeadersCheckTest : public ::testing::Test {
protected:
bool runCheckOnCode(const std::string &Code, const std::string &Filename) {
static const char *const Header = "namespace std {\n"
"class string {};\n"
"} // namespace std\n"
"\n"
"#define SOME_MACRO(x) using x\n";
std::vector<ClangTidyError> Errors;
std::vector<std::string> Args;
if (!StringRef(Filename).endswith(".cpp")) {
Args.emplace_back("-xc++-header");
}
test::runCheckOnCode<readability::GlobalNamesInHeadersCheck>(
Header + Code, &Errors, Filename, Args);
if (Errors.empty())
return false;
assert(Errors.size() == 1);
assert(
Errors[0].Message.Message ==
"using declarations in the global namespace in headers are prohibited");
return true;
}
};
TEST_F(GlobalNamesInHeadersCheckTest, UsingDeclarations) {
EXPECT_TRUE(runCheckOnCode("using std::string;", "foo.h"));
EXPECT_FALSE(runCheckOnCode("using std::string;", "foo.cpp"));
EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
"using std::string;\n"
"} // my_namespace\n",
"foo.h"));
EXPECT_FALSE(runCheckOnCode("SOME_MACRO(std::string);", "foo.h"));
}
TEST_F(GlobalNamesInHeadersCheckTest, UsingDirectives) {
EXPECT_TRUE(runCheckOnCode("using namespace std;", "foo.h"));
EXPECT_FALSE(runCheckOnCode("using namespace std;", "foo.cpp"));
EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
"using namespace std;\n"
"} // my_namespace\n",
"foo.h"));
EXPECT_FALSE(runCheckOnCode("SOME_MACRO(namespace std);", "foo.h"));
}
TEST_F(GlobalNamesInHeadersCheckTest, RegressionAnonymousNamespace) {
EXPECT_FALSE(runCheckOnCode("namespace {}", "foo.h"));
}
} // namespace test
} // namespace tidy
} // namespace clang

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,219 @@
#include "ClangTidyTest.h"
#include "llvm/HeaderGuardCheck.h"
#include "llvm/IncludeOrderCheck.h"
#include "gtest/gtest.h"
using namespace clang::tidy::llvm;
namespace clang {
namespace tidy {
namespace test {
// FIXME: It seems this might be incompatible to dos path. Investigating.
#if !defined(_WIN32)
static std::string runHeaderGuardCheck(StringRef Code, const Twine &Filename,
Optional<StringRef> ExpectedWarning) {
std::vector<ClangTidyError> Errors;
std::string Result = test::runCheckOnCode<LLVMHeaderGuardCheck>(
Code, &Errors, Filename, std::string("-xc++-header"));
if (Errors.size() != (size_t)ExpectedWarning.hasValue())
return "invalid error count";
if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
return "expected: '" + ExpectedWarning->str() + "', saw: '" +
Errors.back().Message.Message + "'";
return Result;
}
namespace {
struct WithEndifComment : public LLVMHeaderGuardCheck {
WithEndifComment(StringRef Name, ClangTidyContext *Context)
: LLVMHeaderGuardCheck(Name, Context) {}
bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
};
} // namespace
static std::string
runHeaderGuardCheckWithEndif(StringRef Code, const Twine &Filename,
Optional<StringRef> ExpectedWarning) {
std::vector<ClangTidyError> Errors;
std::string Result = test::runCheckOnCode<WithEndifComment>(
Code, &Errors, Filename, std::string("-xc++-header"));
if (Errors.size() != (size_t)ExpectedWarning.hasValue())
return "invalid error count";
if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
return "expected: '" + ExpectedWarning->str() + "', saw: '" +
Errors.back().Message.Message + "'";
return Result;
}
TEST(LLVMHeaderGuardCheckTest, FixHeaderGuards) {
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif\n",
runHeaderGuardCheck(
"#ifndef FOO\n"
"#define FOO\n"
"#endif\n",
"include/llvm/ADT/foo.h",
StringRef("header guard does not follow preferred style")));
// Allow trailing underscores.
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
"#define LLVM_ADT_FOO_H_\n"
"#endif\n",
runHeaderGuardCheck("#ifndef LLVM_ADT_FOO_H_\n"
"#define LLVM_ADT_FOO_H_\n"
"#endif\n",
"include/llvm/ADT/foo.h", None));
EXPECT_EQ("#ifndef LLVM_CLANG_C_BAR_H\n"
"#define LLVM_CLANG_C_BAR_H\n"
"\n"
"\n"
"#endif\n",
runHeaderGuardCheck("", "./include/clang-c/bar.h",
StringRef("header is missing header guard")));
EXPECT_EQ("#ifndef LLVM_CLANG_LIB_CODEGEN_C_H\n"
"#define LLVM_CLANG_LIB_CODEGEN_C_H\n"
"\n"
"\n"
"#endif\n",
runHeaderGuardCheck("", "tools/clang/lib/CodeGen/c.h",
StringRef("header is missing header guard")));
EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
"#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
"\n"
"\n"
"#endif\n",
runHeaderGuardCheck("", "tools/clang/tools/extra/clang-tidy/x.h",
StringRef("header is missing header guard")));
EXPECT_EQ(
"int foo;\n"
"#ifndef LLVM_CLANG_BAR_H\n"
"#define LLVM_CLANG_BAR_H\n"
"#endif\n",
runHeaderGuardCheck("int foo;\n"
"#ifndef LLVM_CLANG_BAR_H\n"
"#define LLVM_CLANG_BAR_H\n"
"#endif\n",
"include/clang/bar.h",
StringRef("code/includes outside of area guarded by "
"header guard; consider moving it")));
EXPECT_EQ(
"#ifndef LLVM_CLANG_BAR_H\n"
"#define LLVM_CLANG_BAR_H\n"
"#endif\n"
"int foo;\n",
runHeaderGuardCheck("#ifndef LLVM_CLANG_BAR_H\n"
"#define LLVM_CLANG_BAR_H\n"
"#endif\n"
"int foo;\n",
"include/clang/bar.h",
StringRef("code/includes outside of area guarded by "
"header guard; consider moving it")));
EXPECT_EQ("#ifndef LLVM_CLANG_BAR_H\n"
"#define LLVM_CLANG_BAR_H\n"
"\n"
"int foo;\n"
"#ifndef FOOLOLO\n"
"#define FOOLOLO\n"
"#endif\n"
"\n"
"#endif\n",
runHeaderGuardCheck("int foo;\n"
"#ifndef FOOLOLO\n"
"#define FOOLOLO\n"
"#endif\n",
"include/clang/bar.h",
StringRef("header is missing header guard")));
// Fix incorrect #endif comments even if we shouldn't add new ones.
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif // LLVM_ADT_FOO_H\n",
runHeaderGuardCheck(
"#ifndef FOO\n"
"#define FOO\n"
"#endif // FOO\n",
"include/llvm/ADT/foo.h",
StringRef("header guard does not follow preferred style")));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif // LLVM_ADT_FOO_H\n",
runHeaderGuardCheckWithEndif(
"#ifndef FOO\n"
"#define FOO\n"
"#endif\n",
"include/llvm/ADT/foo.h",
StringRef("header guard does not follow preferred style")));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif // LLVM_ADT_FOO_H\n",
runHeaderGuardCheckWithEndif(
"#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif // LLVM_H\n",
"include/llvm/ADT/foo.h",
StringRef("#endif for a header guard should reference the "
"guard macro in a comment")));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif /* LLVM_ADT_FOO_H */\n",
runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif /* LLVM_ADT_FOO_H */\n",
"include/llvm/ADT/foo.h", None));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
"#define LLVM_ADT_FOO_H_\n"
"#endif // LLVM_ADT_FOO_H_\n",
runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H_\n"
"#define LLVM_ADT_FOO_H_\n"
"#endif // LLVM_ADT_FOO_H_\n",
"include/llvm/ADT/foo.h", None));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif // LLVM_ADT_FOO_H\n",
runHeaderGuardCheckWithEndif(
"#ifndef LLVM_ADT_FOO_H_\n"
"#define LLVM_ADT_FOO_H_\n"
"#endif // LLVM\n",
"include/llvm/ADT/foo.h",
StringRef("header guard does not follow preferred style")));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif \\ \n"
"// LLVM_ADT_FOO_H\n",
runHeaderGuardCheckWithEndif(
"#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif \\ \n"
"// LLVM_ADT_FOO_H\n",
"include/llvm/ADT/foo.h",
StringRef("backslash and newline separated by space")));
EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif /* LLVM_ADT_FOO_H\\ \n"
" FOO */",
runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
"#define LLVM_ADT_FOO_H\n"
"#endif /* LLVM_ADT_FOO_H\\ \n"
" FOO */",
"include/llvm/ADT/foo.h", None));
}
#endif
} // namespace test
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,123 @@
//===---- NamespaceAliaserTest.cpp - clang-tidy
//----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../clang-tidy/utils/NamespaceAliaser.h"
#include "ClangTidyTest.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "gtest/gtest.h"
namespace clang {
namespace tidy {
namespace utils {
// This checker is for testing only. It can only run on one test case
// (e.g. with one SourceManager).
class InsertAliasCheck : public ClangTidyCheck {
public:
InsertAliasCheck(StringRef Name, ClangTidyContext *Context)
:ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override {
Finder->addMatcher(ast_matchers::callExpr().bind("foo"), this);
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
if (!Aliaser)
Aliaser.reset(new NamespaceAliaser(*Result.SourceManager));
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("foo");
assert(Call != nullptr && "Did not find node \"foo\"");
auto Hint = Aliaser->createAlias(*Result.Context, *Call, "::foo::bar",
{"b", "some_alias"});
if (Hint.hasValue())
diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
diag(Call->getLocStart(), "insert call")
<< FixItHint::CreateInsertion(
Call->getLocStart(),
Aliaser->getNamespaceName(*Result.Context, *Call, "::foo::bar") +
"::");
}
private:
std::unique_ptr<NamespaceAliaser> Aliaser;
};
template <typename Check>
std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
"namespace foo {\n"
"namespace bar {\n"
"}\n"
"void func() { }\n"
"}"}};
std::vector<ClangTidyError> errors;
std::string result =
test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
ClangTidyOptions(), AdditionalFileContents);
EXPECT_EQ(ExpectedWarningCount, errors.size());
return result;
}
TEST(NamespaceAliaserTest, AddNewAlias) {
EXPECT_EQ("#include \"foo.h\"\n"
"void f() {\n"
"namespace b = ::foo::bar;"
" b::f(); }",
runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
"void f() { f(); }",
2));
}
TEST(NamespaceAliaserTest, ReuseAlias) {
EXPECT_EQ(
"#include \"foo.h\"\n"
"void f() { namespace x = foo::bar; x::f(); }",
runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
"void f() { namespace x = foo::bar; f(); }",
1));
}
TEST(NamespaceAliaserTest, AddsOnlyOneAlias) {
EXPECT_EQ("#include \"foo.h\"\n"
"void f() {\n"
"namespace b = ::foo::bar;"
" b::f(); b::f(); }",
runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
"void f() { f(); f(); }",
3));
}
TEST(NamespaceAliaserTest, LocalConflict) {
EXPECT_EQ("#include \"foo.h\"\n"
"void f() {\n"
"namespace some_alias = ::foo::bar;"
" namespace b = foo; some_alias::f(); }",
runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
"void f() { namespace b = foo; f(); }",
2));
}
TEST(NamespaceAliaserTest, GlobalConflict) {
EXPECT_EQ("#include \"foo.h\"\n"
"namespace b = foo;\n"
"void f() {\n"
"namespace some_alias = ::foo::bar;"
" some_alias::f(); }",
runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
"namespace b = foo;\n"
"void f() { f(); }",
2));
}
} // namespace utils
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,49 @@
//===---- ObjCModuleTest.cpp - clang-tidy ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangTidyTest.h"
#include "gtest/gtest.h"
#include "objc/ForbiddenSubclassingCheck.h"
using namespace clang::tidy::objc;
namespace clang {
namespace tidy {
namespace test {
TEST(ObjCForbiddenSubclassing, AllowedSubclass) {
std::vector<ClangTidyError> Errors;
runCheckOnCode<ForbiddenSubclassingCheck>(
"@interface Foo\n"
"@end\n"
"@interface Bar : Foo\n"
"@end\n",
&Errors,
"input.m");
EXPECT_EQ(0ul, Errors.size());
}
TEST(ObjCForbiddenSubclassing, ForbiddenSubclass) {
std::vector<ClangTidyError> Errors;
runCheckOnCode<ForbiddenSubclassingCheck>(
"@interface UIImagePickerController\n"
"@end\n"
"@interface Foo : UIImagePickerController\n"
"@end\n",
&Errors,
"input.m");
EXPECT_EQ(1ul, Errors.size());
EXPECT_EQ(
"Objective-C interface 'Foo' subclasses 'UIImagePickerController', which is not intended to be subclassed",
Errors[0].Message.Message);
}
} // namespace test
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,409 @@
//===---- OverlappingReplacementsTest.cpp - clang-tidy --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangTidyTest.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "gtest/gtest.h"
namespace clang {
namespace tidy {
namespace test {
namespace {
const char BoundDecl[] = "decl";
const char BoundIf[] = "if";
// We define a reduced set of very small checks that allow to test different
// overlapping situations (no overlapping, replacements partially overlap, etc),
// as well as different kinds of diagnostics (one check produces several errors,
// several replacement ranges in an error, etc).
class UseCharCheck : public ClangTidyCheck {
public:
UseCharCheck(StringRef CheckName, ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override {
using namespace ast_matchers;
Finder->addMatcher(varDecl(hasType(isInteger())).bind(BoundDecl), this);
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
diag(VD->getLocStart(), "use char") << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(VD->getLocStart(), VD->getLocStart()),
"char");
}
};
class IfFalseCheck : public ClangTidyCheck {
public:
IfFalseCheck(StringRef CheckName, ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override {
using namespace ast_matchers;
Finder->addMatcher(ifStmt().bind(BoundIf), this);
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
auto *If = Result.Nodes.getNodeAs<IfStmt>(BoundIf);
auto *Cond = If->getCond();
SourceRange Range = Cond->getSourceRange();
if (auto *D = If->getConditionVariable()) {
Range = SourceRange(D->getLocStart(), D->getLocEnd());
}
diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(Range), "false");
}
};
class RefactorCheck : public ClangTidyCheck {
public:
RefactorCheck(StringRef CheckName, ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context), NamePattern("::$") {}
RefactorCheck(StringRef CheckName, ClangTidyContext *Context,
StringRef NamePattern)
: ClangTidyCheck(CheckName, Context), NamePattern(NamePattern) {}
virtual std::string newName(StringRef OldName) = 0;
void registerMatchers(ast_matchers::MatchFinder *Finder) final {
using namespace ast_matchers;
Finder->addMatcher(varDecl(matchesName(NamePattern)).bind(BoundDecl), this);
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) final {
auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
std::string NewName = newName(VD->getName());
auto Diag = diag(VD->getLocation(), "refactor %0 into %1")
<< VD->getName() << NewName
<< FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(VD->getLocation(),
VD->getLocation()),
NewName);
class UsageVisitor : public RecursiveASTVisitor<UsageVisitor> {
public:
UsageVisitor(const ValueDecl *VD, StringRef NewName,
DiagnosticBuilder &Diag)
: VD(VD), NewName(NewName), Diag(Diag) {}
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (const ValueDecl *D = E->getDecl()) {
if (VD->getCanonicalDecl() == D->getCanonicalDecl()) {
Diag << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(E->getSourceRange()), NewName);
}
}
return RecursiveASTVisitor<UsageVisitor>::VisitDeclRefExpr(E);
}
private:
const ValueDecl *VD;
StringRef NewName;
DiagnosticBuilder &Diag;
};
UsageVisitor(VD, NewName, Diag)
.TraverseDecl(Result.Context->getTranslationUnitDecl());
}
protected:
const std::string NamePattern;
};
class StartsWithPotaCheck : public RefactorCheck {
public:
StartsWithPotaCheck(StringRef CheckName, ClangTidyContext *Context)
: RefactorCheck(CheckName, Context, "::pota") {}
std::string newName(StringRef OldName) override {
return "toma" + OldName.substr(4).str();
}
};
class EndsWithTatoCheck : public RefactorCheck {
public:
EndsWithTatoCheck(StringRef CheckName, ClangTidyContext *Context)
: RefactorCheck(CheckName, Context, "tato$") {}
std::string newName(StringRef OldName) override {
return OldName.substr(0, OldName.size() - 4).str() + "melo";
}
};
} // namespace
TEST(OverlappingReplacementsTest, UseCharCheckTest) {
const char Code[] =
R"(void f() {
int a = 0;
if (int b = 0) {
int c = a;
}
})";
const char CharFix[] =
R"(void f() {
char a = 0;
if (char b = 0) {
char c = a;
}
})";
EXPECT_EQ(CharFix, runCheckOnCode<UseCharCheck>(Code));
}
TEST(OverlappingReplacementsTest, IfFalseCheckTest) {
const char Code[] =
R"(void f() {
int potato = 0;
if (int b = 0) {
int c = potato;
} else if (true) {
int d = 0;
}
})";
const char IfFix[] =
R"(void f() {
int potato = 0;
if (false) {
int c = potato;
} else if (false) {
int d = 0;
}
})";
EXPECT_EQ(IfFix, runCheckOnCode<IfFalseCheck>(Code));
}
TEST(OverlappingReplacementsTest, StartsWithCheckTest) {
const char Code[] =
R"(void f() {
int a = 0;
int potato = 0;
if (int b = 0) {
int c = potato;
} else if (true) {
int d = 0;
}
})";
const char StartsFix[] =
R"(void f() {
int a = 0;
int tomato = 0;
if (int b = 0) {
int c = tomato;
} else if (true) {
int d = 0;
}
})";
EXPECT_EQ(StartsFix, runCheckOnCode<StartsWithPotaCheck>(Code));
}
TEST(OverlappingReplacementsTest, EndsWithCheckTest) {
const char Code[] =
R"(void f() {
int a = 0;
int potato = 0;
if (int b = 0) {
int c = potato;
} else if (true) {
int d = 0;
}
})";
const char EndsFix[] =
R"(void f() {
int a = 0;
int pomelo = 0;
if (int b = 0) {
int c = pomelo;
} else if (true) {
int d = 0;
}
})";
EXPECT_EQ(EndsFix, runCheckOnCode<EndsWithTatoCheck>(Code));
}
TEST(OverlappingReplacementTest, ReplacementsDoNotOverlap) {
std::string Res;
const char Code[] =
R"(void f() {
int potassium = 0;
if (true) {
int Potato = potassium;
}
})";
const char CharIfFix[] =
R"(void f() {
char potassium = 0;
if (false) {
char Potato = potassium;
}
})";
Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
EXPECT_EQ(CharIfFix, Res);
const char StartsEndsFix[] =
R"(void f() {
int tomassium = 0;
if (true) {
int Pomelo = tomassium;
}
})";
Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
EXPECT_EQ(StartsEndsFix, Res);
const char CharIfStartsEndsFix[] =
R"(void f() {
char tomassium = 0;
if (false) {
char Pomelo = tomassium;
}
})";
Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck,
EndsWithTatoCheck>(Code);
EXPECT_EQ(CharIfStartsEndsFix, Res);
}
TEST(OverlappingReplacementsTest, ReplacementInsideOtherReplacement) {
std::string Res;
const char Code[] =
R"(void f() {
if (char potato = 0) {
} else if (int a = 0) {
char potato = 0;
if (potato) potato;
}
})";
// Apply the UseCharCheck together with the IfFalseCheck.
//
// The 'If' fix contains the other, so that is the one that has to be applied.
// } else if (int a = 0) {
// ^^^ -> char
// ~~~~~~~~~ -> false
const char CharIfFix[] =
R"(void f() {
if (false) {
} else if (false) {
char potato = 0;
if (false) potato;
}
})";
Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
EXPECT_EQ(CharIfFix, Res);
Res = runCheckOnCode<IfFalseCheck, UseCharCheck>(Code);
EXPECT_EQ(CharIfFix, Res);
// Apply the IfFalseCheck with the StartsWithPotaCheck.
//
// The 'If' replacement is bigger here.
// if (char potato = 0) {
// ^^^^^^ -> tomato
// ~~~~~~~~~~~~~~~ -> false
//
// But the refactoring is the one that contains the other here:
// char potato = 0;
// ^^^^^^ -> tomato
// if (potato) potato;
// ^^^^^^ ^^^^^^ -> tomato, tomato
// ~~~~~~ -> false
const char IfStartsFix[] =
R"(void f() {
if (false) {
} else if (false) {
char tomato = 0;
if (tomato) tomato;
}
})";
Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
EXPECT_EQ(IfStartsFix, Res);
Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck>(Code);
EXPECT_EQ(IfStartsFix, Res);
}
TEST(OverlappingReplacements, TwoReplacementsInsideOne) {
std::string Res;
const char Code[] =
R"(void f() {
if (int potato = 0) {
int a = 0;
}
})";
// The two smallest replacements should not be applied.
// if (int potato = 0) {
// ^^^^^^ -> tomato
// *** -> char
// ~~~~~~~~~~~~~~ -> false
// But other errors from the same checks should not be affected.
// int a = 0;
// *** -> char
const char Fix[] =
R"(void f() {
if (false) {
char a = 0;
}
})";
Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck>(Code);
EXPECT_EQ(Fix, Res);
Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck, UseCharCheck>(Code);
EXPECT_EQ(Fix, Res);
}
TEST(OverlappingReplacementsTest,
ApplyAtMostOneOfTheChangesWhenPartialOverlapping) {
std::string Res;
const char Code[] =
R"(void f() {
if (int potato = 0) {
int a = potato;
}
})";
// These two replacements overlap, but none of them is completely contained
// inside the other.
// if (int potato = 0) {
// ^^^^^^ -> tomato
// ~~~~~~~~~~~~~~ -> false
// int a = potato;
// ^^^^^^ -> tomato
//
// The 'StartsWithPotaCheck' fix has endpoints inside the 'IfFalseCheck' fix,
// so it is going to be set as inapplicable. The 'if' fix will be applied.
const char IfFix[] =
R"(void f() {
if (false) {
int a = potato;
}
})";
Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
EXPECT_EQ(IfFix, Res);
}
TEST(OverlappingReplacementsTest, TwoErrorsHavePerfectOverlapping) {
std::string Res;
const char Code[] =
R"(void f() {
int potato = 0;
potato += potato * potato;
if (char a = potato) potato;
})";
// StartsWithPotaCheck will try to refactor 'potato' into 'tomato', and
// EndsWithTatoCheck will try to use 'pomelo'. Both fixes have the same set of
// ranges. This is a corner case of one error completely containing another:
// the other completely contains the first one as well. Both errors are
// discarded.
Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
EXPECT_EQ(Code, Res);
}
} // namespace test
} // namespace tidy
} // namespace clang

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
//===---- UsingInserterTest.cpp - clang-tidy ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../clang-tidy/utils/UsingInserter.h"
#include "ClangTidyTest.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "gtest/gtest.h"
namespace clang {
namespace tidy {
namespace utils {
// Replace all function calls with calls to foo::func. Inserts using
// declarations as necessary. This checker is for testing only. It
// can only run on one test case (e.g. wih one SourceManager).
class InsertUsingCheck : public clang::tidy::ClangTidyCheck {
public:
InsertUsingCheck(StringRef Name, ClangTidyContext *Context)
:ClangTidyCheck(Name, Context) {}
void registerMatchers(clang::ast_matchers::MatchFinder *Finder) override {
Finder->addMatcher(clang::ast_matchers::callExpr().bind("foo"), this);
}
void
check(const clang::ast_matchers::MatchFinder::MatchResult &Result) override {
if (!Inserter)
Inserter.reset(new UsingInserter(*Result.SourceManager));
const auto *Call = Result.Nodes.getNodeAs<clang::CallExpr>("foo");
assert(Call != nullptr && "Did not find node \"foo\"");
auto Hint =
Inserter->createUsingDeclaration(*Result.Context, *Call, "::foo::func");
if (Hint.hasValue())
diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
diag(Call->getLocStart(), "insert call")
<< clang::FixItHint::CreateReplacement(
Call->getCallee()->getSourceRange(),
Inserter->getShortName(*Result.Context, *Call, "::foo::func"));
}
private:
std::unique_ptr<UsingInserter> Inserter;
};
template <typename Check>
std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
"namespace foo {\n"
"namespace bar {\n"
"}\n"
"void func() { }\n"
"}"}};
std::vector<ClangTidyError> errors;
std::string result =
test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
ClangTidyOptions(), AdditionalFileContents);
EXPECT_EQ(ExpectedWarningCount, errors.size());
return result;
}
TEST(UsingInserterTest, ReusesExisting) {
EXPECT_EQ("#include \"foo.h\"\n"
"namespace {"
"using ::foo::func;\n"
"void f() { func(); }"
"}",
runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
"namespace {"
"using ::foo::func;\n"
"void f() { f(); }"
"}",
1));
}
TEST(UsingInserterTest, ReusesExistingGlobal) {
EXPECT_EQ("#include \"foo.h\"\n"
"using ::foo::func;\n"
"namespace {"
"void f() { func(); }"
"}",
runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
"using ::foo::func;\n"
"namespace {"
"void f() { f(); }"
"}",
1));
}
TEST(UsingInserterTest, AvoidsConflict) {
EXPECT_EQ("#include \"foo.h\"\n"
"namespace {"
"void f() { int func; ::foo::func(); }"
"}",
runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
"namespace {"
"void f() { int func; f(); }"
"}",
1));
}
} // namespace utils
} // namespace tidy
} // namespace clang