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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Support
)
# By default MSVC has a 2^16 limit on the number of sections in an object file,
# and this needs more than that.
if (MSVC)
set_source_files_properties(RecursiveASTVisitorTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
set_source_files_properties(RecursiveASTVisitorTestExprVisitor.cpp PROPERTIES COMPILE_FLAGS /bigobj)
endif()
add_clang_unittest(ToolingTests
ASTSelectionTest.cpp
CastExprTest.cpp
CommentHandlerTest.cpp
CompilationDatabaseTest.cpp
DiagnosticsYamlTest.cpp
ExecutionTest.cpp
FixItTest.cpp
LexicallyOrderedRecursiveASTVisitorTest.cpp
LookupTest.cpp
QualTypeNamesTest.cpp
RecursiveASTVisitorTest.cpp
RecursiveASTVisitorTestCallVisitor.cpp
RecursiveASTVisitorTestDeclVisitor.cpp
RecursiveASTVisitorTestExprVisitor.cpp
RecursiveASTVisitorTestTypeLocVisitor.cpp
RefactoringActionRulesTest.cpp
RefactoringCallbacksTest.cpp
RefactoringTest.cpp
ReplacementsYamlTest.cpp
RewriterTest.cpp
ToolingTest.cpp
)
target_link_libraries(ToolingTests
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangRewrite
clangTooling
clangToolingCore
clangToolingRefactor
)

View File

@@ -0,0 +1,38 @@
//===- unittest/Tooling/CastExprTest.cpp ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
struct CastExprVisitor : TestVisitor<CastExprVisitor> {
std::function<void(ExplicitCastExpr *)> OnExplicitCast;
bool VisitExplicitCastExpr(ExplicitCastExpr *Expr) {
if (OnExplicitCast)
OnExplicitCast(Expr);
return true;
}
};
TEST(CastExprTest, GetSubExprAsWrittenThroughMaterializedTemporary) {
CastExprVisitor Visitor;
Visitor.OnExplicitCast = [](ExplicitCastExpr *Expr) {
auto Sub = Expr->getSubExprAsWritten();
EXPECT_TRUE(isa<DeclRefExpr>(Sub))
<< "Expected DeclRefExpr, but saw " << Sub->getStmtClassName();
};
Visitor.runOver("struct S1 {};\n"
"struct S2 { operator S1(); };\n"
"S1 f(S2 s) { return static_cast<S1>(s); }\n");
}
}

View File

@@ -0,0 +1,224 @@
//===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include "clang/Lex/Preprocessor.h"
namespace clang {
struct Comment {
Comment(const std::string &Message, unsigned Line, unsigned Col)
: Message(Message), Line(Line), Col(Col) { }
std::string Message;
unsigned Line, Col;
};
class CommentVerifier;
typedef std::vector<Comment> CommentList;
class CommentHandlerVisitor : public TestVisitor<CommentHandlerVisitor>,
public CommentHandler {
typedef TestVisitor<CommentHandlerVisitor> base;
public:
CommentHandlerVisitor() : base(), PP(nullptr), Verified(false) {}
~CommentHandlerVisitor() override {
EXPECT_TRUE(Verified) << "CommentVerifier not accessed";
}
bool HandleComment(Preprocessor &PP, SourceRange Loc) override {
assert(&PP == this->PP && "Preprocessor changed!");
SourceLocation Start = Loc.getBegin();
SourceManager &SM = PP.getSourceManager();
std::string C(SM.getCharacterData(Start),
SM.getCharacterData(Loc.getEnd()));
bool Invalid;
unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid);
EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C;
unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid);
EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C;
Comments.push_back(Comment(C, CLine, CCol));
return false;
}
CommentVerifier GetVerifier();
protected:
ASTFrontendAction *CreateTestAction() override {
return new CommentHandlerAction(this);
}
private:
Preprocessor *PP;
CommentList Comments;
bool Verified;
class CommentHandlerAction : public base::TestAction {
public:
CommentHandlerAction(CommentHandlerVisitor *Visitor)
: TestAction(Visitor) { }
bool BeginSourceFileAction(CompilerInstance &CI) override {
CommentHandlerVisitor *V =
static_cast<CommentHandlerVisitor*>(this->Visitor);
V->PP = &CI.getPreprocessor();
V->PP->addCommentHandler(V);
return true;
}
void EndSourceFileAction() override {
CommentHandlerVisitor *V =
static_cast<CommentHandlerVisitor*>(this->Visitor);
V->PP->removeCommentHandler(V);
}
};
};
class CommentVerifier {
CommentList::const_iterator Current;
CommentList::const_iterator End;
Preprocessor *PP;
public:
CommentVerifier(const CommentList &Comments, Preprocessor *PP)
: Current(Comments.begin()), End(Comments.end()), PP(PP)
{ }
CommentVerifier(CommentVerifier &&C) : Current(C.Current), End(C.End), PP(C.PP) {
C.Current = C.End;
}
~CommentVerifier() {
if (Current != End) {
EXPECT_TRUE(Current == End) << "Unexpected comment \""
<< Current->Message << "\" at line " << Current->Line << ", column "
<< Current->Col;
}
}
void Match(const char *Message, unsigned Line, unsigned Col) {
EXPECT_TRUE(Current != End) << "Comment " << Message << " not found";
if (Current == End) return;
const Comment &C = *Current;
EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col)
<< "Expected comment \"" << Message
<< "\" at line " << Line << ", column " << Col
<< "\nActual comment \"" << C.Message
<< "\" at line " << C.Line << ", column " << C.Col;
++Current;
}
};
CommentVerifier CommentHandlerVisitor::GetVerifier() {
Verified = true;
return CommentVerifier(Comments, PP);
}
TEST(CommentHandlerTest, BasicTest1) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }"));
CommentVerifier Verifier = Visitor.GetVerifier();
}
TEST(CommentHandlerTest, BasicTest2) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(
"class X {}; int main() { /* comment */ return 0; }"));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match("/* comment */", 1, 26);
}
TEST(CommentHandlerTest, BasicTest3) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(
"class X {}; // comment 1\n"
"int main() {\n"
" // comment 2\n"
" return 0;\n"
"}"));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match("// comment 1", 1, 13);
Verifier.Match("// comment 2", 3, 3);
}
TEST(CommentHandlerTest, IfBlock1) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(
"#if 0\n"
"// ignored comment\n"
"#endif\n"
"// visible comment\n"));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match("// visible comment", 4, 1);
}
TEST(CommentHandlerTest, IfBlock2) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(
"#define TEST // visible_1\n"
"#ifndef TEST // visible_2\n"
" // ignored_3\n"
"# ifdef UNDEFINED // ignored_4\n"
"# endif // ignored_5\n"
"#elif defined(TEST) // visible_6\n"
"# if 1 // visible_7\n"
" // visible_8\n"
"# else // visible_9\n"
" // ignored_10\n"
"# ifndef TEST // ignored_11\n"
"# endif // ignored_12\n"
"# endif // visible_13\n"
"#endif // visible_14\n"));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match("// visible_1", 1, 21);
Verifier.Match("// visible_2", 2, 21);
Verifier.Match("// visible_6", 6, 21);
Verifier.Match("// visible_7", 7, 21);
Verifier.Match("// visible_8", 8, 21);
Verifier.Match("// visible_9", 9, 21);
Verifier.Match("// visible_13", 13, 21);
Verifier.Match("// visible_14", 14, 21);
}
TEST(CommentHandlerTest, IfBlock3) {
const char *Source =
"/* commented out ...\n"
"#if 0\n"
"// enclosed\n"
"#endif */";
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(Source));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match(Source, 1, 1);
}
TEST(CommentHandlerTest, PPDirectives) {
CommentHandlerVisitor Visitor;
EXPECT_TRUE(Visitor.runOver(
"#warning Y // ignored_1\n" // #warning takes whole line as message
"#undef MACRO // visible_2\n"
"#line 1 // visible_3\n"));
CommentVerifier Verifier = Visitor.GetVerifier();
Verifier.Match("// visible_2", 2, 14);
Verifier.Match("// visible_3", 3, 14);
}
} // end namespace clang

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
//===- unittests/Tooling/DiagnosticsYamlTest.cpp - Serialization tests ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Tests for serialization of Diagnostics.
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/DiagnosticsYaml.h"
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace clang::tooling;
using clang::tooling::Diagnostic;
static Diagnostic makeDiagnostic(StringRef DiagnosticName,
const std::string &Message, int FileOffset,
const std::string &FilePath,
const StringMap<Replacements> &Fix) {
DiagnosticMessage DiagMessage;
DiagMessage.Message = Message;
DiagMessage.FileOffset = FileOffset;
DiagMessage.FilePath = FilePath;
return Diagnostic(DiagnosticName, DiagMessage, Fix, {}, Diagnostic::Warning,
"path/to/build/directory");
}
TEST(DiagnosticsYamlTest, serializesDiagnostics) {
TranslationUnitDiagnostics TUD;
TUD.MainSourceFile = "path/to/source.cpp";
StringMap<Replacements> Fix1 = {
{"path/to/source.cpp",
Replacements({"path/to/source.cpp", 100, 12, "replacement #1"})}};
TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#1", "message #1", 55,
"path/to/source.cpp", Fix1));
StringMap<Replacements> Fix2 = {
{"path/to/header.h",
Replacements({"path/to/header.h", 62, 2, "replacement #2"})}};
TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#2", "message #2", 60,
"path/to/header.h", Fix2));
TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#3", "message #3", 72,
"path/to/source2.cpp", {}));
std::string YamlContent;
raw_string_ostream YamlContentStream(YamlContent);
yaml::Output YAML(YamlContentStream);
YAML << TUD;
EXPECT_EQ("---\n"
"MainSourceFile: path/to/source.cpp\n"
"Diagnostics: \n"
" - DiagnosticName: 'diagnostic#1\'\n"
" Message: 'message #1'\n"
" FileOffset: 55\n"
" FilePath: path/to/source.cpp\n"
" Replacements: \n"
" - FilePath: path/to/source.cpp\n"
" Offset: 100\n"
" Length: 12\n"
" ReplacementText: 'replacement #1'\n"
" - DiagnosticName: 'diagnostic#2'\n"
" Message: 'message #2'\n"
" FileOffset: 60\n"
" FilePath: path/to/header.h\n"
" Replacements: \n"
" - FilePath: path/to/header.h\n"
" Offset: 62\n"
" Length: 2\n"
" ReplacementText: 'replacement #2'\n"
" - DiagnosticName: 'diagnostic#3'\n"
" Message: 'message #3'\n"
" FileOffset: 72\n"
" FilePath: path/to/source2.cpp\n"
" Replacements: \n"
"...\n",
YamlContentStream.str());
}
TEST(DiagnosticsYamlTest, deserializesDiagnostics) {
std::string YamlContent = "---\n"
"MainSourceFile: path/to/source.cpp\n"
"Diagnostics: \n"
" - DiagnosticName: 'diagnostic#1'\n"
" Message: 'message #1'\n"
" FileOffset: 55\n"
" FilePath: path/to/source.cpp\n"
" Replacements: \n"
" - FilePath: path/to/source.cpp\n"
" Offset: 100\n"
" Length: 12\n"
" ReplacementText: 'replacement #1'\n"
" - DiagnosticName: 'diagnostic#2'\n"
" Message: 'message #2'\n"
" FileOffset: 60\n"
" FilePath: path/to/header.h\n"
" Replacements: \n"
" - FilePath: path/to/header.h\n"
" Offset: 62\n"
" Length: 2\n"
" ReplacementText: 'replacement #2'\n"
" - DiagnosticName: 'diagnostic#3'\n"
" Message: 'message #3'\n"
" FileOffset: 98\n"
" FilePath: path/to/source.cpp\n"
" Replacements: \n"
"...\n";
TranslationUnitDiagnostics TUDActual;
yaml::Input YAML(YamlContent);
YAML >> TUDActual;
ASSERT_FALSE(YAML.error());
ASSERT_EQ(3u, TUDActual.Diagnostics.size());
EXPECT_EQ("path/to/source.cpp", TUDActual.MainSourceFile);
auto getFixes = [](const StringMap<Replacements> &Fix) {
std::vector<Replacement> Fixes;
for (auto &Replacements : Fix) {
for (auto &Replacement : Replacements.second) {
Fixes.push_back(Replacement);
}
}
return Fixes;
};
Diagnostic D1 = TUDActual.Diagnostics[0];
EXPECT_EQ("diagnostic#1", D1.DiagnosticName);
EXPECT_EQ("message #1", D1.Message.Message);
EXPECT_EQ(55u, D1.Message.FileOffset);
EXPECT_EQ("path/to/source.cpp", D1.Message.FilePath);
std::vector<Replacement> Fixes1 = getFixes(D1.Fix);
ASSERT_EQ(1u, Fixes1.size());
EXPECT_EQ("path/to/source.cpp", Fixes1[0].getFilePath());
EXPECT_EQ(100u, Fixes1[0].getOffset());
EXPECT_EQ(12u, Fixes1[0].getLength());
EXPECT_EQ("replacement #1", Fixes1[0].getReplacementText());
Diagnostic D2 = TUDActual.Diagnostics[1];
EXPECT_EQ("diagnostic#2", D2.DiagnosticName);
EXPECT_EQ("message #2", D2.Message.Message);
EXPECT_EQ(60u, D2.Message.FileOffset);
EXPECT_EQ("path/to/header.h", D2.Message.FilePath);
std::vector<Replacement> Fixes2 = getFixes(D2.Fix);
ASSERT_EQ(1u, Fixes2.size());
EXPECT_EQ("path/to/header.h", Fixes2[0].getFilePath());
EXPECT_EQ(62u, Fixes2[0].getOffset());
EXPECT_EQ(2u, Fixes2[0].getLength());
EXPECT_EQ("replacement #2", Fixes2[0].getReplacementText());
Diagnostic D3 = TUDActual.Diagnostics[2];
EXPECT_EQ("diagnostic#3", D3.DiagnosticName);
EXPECT_EQ("message #3", D3.Message.Message);
EXPECT_EQ(98u, D3.Message.FileOffset);
EXPECT_EQ("path/to/source.cpp", D3.Message.FilePath);
std::vector<Replacement> Fixes3 = getFixes(D3.Fix);
EXPECT_TRUE(Fixes3.empty());
}

View File

@@ -0,0 +1,221 @@
//===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <string>
namespace clang {
namespace tooling {
namespace {
// This traverses the AST and outputs function name as key and "1" as value for
// each function declaration.
class ASTConsumerWithResult
: public ASTConsumer,
public RecursiveASTVisitor<ASTConsumerWithResult> {
public:
using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>;
explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) {
assert(Context != nullptr);
}
void HandleTranslationUnit(clang::ASTContext &Context) override {
TraverseDecl(Context.getTranslationUnitDecl());
}
bool TraverseFunctionDecl(clang::FunctionDecl *Decl) {
Context->reportResult(Decl->getNameAsString(), "1");
return ASTVisitor::TraverseFunctionDecl(Decl);
}
private:
ExecutionContext *const Context;
};
class ReportResultAction : public ASTFrontendAction {
public:
explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {
assert(Context != nullptr);
}
protected:
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &compiler,
StringRef /* dummy */) override {
std::unique_ptr<clang::ASTConsumer> ast_consumer{
new ASTConsumerWithResult(Context)};
return ast_consumer;
}
private:
ExecutionContext *const Context;
};
class ReportResultActionFactory : public FrontendActionFactory {
public:
ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}
FrontendAction *create() override { return new ReportResultAction(Context); }
private:
ExecutionContext *const Context;
};
} // namespace
class TestToolExecutor : public ToolExecutor {
public:
static const char *ExecutorName;
TestToolExecutor(CommonOptionsParser Options)
: OptionsParser(std::move(Options)) {}
StringRef getExecutorName() const override { return ExecutorName; }
llvm::Error
execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,
ArgumentsAdjuster>>) override {
return llvm::Error::success();
}
ExecutionContext *getExecutionContext() override { return nullptr; };
ToolResults *getToolResults() override { return nullptr; }
llvm::ArrayRef<std::string> getSourcePaths() const {
return OptionsParser.getSourcePathList();
}
void mapVirtualFile(StringRef FilePath, StringRef Content) override {
VFS[FilePath] = Content;
}
private:
CommonOptionsParser OptionsParser;
std::string SourcePaths;
std::map<std::string, std::string> VFS;
};
const char *TestToolExecutor::ExecutorName = "test-executor";
class TestToolExecutorPlugin : public ToolExecutorPlugin {
public:
llvm::Expected<std::unique_ptr<ToolExecutor>>
create(CommonOptionsParser &OptionsParser) override {
return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser));
}
};
static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
X("test-executor", "Plugin for TestToolExecutor.");
llvm::cl::OptionCategory TestCategory("execution-test options");
TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {
std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};
int argc = argv.size();
auto Executor = internal::createExecutorFromCommandLineArgsImpl(
argc, &argv[0], TestCategory);
ASSERT_FALSE((bool)Executor);
llvm::consumeError(Executor.takeError());
}
TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {
llvm::cl::opt<std::string> BeforeReset(
"before_reset", llvm::cl::desc("Defined before reset."),
llvm::cl::init(""));
llvm::cl::ResetAllOptionOccurrences();
std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};
int argc = argv.size();
auto Executor = internal::createExecutorFromCommandLineArgsImpl(
argc, &argv[0], TestCategory);
ASSERT_TRUE((bool)Executor);
EXPECT_EQ(BeforeReset, "set");
BeforeReset.removeArgument();
}
TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {
std::vector<const char *> argv = {"prog", "standalone.cpp"};
int argc = argv.size();
auto Executor = internal::createExecutorFromCommandLineArgsImpl(
argc, &argv[0], TestCategory);
ASSERT_TRUE((bool)Executor);
EXPECT_EQ(Executor->get()->getExecutorName(),
StandaloneToolExecutor::ExecutorName);
}
TEST(CreateToolExecutorTest, CreateTestToolExecutor) {
std::vector<const char *> argv = {"prog", "test.cpp",
"--executor=test-executor"};
int argc = argv.size();
auto Executor = internal::createExecutorFromCommandLineArgsImpl(
argc, &argv[0], TestCategory);
ASSERT_TRUE((bool)Executor);
EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
}
TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {
FixedCompilationDatabase Compilations(".", std::vector<std::string>());
StandaloneToolExecutor Executor(Compilations,
std::vector<std::string>(1, "a.cc"));
Executor.mapVirtualFile("a.cc", "int x = 0;");
auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(),
getClangSyntaxOnlyAdjuster());
ASSERT_TRUE(!Err);
}
TEST(StandaloneToolTest, SimpleAction) {
FixedCompilationDatabase Compilations(".", std::vector<std::string>());
StandaloneToolExecutor Executor(Compilations,
std::vector<std::string>(1, "a.cc"));
Executor.mapVirtualFile("a.cc", "int x = 0;");
auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
new ReportResultActionFactory(Executor.getExecutionContext())));
ASSERT_TRUE(!Err);
auto KVs = Executor.getToolResults()->AllKVResults();
ASSERT_EQ(KVs.size(), 0u);
}
TEST(StandaloneToolTest, SimpleActionWithResult) {
FixedCompilationDatabase Compilations(".", std::vector<std::string>());
StandaloneToolExecutor Executor(Compilations,
std::vector<std::string>(1, "a.cc"));
Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}");
auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
new ReportResultActionFactory(Executor.getExecutionContext())));
ASSERT_TRUE(!Err);
auto KVs = Executor.getToolResults()->AllKVResults();
ASSERT_EQ(KVs.size(), 1u);
EXPECT_EQ("f", KVs[0].first);
EXPECT_EQ("1", KVs[0].second);
Executor.getToolResults()->forEachResult(
[](StringRef, StringRef Value) { EXPECT_EQ("1", Value); });
}
} // end namespace tooling
} // end namespace clang

View File

@@ -0,0 +1,232 @@
//===- unittest/Tooling/FixitTest.cpp ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Tooling/FixIt.h"
using namespace clang;
using tooling::fixit::getText;
using tooling::fixit::createRemoval;
using tooling::fixit::createReplacement;
namespace {
struct CallsVisitor : TestVisitor<CallsVisitor> {
bool VisitCallExpr(CallExpr *Expr) {
OnCall(Expr, Context);
return true;
}
std::function<void(CallExpr *, ASTContext *Context)> OnCall;
};
std::string LocationToString(SourceLocation Loc, ASTContext *Context) {
return Loc.printToString(Context->getSourceManager());
}
TEST(FixItTest, getText) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
EXPECT_EQ("foo(x, y)", getText(*CE, *Context));
EXPECT_EQ("foo(x, y)", getText(CE->getSourceRange(), *Context));
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
EXPECT_EQ("x", getText(*P0, *Context));
EXPECT_EQ("y", getText(*P1, *Context));
};
Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context));
};
Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n"
"void foo(int x, int y) { APPLY(foo, x, y); }");
}
TEST(FixItTest, getTextWithMacro) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
EXPECT_EQ("F OO", getText(*CE, *Context));
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
EXPECT_EQ("", getText(*P0, *Context));
EXPECT_EQ("", getText(*P1, *Context));
};
Visitor.runOver("#define F foo(\n"
"#define OO x, y)\n"
"void foo(int x, int y) { F OO ; }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
EXPECT_EQ("", getText(*CE, *Context));
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
EXPECT_EQ("x", getText(*P0, *Context));
EXPECT_EQ("y", getText(*P1, *Context));
};
Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
"void foo(int x, int y) { FOO(x,y) }");
}
TEST(FixItTest, createRemoval) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
FixItHint Hint = createRemoval(*CE);
EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint.CodeToInsert.empty());
Expr *P0 = CE->getArg(0);
FixItHint Hint0 = createRemoval(*P0);
EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint0.CodeToInsert.empty());
Expr *P1 = CE->getArg(1);
FixItHint Hint1 = createRemoval(*P1);
EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint1.CodeToInsert.empty());
};
Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
Expr *P0 = CE->getArg(0);
FixItHint Hint0 = createRemoval(*P0);
EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), *Context));
Expr *P1 = CE->getArg(1);
FixItHint Hint1 = createRemoval(*P1);
EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), *Context));
};
Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }");
}
TEST(FixItTest, createRemovalWithMacro) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
FixItHint Hint = createRemoval(*CE);
EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint.CodeToInsert.empty());
Expr *P0 = CE->getArg(0);
FixItHint Hint0 = createRemoval(*P0);
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
LocationToString(Hint0.RemoveRange.getBegin(), Context));
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
LocationToString(Hint0.RemoveRange.getEnd(), Context));
EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint0.CodeToInsert.empty());
Expr *P1 = CE->getArg(1);
FixItHint Hint1 = createRemoval(*P1);
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>",
LocationToString(Hint1.RemoveRange.getBegin(), Context));
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>",
LocationToString(Hint1.RemoveRange.getEnd(), Context));
EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint1.CodeToInsert.empty());
};
Visitor.runOver("#define FOO foo(1, 1)\n"
"void foo(int x, int y) { FOO; }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
FixItHint Hint = createRemoval(*CE);
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:37>",
LocationToString(Hint.RemoveRange.getBegin(), Context));
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:45>",
LocationToString(Hint.RemoveRange.getEnd(), Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint.CodeToInsert.empty());
};
Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
"void foo(int x, int y) { FOO(x,y) }");
}
TEST(FixItTest, createReplacement) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
FixItHint Hint0 = createReplacement(*P0, *P1, *Context);
FixItHint Hint1 = createReplacement(*P1, *P0, *Context);
// Validate Hint0 fields.
EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
EXPECT_EQ(Hint0.CodeToInsert, "y");
// Validate Hint1 fields.
EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
EXPECT_EQ(Hint1.CodeToInsert, "x");
};
Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n"
"void foo(int x, int y) { APPLY(foo, x, y); }");
Visitor.runOver("#define APPLY(f, P) f(P)\n"
"#define PAIR(x, y) x, y\n"
"void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n");
}
TEST(FixItTest, createReplacementWithMacro) {
CallsVisitor Visitor;
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
FixItHint Hint = createReplacement(*P0, *P1, *Context);
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
LocationToString(Hint.RemoveRange.getBegin(), Context));
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
LocationToString(Hint.RemoveRange.getEnd(), Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_TRUE(Hint.CodeToInsert.empty());
};
Visitor.runOver("#define FOO foo(1, 1)\n"
"void foo(int x, int y) { FOO; }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
FixItHint Hint = createReplacement(*P0, *P1, *Context);
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>",
LocationToString(Hint.RemoveRange.getBegin(), Context));
EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>",
LocationToString(Hint.RemoveRange.getEnd(), Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_EQ("y", Hint.CodeToInsert);
};
Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
"void foo(int x, int y) { FOO(x,y) }");
Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
Expr *P0 = CE->getArg(0);
Expr *P1 = CE->getArg(1);
FixItHint Hint = createReplacement(*P0, *P1, *Context);
EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), *Context));
EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
EXPECT_EQ("y + x", Hint.CodeToInsert);
};
Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }");
}
} // end anonymous namespace

View File

@@ -0,0 +1,227 @@
//===- unittest/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
#include <stack>
using namespace clang;
namespace {
class DummyMatchVisitor;
class LexicallyOrderedDeclVisitor
: public LexicallyOrderedRecursiveASTVisitor<LexicallyOrderedDeclVisitor> {
public:
LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
const SourceManager &SM, bool EmitDeclIndices,
bool EmitStmtIndices)
: LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
bool TraverseDecl(Decl *D) {
TraversalStack.push_back(D);
LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
TraversalStack.pop_back();
return true;
}
bool TraverseStmt(Stmt *S);
bool VisitNamedDecl(const NamedDecl *D);
bool VisitDeclRefExpr(const DeclRefExpr *D);
private:
DummyMatchVisitor &Matcher;
bool EmitDeclIndices, EmitStmtIndices;
unsigned Index = 0;
llvm::SmallVector<Decl *, 8> TraversalStack;
};
class DummyMatchVisitor : public ExpectedLocationVisitor<DummyMatchVisitor> {
bool EmitDeclIndices, EmitStmtIndices;
public:
DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
: EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
const ASTContext &Context = TU->getASTContext();
const SourceManager &SM = Context.getSourceManager();
LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
EmitStmtIndices);
SubVisitor.TraverseDecl(TU);
return false;
}
template <class T> void match(StringRef Path, const T *D) {
Match(Path, D->getLocStart());
}
};
bool LexicallyOrderedDeclVisitor::TraverseStmt(Stmt *S) {
Matcher.match("overridden TraverseStmt", S);
return LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
}
bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
std::string Path;
llvm::raw_string_ostream OS(Path);
assert(TraversalStack.back() == D);
for (const Decl *D : TraversalStack) {
if (isa<TranslationUnitDecl>(D)) {
OS << "/";
continue;
}
if (const auto *ND = dyn_cast<NamedDecl>(D))
OS << ND->getNameAsString();
else
OS << "???";
if (isa<DeclContext>(D) || isa<TemplateDecl>(D))
OS << "/";
}
if (EmitDeclIndices)
OS << "@" << Index++;
Matcher.match(OS.str(), D);
return true;
}
bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
std::string Name = D->getFoundDecl()->getNameAsString();
llvm::raw_string_ostream OS(Name);
if (EmitStmtIndices)
OS << "@" << Index++;
Matcher.match(OS.str(), D);
return true;
}
TEST(LexicallyOrderedRecursiveASTVisitor, VisitDeclsInImplementation) {
StringRef Source = R"(
@interface I
@end
@implementation I
int nestedFunction() { }
- (void) method{ }
int anotherNestedFunction(int x) {
return x;
}
int innerVariable = 0;
@end
int outerVariable = 0;
@implementation I(Cat)
void catF() { }
@end
void outerFunction() { }
)";
DummyMatchVisitor Visitor;
Visitor.DisallowMatch("/nestedFunction/", 6, 1);
Visitor.ExpectMatch("/I/nestedFunction/", 6, 1);
Visitor.ExpectMatch("/I/method/", 8, 1);
Visitor.DisallowMatch("/anotherNestedFunction/", 10, 1);
Visitor.ExpectMatch("/I/anotherNestedFunction/", 10, 1);
Visitor.DisallowMatch("/innerVariable", 14, 1);
Visitor.ExpectMatch("/I/innerVariable", 14, 1);
Visitor.ExpectMatch("/outerVariable", 18, 1);
Visitor.DisallowMatch("/catF/", 22, 1);
Visitor.ExpectMatch("/Cat/catF/", 22, 1);
Visitor.ExpectMatch("/outerFunction/", 26, 1);
EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC));
}
TEST(LexicallyOrderedRecursiveASTVisitor, VisitMacroDeclsInImplementation) {
StringRef Source = R"(
@interface I
@end
void outerFunction() { }
#define MACRO_F(x) void nestedFunction##x() { }
@implementation I
MACRO_F(1)
@end
MACRO_F(2)
)";
DummyMatchVisitor Visitor;
Visitor.ExpectMatch("/outerFunction/", 5, 1);
Visitor.ExpectMatch("/I/nestedFunction1/", 7, 20);
Visitor.ExpectMatch("/nestedFunction2/", 7, 20);
EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC));
}
TEST(LexicallyOrderedRecursiveASTVisitor, VisitTemplateDecl) {
StringRef Source = R"(
template <class T> T f();
template <class U, class = void> class Class {};
)";
DummyMatchVisitor Visitor(/*EmitIndices=*/true);
Visitor.ExpectMatch("/f/T@1", 2, 11);
Visitor.ExpectMatch("/f/f/@2", 2, 20);
Visitor.ExpectMatch("/Class/U@4", 3, 11);
Visitor.ExpectMatch("/Class/@5", 3, 20);
Visitor.ExpectMatch("/Class/Class/@6", 3, 34);
EXPECT_TRUE(Visitor.runOver(Source));
}
TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
StringRef Source = R"(
struct S {
S &operator+(S&);
S *operator->();
S &operator++();
S operator++(int);
void operator()(int, int);
void operator[](int);
void f();
};
S a, b, c;
void test() {
a = b + c;
a->f();
a(1, 2);
b[0];
++a;
b++;
}
)";
DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
/*EmitStmtIndices=*/true);
// There are two overloaded operators that start at this point
// This makes sure they are both traversed using the overridden
// TraverseStmt, as the traversal is implemented by us for
// CXXOperatorCallExpr.
Visitor.ExpectMatch("overridden TraverseStmt", 14, 3, 2);
Visitor.ExpectMatch("a@0", 14, 3);
Visitor.ExpectMatch("operator=@1", 14, 5);
Visitor.ExpectMatch("b@2", 14, 7);
Visitor.ExpectMatch("operator+@3", 14, 9);
Visitor.ExpectMatch("c@4", 14, 11);
Visitor.ExpectMatch("operator->@6", 15, 4);
Visitor.ExpectMatch("operator()@8", 16, 4);
Visitor.ExpectMatch("operator[]@10", 17, 4);
Visitor.ExpectMatch("operator++@11", 18, 3);
Visitor.ExpectMatch("operator++@14", 19, 4);
EXPECT_TRUE(Visitor.runOver(Source));
}
} // end anonymous namespace

View File

@@ -0,0 +1,167 @@
//===- unittest/Tooling/LookupTest.cpp ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include "clang/Tooling/Core/Lookup.h"
using namespace clang;
namespace {
struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
std::function<void(CallExpr *)> OnCall;
std::function<void(RecordTypeLoc)> OnRecordTypeLoc;
SmallVector<Decl *, 4> DeclStack;
bool VisitCallExpr(CallExpr *Expr) {
if (OnCall)
OnCall(Expr);
return true;
}
bool VisitRecordTypeLoc(RecordTypeLoc Loc) {
if (OnRecordTypeLoc)
OnRecordTypeLoc(Loc);
return true;
}
bool TraverseDecl(Decl *D) {
DeclStack.push_back(D);
bool Ret = TestVisitor::TraverseDecl(D);
DeclStack.pop_back();
return Ret;
}
};
TEST(LookupTest, replaceNestedFunctionName) {
GetDeclsVisitor Visitor;
auto replaceCallExpr = [&](const CallExpr *Expr,
StringRef ReplacementString) {
const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit());
const ValueDecl *FD = Callee->getDecl();
return tooling::replaceNestedName(
Callee->getQualifier(), Visitor.DeclStack.back()->getDeclContext(), FD,
ReplacementString);
};
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
};
Visitor.runOver("namespace a { void foo(); }\n"
"namespace a { void f() { foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { void foo(); }\n"
"namespace a { void f() { foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { void foo(); }\n"
"namespace b { void f() { a::foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { void foo(); }\n"
"namespace b { namespace a { void foo(); }\n"
"void f() { a::foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));
};
Visitor.runOver("namespace a { namespace b { void foo(); }\n"
"void f() { b::foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { namespace b { void foo(); }\n"
"void f() { b::foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));
};
Visitor.runOver("void foo(); void f() { foo(); }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("::bar", replaceCallExpr(Expr, "::bar"));
};
Visitor.runOver("void foo(); void f() { ::foo(); }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { void foo(); }\nvoid f() { a::foo(); }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { int foo(); }\nauto f = a::foo();\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver(
"namespace a { int foo(); }\nusing a::foo;\nauto f = foo();\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));
};
Visitor.runOver("namespace a { namespace b { void foo(); } }\n"
"namespace a { namespace b { namespace {"
"void f() { foo(); }"
"} } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
EXPECT_EQ("x::bar", replaceCallExpr(Expr, "::a::x::bar"));
};
Visitor.runOver("namespace a { namespace b { void foo(); } }\n"
"namespace a { namespace b { namespace c {"
"void f() { foo(); }"
"} } }\n");
}
TEST(LookupTest, replaceNestedClassName) {
GetDeclsVisitor Visitor;
auto replaceRecordTypeLoc = [&](RecordTypeLoc Loc,
StringRef ReplacementString) {
const auto *FD = cast<CXXRecordDecl>(Loc.getDecl());
return tooling::replaceNestedName(
nullptr, Visitor.DeclStack.back()->getDeclContext(), FD,
ReplacementString);
};
Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
// Filter Types by name since there are other `RecordTypeLoc` in the test
// file.
if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") {
EXPECT_EQ("x::Bar", replaceRecordTypeLoc(Type, "::a::x::Bar"));
}
};
Visitor.runOver("namespace a { namespace b {\n"
"class Foo;\n"
"namespace c { Foo f();; }\n"
"} }\n");
Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
// Filter Types by name since there are other `RecordTypeLoc` in the test
// file.
// `a::b::Foo` in using shadow decl is not `TypeLoc`.
if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") {
EXPECT_EQ("Bar", replaceRecordTypeLoc(Type, "::a::x::Bar"));
}
};
Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n"
"namespace c { using a::b::Foo; Foo f();; }\n");
}
} // end anonymous namespace

View File

@@ -0,0 +1,222 @@
//===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/QualTypeNames.h"
#include "TestVisitor.h"
using namespace clang;
namespace {
struct TypeNameVisitor : TestVisitor<TypeNameVisitor> {
llvm::StringMap<std::string> ExpectedQualTypeNames;
bool WithGlobalNsPrefix = false;
// ValueDecls are the least-derived decl with both a qualtype and a
// name.
bool traverseDecl(Decl *D) {
return true; // Always continue
}
bool VisitValueDecl(const ValueDecl *VD) {
std::string ExpectedName =
ExpectedQualTypeNames.lookup(VD->getNameAsString());
if (ExpectedName != "") {
std::string ActualName =
TypeName::getFullyQualifiedName(VD->getType(), *Context,
WithGlobalNsPrefix);
if (ExpectedName != ActualName) {
// A custom message makes it much easier to see what declaration
// failed compared to EXPECT_EQ.
EXPECT_TRUE(false) << "Typename::getFullyQualifiedName failed for "
<< VD->getQualifiedNameAsString() << std::endl
<< " Actual: " << ActualName << std::endl
<< " Exepcted: " << ExpectedName;
}
}
return true;
}
};
// named namespaces inside anonymous namespaces
TEST(QualTypeNameTest, getFullyQualifiedName) {
TypeNameVisitor Visitor;
// Simple case to test the test framework itself.
Visitor.ExpectedQualTypeNames["CheckInt"] = "int";
// Keeping the names of the variables whose types we check unique
// within the entire test--regardless of their own scope--makes it
// easier to diagnose test failures.
// Simple namespace qualifier
Visitor.ExpectedQualTypeNames["CheckA"] = "A::B::Class0";
// Lookup up the enclosing scopes, then down another one. (These
// appear as elaborated type in the AST. In that case--even if
// policy.SuppressScope = 0--qual_type.getAsString(policy) only
// gives the name as it appears in the source, not the full name.
Visitor.ExpectedQualTypeNames["CheckB"] = "A::B::C::Class1";
// Template parameter expansion.
Visitor.ExpectedQualTypeNames["CheckC"] =
"A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>";
// Recursive template parameter expansion.
Visitor.ExpectedQualTypeNames["CheckD"] =
"A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, "
"A::B::Template0<int, long> >";
// Variadic Template expansion.
Visitor.ExpectedQualTypeNames["CheckE"] =
"A::Variadic<int, A::B::Template0<int, char>, "
"A::B::Template1<int, long>, A::B::C::MyInt>";
// Using declarations should be fully expanded.
Visitor.ExpectedQualTypeNames["CheckF"] = "A::B::Class0";
// Elements found within "using namespace foo;" should be fully
// expanded.
Visitor.ExpectedQualTypeNames["CheckG"] = "A::B::C::MyInt";
// Type inside function
Visitor.ExpectedQualTypeNames["CheckH"] = "struct X";
// Anonymous Namespaces
Visitor.ExpectedQualTypeNames["CheckI"] = "aClass";
// Keyword inclusion with namespaces
Visitor.ExpectedQualTypeNames["CheckJ"] = "struct A::aStruct";
// Anonymous Namespaces nested in named namespaces and vice-versa.
Visitor.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
// Namespace alias
Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt";
Visitor.ExpectedQualTypeNames["non_dependent_type_var"] =
"Foo<X>::non_dependent_type";
Visitor.ExpectedQualTypeNames["AnEnumVar"] = "EnumScopeClass::AnEnum";
Visitor.ExpectedQualTypeNames["AliasTypeVal"] = "A::B::C::InnerAlias<int>";
Visitor.ExpectedQualTypeNames["CheckM"] = "const A::B::Class0 *";
Visitor.ExpectedQualTypeNames["CheckN"] = "const X *";
Visitor.runOver(
"int CheckInt;\n"
"template <typename T>\n"
"class OuterTemplateClass { };\n"
"namespace A {\n"
" namespace B {\n"
" class Class0 { };\n"
" namespace C {\n"
" typedef int MyInt;"
" template <typename T>\n"
" using InnerAlias = OuterTemplateClass<T>;\n"
" InnerAlias<int> AliasTypeVal;\n"
" }\n"
" template<class X, class Y> class Template0;"
" template<class X, class Y> class Template1;"
" typedef B::Class0 AnotherClass;\n"
" void Function1(Template0<C::MyInt,\n"
" AnotherClass> CheckC);\n"
" void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n"
" Template0<int, long> > CheckD);\n"
" void Function3(const B::Class0* CheckM);\n"
" }\n"
"template<typename... Values> class Variadic {};\n"
"Variadic<int, B::Template0<int, char>, "
" B::Template1<int, long>, "
" B::C::MyInt > CheckE;\n"
" namespace BC = B::C;\n"
" BC::MyInt CheckL;\n"
"}\n"
"using A::B::Class0;\n"
"void Function(Class0 CheckF);\n"
"using namespace A::B::C;\n"
"void Function(MyInt CheckG);\n"
"void f() {\n"
" struct X {} CheckH;\n"
"}\n"
"struct X;\n"
"void f(const ::X* CheckN) {}\n"
"namespace {\n"
" class aClass {};\n"
" aClass CheckI;\n"
"}\n"
"namespace A {\n"
" struct aStruct {} CheckJ;\n"
"}\n"
"namespace {\n"
" namespace D {\n"
" namespace {\n"
" class aStruct {};\n"
" aStruct CheckK;\n"
" }\n"
" }\n"
"}\n"
"template<class T> struct Foo {\n"
" typedef typename T::A dependent_type;\n"
" typedef int non_dependent_type;\n"
" dependent_type dependent_type_var;\n"
" non_dependent_type non_dependent_type_var;\n"
"};\n"
"struct X { typedef int A; };"
"Foo<X> var;"
"void F() {\n"
" var.dependent_type_var = 0;\n"
"var.non_dependent_type_var = 0;\n"
"}\n"
"class EnumScopeClass {\n"
"public:\n"
" enum AnEnum { ZERO, ONE };\n"
"};\n"
"EnumScopeClass::AnEnum AnEnumVar;\n",
TypeNameVisitor::Lang_CXX11
);
TypeNameVisitor Complex;
Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX";
Complex.runOver(
"namespace A {"
" struct X {};"
"}"
"using A::X;"
"namespace fake_std {"
" template<class... Types > class tuple {};"
"}"
"namespace B {"
" using fake_std::tuple;"
" typedef tuple<X> TX;"
" TX CheckTX;"
" struct A { typedef int X; };"
"}");
TypeNameVisitor GlobalNsPrefix;
GlobalNsPrefix.WithGlobalNsPrefix = true;
GlobalNsPrefix.ExpectedQualTypeNames["IntVal"] = "int";
GlobalNsPrefix.ExpectedQualTypeNames["BoolVal"] = "bool";
GlobalNsPrefix.ExpectedQualTypeNames["XVal"] = "::A::B::X";
GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal"] = "::A::B::Alias<int>";
GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z";
GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z";
GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
GlobalNsPrefix.runOver(
"namespace A {\n"
" namespace B {\n"
" int IntVal;\n"
" bool BoolVal;\n"
" struct X {};\n"
" X XVal;\n"
" template <typename T> class CCC { };\n"
" template <typename T>\n"
" using Alias = CCC<T>;\n"
" Alias<int> IntAliasVal;\n"
" struct Y { struct Z {}; };\n"
" Y::Z ZVal;\n"
" }\n"
"}\n"
"struct Z {};\n"
"Z GlobalZVal;\n"
"namespace {\n"
" namespace D {\n"
" namespace {\n"
" class aStruct {};\n"
" aStruct CheckK;\n"
" }\n"
" }\n"
"}\n"
);
}
} // end anonymous namespace

View File

@@ -0,0 +1,305 @@
//===- unittest/Tooling/RecursiveASTVisitorTest.cpp -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include <stack>
using namespace clang;
namespace {
class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> {
public:
bool VisitLambdaExpr(LambdaExpr *Lambda) {
PendingBodies.push(Lambda);
Match("", Lambda->getIntroducerRange().getBegin());
return true;
}
/// For each call to VisitLambdaExpr, we expect a subsequent call (with
/// proper nesting) to TraverseLambdaBody.
bool TraverseLambdaBody(LambdaExpr *Lambda) {
EXPECT_FALSE(PendingBodies.empty());
EXPECT_EQ(PendingBodies.top(), Lambda);
PendingBodies.pop();
return TraverseStmt(Lambda->getBody());
}
/// Determine whether TraverseLambdaBody has been called for every call to
/// VisitLambdaExpr.
bool allBodiesHaveBeenTraversed() const {
return PendingBodies.empty();
}
private:
std::stack<LambdaExpr *> PendingBodies;
};
TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
LambdaExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
LambdaExprVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
}
TEST(RecursiveASTVisitor, VisitsAttributedLambdaExpr) {
LambdaExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver(
"void f() { [] () __attribute__ (( fastcall )) { return; }(); }",
LambdaExprVisitor::Lang_CXX14));
}
// Matches the (optional) capture-default of a lambda-introducer.
class LambdaDefaultCaptureVisitor
: public ExpectedLocationVisitor<LambdaDefaultCaptureVisitor> {
public:
bool VisitLambdaExpr(LambdaExpr *Lambda) {
if (Lambda->getCaptureDefault() != LCD_None) {
Match("", Lambda->getCaptureDefaultLoc());
}
return true;
}
};
TEST(RecursiveASTVisitor, HasCaptureDefaultLoc) {
LambdaDefaultCaptureVisitor Visitor;
Visitor.ExpectMatch("", 1, 20);
EXPECT_TRUE(Visitor.runOver("void f() { int a; [=]{a;}; }",
LambdaDefaultCaptureVisitor::Lang_CXX11));
}
// Checks for lambda classes that are not marked as implicitly-generated.
// (There should be none.)
class ClassVisitor : public ExpectedLocationVisitor<ClassVisitor> {
public:
ClassVisitor() : SawNonImplicitLambdaClass(false) {}
bool VisitCXXRecordDecl(CXXRecordDecl* record) {
if (record->isLambda() && !record->isImplicit())
SawNonImplicitLambdaClass = true;
return true;
}
bool sawOnlyImplicitLambdaClasses() const {
return !SawNonImplicitLambdaClass;
}
private:
bool SawNonImplicitLambdaClass;
};
TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
ClassVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("auto lambda = []{};", ClassVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
}
// Check to ensure that attributes and expressions within them are being
// visited.
class AttrVisitor : public ExpectedLocationVisitor<AttrVisitor> {
public:
bool VisitMemberExpr(MemberExpr *ME) {
Match(ME->getMemberDecl()->getNameAsString(), ME->getLocStart());
return true;
}
bool VisitAttr(Attr *A) {
Match("Attr", A->getLocation());
return true;
}
bool VisitGuardedByAttr(GuardedByAttr *A) {
Match("guarded_by", A->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, AttributesAreVisited) {
AttrVisitor Visitor;
Visitor.ExpectMatch("Attr", 4, 24);
Visitor.ExpectMatch("guarded_by", 4, 24);
Visitor.ExpectMatch("mu1", 4, 35);
Visitor.ExpectMatch("Attr", 5, 29);
Visitor.ExpectMatch("mu1", 5, 54);
Visitor.ExpectMatch("mu2", 5, 59);
EXPECT_TRUE(Visitor.runOver(
"class Foo {\n"
" int mu1;\n"
" int mu2;\n"
" int a __attribute__((guarded_by(mu1)));\n"
" void bar() __attribute__((exclusive_locks_required(mu1, mu2)));\n"
"};\n"));
}
// Check to ensure that implicit default argument expressions are visited.
class IntegerLiteralVisitor
: public ExpectedLocationVisitor<IntegerLiteralVisitor> {
public:
bool VisitIntegerLiteral(const IntegerLiteral *IL) {
Match("literal", IL->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, DefaultArgumentsAreVisited) {
IntegerLiteralVisitor Visitor;
Visitor.ExpectMatch("literal", 1, 15, 2);
EXPECT_TRUE(Visitor.runOver("int f(int i = 1);\n"
"static int k = f();\n"));
}
// Check to ensure that InitListExpr is visited twice, once each for the
// syntactic and semantic form.
class InitListExprPreOrderVisitor
: public ExpectedLocationVisitor<InitListExprPreOrderVisitor> {
public:
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPostOrderVisitor
: public ExpectedLocationVisitor<InitListExprPostOrderVisitor> {
public:
bool shouldTraversePostOrder() const { return true; }
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPreOrderNoQueueVisitor
: public ExpectedLocationVisitor<InitListExprPreOrderNoQueueVisitor> {
public:
bool TraverseInitListExpr(InitListExpr *ILE) {
return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
}
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPostOrderNoQueueVisitor
: public ExpectedLocationVisitor<InitListExprPostOrderNoQueueVisitor> {
public:
bool shouldTraversePostOrder() const { return true; }
bool TraverseInitListExpr(InitListExpr *ILE) {
return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
}
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
TEST(RecursiveASTVisitor, InitListExprIsPreOrderVisitedTwice) {
InitListExprPreOrderVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPreOrderVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPostOrderVisitedTwice) {
InitListExprPostOrderVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPostOrderVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPreOrderNoQueueVisitedTwice) {
InitListExprPreOrderNoQueueVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPreOrderNoQueueVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPostOrderNoQueueVisitedTwice) {
InitListExprPostOrderNoQueueVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPostOrderNoQueueVisitor::Lang_C));
}
// Check to ensure that nested name specifiers are visited.
class NestedNameSpecifiersVisitor
: public ExpectedLocationVisitor<NestedNameSpecifiersVisitor> {
public:
bool VisitRecordTypeLoc(RecordTypeLoc RTL) {
if (!RTL)
return true;
Match(RTL.getDecl()->getName(), RTL.getNameLoc());
return true;
}
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
if (!NNS)
return true;
if (const NamespaceDecl *ND =
NNS.getNestedNameSpecifier()->getAsNamespace())
Match(ND->getName(), NNS.getLocalBeginLoc());
return ExpectedLocationVisitor::TraverseNestedNameSpecifierLoc(NNS);
}
};
TEST(RecursiveASTVisitor,
NestedNameSpecifiersForTemplateSpecializationsAreVisited) {
StringRef Source = R"(
namespace ns {
struct Outer {
template<typename T, typename U>
struct Nested { };
template<typename T>
static T x;
};
}
template<>
struct ns::Outer::Nested<int, int>;
template<>
struct ns::Outer::Nested<int, int> { };
template<typename T>
struct ns::Outer::Nested<int, T> { };
template<>
int ns::Outer::x<int> = 0;
)";
NestedNameSpecifiersVisitor Visitor;
Visitor.ExpectMatch("ns", 13, 8);
Visitor.ExpectMatch("ns", 16, 8);
Visitor.ExpectMatch("ns", 19, 8);
Visitor.ExpectMatch("ns", 22, 5);
Visitor.ExpectMatch("Outer", 13, 12);
Visitor.ExpectMatch("Outer", 16, 12);
Visitor.ExpectMatch("Outer", 19, 12);
Visitor.ExpectMatch("Outer", 22, 9);
EXPECT_TRUE(Visitor.runOver(Source, NestedNameSpecifiersVisitor::Lang_CXX14));
}
} // end anonymous namespace

View File

@@ -0,0 +1,120 @@
//===- unittest/Tooling/RecursiveASTVisitorTestCallVisitor.cpp ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
class CXXMemberCallVisitor
: public ExpectedLocationVisitor<CXXMemberCallVisitor> {
public:
bool VisitCXXMemberCallExpr(CXXMemberCallExpr *Call) {
Match(Call->getMethodDecl()->getQualifiedNameAsString(),
Call->getLocStart());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsCallInTemplateInstantiation) {
CXXMemberCallVisitor Visitor;
Visitor.ExpectMatch("Y::x", 3, 3);
EXPECT_TRUE(Visitor.runOver(
"struct Y { void x(); };\n"
"template<typename T> void y(T t) {\n"
" t.x();\n"
"}\n"
"void foo() { y<Y>(Y()); }"));
}
TEST(RecursiveASTVisitor, VisitsCallInNestedFunctionTemplateInstantiation) {
CXXMemberCallVisitor Visitor;
Visitor.ExpectMatch("Y::x", 4, 5);
EXPECT_TRUE(Visitor.runOver(
"struct Y { void x(); };\n"
"template<typename T> struct Z {\n"
" template<typename U> static void f() {\n"
" T().x();\n"
" }\n"
"};\n"
"void foo() { Z<Y>::f<int>(); }"));
}
TEST(RecursiveASTVisitor, VisitsCallInNestedClassTemplateInstantiation) {
CXXMemberCallVisitor Visitor;
Visitor.ExpectMatch("A::x", 5, 7);
EXPECT_TRUE(Visitor.runOver(
"template <typename T1> struct X {\n"
" template <typename T2> struct Y {\n"
" void f() {\n"
" T2 y;\n"
" y.x();\n"
" }\n"
" };\n"
"};\n"
"struct A { void x(); };\n"
"int main() {\n"
" (new X<A>::Y<A>())->f();\n"
"}"));
}
TEST(RecursiveASTVisitor, VisitsCallInPartialTemplateSpecialization) {
CXXMemberCallVisitor Visitor;
Visitor.ExpectMatch("A::x", 6, 20);
EXPECT_TRUE(Visitor.runOver(
"template <typename T1> struct X {\n"
" template <typename T2, bool B> struct Y { void g(); };\n"
"};\n"
"template <typename T1> template <typename T2>\n"
"struct X<T1>::Y<T2, true> {\n"
" void f() { T2 y; y.x(); }\n"
"};\n"
"struct A { void x(); };\n"
"int main() {\n"
" (new X<A>::Y<A, true>())->f();\n"
"}\n"));
}
TEST(RecursiveASTVisitor, VisitsExplicitTemplateSpecialization) {
CXXMemberCallVisitor Visitor;
Visitor.ExpectMatch("A::f", 4, 5);
EXPECT_TRUE(Visitor.runOver(
"struct A {\n"
" void f() const {}\n"
" template<class T> void g(const T& t) const {\n"
" t.f();\n"
" }\n"
"};\n"
"template void A::g(const A& a) const;\n"));
}
class CXXOperatorCallExprTraverser
: public ExpectedLocationVisitor<CXXOperatorCallExprTraverser> {
public:
// Use Traverse, not Visit, to check that data recursion optimization isn't
// bypassing the call of this function.
bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *CE) {
Match(getOperatorSpelling(CE->getOperator()), CE->getExprLoc());
return ExpectedLocationVisitor<CXXOperatorCallExprTraverser>::
TraverseCXXOperatorCallExpr(CE);
}
};
TEST(RecursiveASTVisitor, TraversesOverloadedOperator) {
CXXOperatorCallExprTraverser Visitor;
Visitor.ExpectMatch("()", 4, 9);
EXPECT_TRUE(Visitor.runOver(
"struct A {\n"
" int operator()();\n"
"} a;\n"
"int k = a();\n"));
}
} // end anonymous namespace

View File

@@ -0,0 +1,140 @@
//===- unittest/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> {
public:
bool VisitVarDecl(VarDecl *Variable) {
Match(Variable->getNameAsString(), Variable->getLocStart());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
VarDeclVisitor Visitor;
Visitor.ExpectMatch("i", 2, 17);
EXPECT_TRUE(Visitor.runOver(
"int x[5];\n"
"void f() { for (int i : x) {} }",
VarDeclVisitor::Lang_CXX11));
}
class ParmVarDeclVisitorForImplicitCode :
public ExpectedLocationVisitor<ParmVarDeclVisitorForImplicitCode> {
public:
bool shouldVisitImplicitCode() const { return true; }
bool VisitParmVarDecl(ParmVarDecl *ParamVar) {
Match(ParamVar->getNameAsString(), ParamVar->getLocStart());
return true;
}
};
// Test RAV visits parameter variable declaration of the implicit
// copy assignment operator and implicit copy constructor.
TEST(RecursiveASTVisitor, VisitsParmVarDeclForImplicitCode) {
ParmVarDeclVisitorForImplicitCode Visitor;
// Match parameter variable name of implicit copy assignment operator and
// implicit copy constructor.
// This parameter name does not have a valid IdentifierInfo, and shares
// same SourceLocation with its class declaration, so we match an empty name
// with the class' source location.
Visitor.ExpectMatch("", 1, 7);
Visitor.ExpectMatch("", 3, 7);
EXPECT_TRUE(Visitor.runOver(
"class X {};\n"
"void foo(X a, X b) {a = b;}\n"
"class Y {};\n"
"void bar(Y a) {Y b = a;}"));
}
class NamedDeclVisitor
: public ExpectedLocationVisitor<NamedDeclVisitor> {
public:
bool VisitNamedDecl(NamedDecl *Decl) {
std::string NameWithTemplateArgs;
llvm::raw_string_ostream OS(NameWithTemplateArgs);
Decl->getNameForDiagnostic(OS,
Decl->getASTContext().getPrintingPolicy(),
true);
Match(OS.str(), Decl->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
// From cfe-commits/Week-of-Mon-20100830/033998.html
// Contrary to the approach suggested in that email, we visit all
// specializations when we visit the primary template. Visiting them when we
// visit the associated specialization is problematic for specializations of
// template members of class templates.
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("A<bool>", 1, 26);
Visitor.ExpectMatch("A<char *>", 2, 26);
EXPECT_TRUE(Visitor.runOver(
"template <class T> class A {};\n"
"template <class T> class A<T*> {};\n"
"A<bool> ab;\n"
"A<char*> acp;\n"));
}
TEST(RecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("A<int>", 1, 29);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> struct A;\n"
"A<int> *p;\n"));
}
TEST(RecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> struct A {\n"
" template<typename U> struct B;\n"
"};\n"
"A<int>::B<char> *p;\n"));
}
TEST(RecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("A<int>", 1, 26);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> int A();\n"
"int k = A<int>();\n"));
}
TEST(RecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> struct A {\n"
" template<typename U> static int B();\n"
"};\n"
"int k = A<int>::B<char>();\n"));
}
TEST(RecursiveASTVisitor, NoRecursionInSelfFriend) {
// From cfe-commits/Week-of-Mon-20100830/033977.html
NamedDeclVisitor Visitor;
Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
EXPECT_TRUE(Visitor.runOver(
"template<typename Container>\n"
"class vector_iterator {\n"
" template <typename C> friend class vector_iterator;\n"
"};\n"
"vector_iterator<int> it_int;\n"));
}
} // end anonymous namespace

View File

@@ -0,0 +1,271 @@
//===- unittest/Tooling/RecursiveASTVisitorTestExprVisitor.cpp ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
class ParenExprVisitor : public ExpectedLocationVisitor<ParenExprVisitor> {
public:
bool VisitParenExpr(ParenExpr *Parens) {
Match("", Parens->getExprLoc());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsParensDuringDataRecursion) {
ParenExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 9);
EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n"));
}
class TemplateArgumentLocTraverser
: public ExpectedLocationVisitor<TemplateArgumentLocTraverser> {
public:
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
std::string ArgStr;
llvm::raw_string_ostream Stream(ArgStr);
const TemplateArgument &Arg = ArgLoc.getArgument();
Arg.print(Context->getPrintingPolicy(), Stream);
Match(Stream.str(), ArgLoc.getLocation());
return ExpectedLocationVisitor<TemplateArgumentLocTraverser>::
TraverseTemplateArgumentLoc(ArgLoc);
}
};
TEST(RecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) {
TemplateArgumentLocTraverser Visitor;
Visitor.ExpectMatch("X", 2, 40);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> class X;\n"
"template<template <typename> class T = X> class Y;\n"
"template<template <typename> class T> class Y {};\n"));
}
class CXXBoolLiteralExprVisitor
: public ExpectedLocationVisitor<CXXBoolLiteralExprVisitor> {
public:
bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) {
if (BE->getValue())
Match("true", BE->getLocation());
else
Match("false", BE->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) {
CXXBoolLiteralExprVisitor Visitor;
Visitor.ExpectMatch("true", 2, 19);
EXPECT_TRUE(Visitor.runOver(
"template<bool B> class X;\n"
"template<bool B = true> class Y;\n"
"template<bool B> class Y {};\n"));
}
// A visitor that visits implicit declarations and matches constructors.
class ImplicitCtorVisitor
: public ExpectedLocationVisitor<ImplicitCtorVisitor> {
public:
bool shouldVisitImplicitCode() const { return true; }
bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) {
if (Ctor->isImplicit()) { // Was not written in source code
if (const CXXRecordDecl* Class = Ctor->getParent()) {
Match(Class->getName(), Ctor->getLocation());
}
}
return true;
}
};
TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) {
ImplicitCtorVisitor Visitor;
Visitor.ExpectMatch("Simple", 2, 8);
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { Simple(); WithCtor w; }; \n"
"int main() { Simple s; Simple t(s); }\n"));
}
/// \brief A visitor that optionally includes implicit code and matches
/// CXXConstructExpr.
///
/// The name recorded for the match is the name of the class whose constructor
/// is invoked by the CXXConstructExpr, not the name of the class whose
/// constructor the CXXConstructExpr is contained in.
class ConstructExprVisitor
: public ExpectedLocationVisitor<ConstructExprVisitor> {
public:
ConstructExprVisitor() : ShouldVisitImplicitCode(false) {}
bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
void setShouldVisitImplicitCode(bool NewValue) {
ShouldVisitImplicitCode = NewValue;
}
bool VisitCXXConstructExpr(CXXConstructExpr* Expr) {
if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) {
if (const CXXRecordDecl* Class = Ctor->getParent()) {
Match(Class->getName(), Expr->getLocation());
}
}
return true;
}
private:
bool ShouldVisitImplicitCode;
};
TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) {
ConstructExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(true);
Visitor.ExpectMatch("WithCtor", 2, 8);
// Simple has a constructor that implicitly initializes 'w'. Test
// that a visitor that visits implicit code visits that initialization.
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { WithCtor w; }; \n"
"int main() { Simple s; }\n"));
}
// The same as CanVisitImplicitMemberInitializations, but checking that the
// visits are omitted when the visitor does not include implicit code.
TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
ConstructExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(false);
Visitor.DisallowMatch("WithCtor", 2, 8);
// Simple has a constructor that implicitly initializes 'w'. Test
// that a visitor that skips implicit code skips that initialization.
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { WithCtor w; }; \n"
"int main() { Simple s; }\n"));
}
class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> {
public:
DeclRefExprVisitor() : ShouldVisitImplicitCode(false) {}
bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
void setShouldVisitImplicitCode(bool NewValue) {
ShouldVisitImplicitCode = NewValue;
}
bool VisitDeclRefExpr(DeclRefExpr *Reference) {
Match(Reference->getNameInfo().getAsString(), Reference->getLocation());
return true;
}
private:
bool ShouldVisitImplicitCode;
};
TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 2, 3);
EXPECT_TRUE(Visitor.runOver(
"void x(); template <void (*T)()> class X {};\nX<x> y;"));
}
TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 2, 25);
Visitor.ExpectMatch("x", 2, 30);
EXPECT_TRUE(Visitor.runOver(
"int x[5];\n"
"void f() { for (int i : x) { x[0] = 1; } }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsCallExpr) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 1, 22);
EXPECT_TRUE(Visitor.runOver(
"void x(); void y() { x(); }"));
}
TEST(RecursiveASTVisitor, VisitsExplicitLambdaCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 20);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [i]{}; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsUseOfImplicitLambdaCapture) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [=]{ i; }; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsImplicitLambdaCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(true);
// We're expecting the "i" in the lambda to be visited twice:
// - Once for the DeclRefExpr in the lambda capture initialization (whose
// source code location is set to the first use of the variable).
// - Once for the DeclRefExpr for the use of "i" inside the lambda.
Visitor.ExpectMatch("i", 1, 24, /*Times=*/2);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [=]{ i; }; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsLambdaInitCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [a = i + 1]{}; }",
DeclRefExprVisitor::Lang_CXX14));
}
/* FIXME: According to Richard Smith this is a bug in the AST.
TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 3, 43);
EXPECT_TRUE(Visitor.runOver(
"template <typename T> void x();\n"
"template <void (*T)()> class X {};\n"
"template <typename T> class Y : public X< x<T> > {};\n"
"Y<int> y;"));
}
*/
TEST(RecursiveASTVisitor, VisitsExtension) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("s", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"int s = __extension__ (s);\n"));
}
TEST(RecursiveASTVisitor, VisitsCopyExprOfBlockDeclCapture) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 3, 24);
EXPECT_TRUE(Visitor.runOver("void f(int(^)(int)); \n"
"void g() { \n"
" f([&](int x){ return x; }); \n"
"}",
DeclRefExprVisitor::Lang_OBJCXX11));
}
} // end anonymous namespace

View File

@@ -0,0 +1,92 @@
//===- unittest/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
class TypeLocVisitor : public ExpectedLocationVisitor<TypeLocVisitor> {
public:
bool VisitTypeLoc(TypeLoc TypeLocation) {
Match(TypeLocation.getType().getAsString(), TypeLocation.getBeginLoc());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("class X", 1, 30);
EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};"));
}
TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("class X", 3, 18);
EXPECT_TRUE(Visitor.runOver(
"class Y;\n"
"class X {};\n"
"class Y : public X {};"));
}
TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("class X", 2, 18);
EXPECT_TRUE(Visitor.runOver(
"class X {};\n"
"class Y : public X { class Z; };"));
}
TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("X<class Y>", 2, 18);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> class X {};\n"
"class Y : public X<Y> {};"));
}
TEST(RecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("class X", 2, 23);
EXPECT_TRUE(Visitor.runOver(
"class X;\n"
"template<typename T = X> class Y;\n"
"template<typename T> class Y {};\n"));
}
TEST(RecursiveASTVisitor, VisitsCompoundLiteralType) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("struct S", 1, 26);
EXPECT_TRUE(Visitor.runOver(
"int f() { return (struct S { int a; }){.a = 0}.a; }",
TypeLocVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, VisitsObjCPropertyType) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("NSNumber", 2, 33);
EXPECT_TRUE(Visitor.runOver(
"@class NSNumber; \n"
"@interface A @property (retain) NSNumber *x; @end\n",
TypeLocVisitor::Lang_OBJC));
}
TEST(RecursiveASTVisitor, VisitInvalidType) {
TypeLocVisitor Visitor;
// FIXME: It would be nice to have information about subtypes of invalid type
//Visitor.ExpectMatch("typeof(struct F *) []", 1, 1);
// Even if the full type is invalid, it should still find sub types
//Visitor.ExpectMatch("struct F", 1, 19);
EXPECT_FALSE(Visitor.runOver(
"__typeof__(struct F*) var[invalid];\n",
TypeLocVisitor::Lang_C));
}
} // end anonymous namespace

View File

@@ -0,0 +1,248 @@
//===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ReplacementTest.h"
#include "RewriterTestContext.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/Extract/Extract.h"
#include "clang/Tooling/Refactoring/RefactoringAction.h"
#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Errc.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace tooling;
namespace {
class RefactoringActionRulesTest : public ::testing::Test {
protected:
void SetUp() override {
Context.Sources.setMainFileID(
Context.createInMemoryFile("input.cpp", DefaultCode));
}
RewriterTestContext Context;
std::string DefaultCode = std::string(100, 'a');
};
Expected<AtomicChanges>
createReplacements(const std::unique_ptr<RefactoringActionRule> &Rule,
RefactoringRuleContext &Context) {
class Consumer final : public RefactoringResultConsumer {
void handleError(llvm::Error Err) override { Result = std::move(Err); }
void handle(AtomicChanges SourceReplacements) override {
Result = std::move(SourceReplacements);
}
void handle(SymbolOccurrences Occurrences) override {
RefactoringResultConsumer::handle(std::move(Occurrences));
}
public:
Optional<Expected<AtomicChanges>> Result;
};
Consumer C;
Rule->invoke(C, Context);
return std::move(*C.Result);
}
TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
class ReplaceAWithB : public SourceChangeRefactoringRule {
std::pair<SourceRange, int> Selection;
public:
ReplaceAWithB(std::pair<SourceRange, int> Selection)
: Selection(Selection) {}
static Expected<ReplaceAWithB>
initiate(RefactoringRuleContext &Cotnext,
std::pair<SourceRange, int> Selection) {
return ReplaceAWithB(Selection);
}
Expected<AtomicChanges>
createSourceReplacements(RefactoringRuleContext &Context) {
const SourceManager &SM = Context.getSources();
SourceLocation Loc =
Selection.first.getBegin().getLocWithOffset(Selection.second);
AtomicChange Change(SM, Loc);
llvm::Error E = Change.replace(SM, Loc, 1, "b");
if (E)
return std::move(E);
return AtomicChanges{Change};
}
};
class SelectionRequirement : public SourceRangeSelectionRequirement {
public:
Expected<std::pair<SourceRange, int>>
evaluate(RefactoringRuleContext &Context) const {
Expected<SourceRange> R =
SourceRangeSelectionRequirement::evaluate(Context);
if (!R)
return R.takeError();
return std::make_pair(*R, 20);
}
};
auto Rule =
createRefactoringActionRule<ReplaceAWithB>(SelectionRequirement());
// When the requirements are satisifed, the rule's function must be invoked.
{
RefactoringRuleContext RefContext(Context.Sources);
SourceLocation Cursor =
Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID())
.getLocWithOffset(10);
RefContext.setSelectionRange({Cursor, Cursor});
Expected<AtomicChanges> ErrorOrResult =
createReplacements(Rule, RefContext);
ASSERT_FALSE(!ErrorOrResult);
AtomicChanges Result = std::move(*ErrorOrResult);
ASSERT_EQ(Result.size(), 1u);
std::string YAMLString =
const_cast<AtomicChange &>(Result[0]).toYAMLString();
ASSERT_STREQ("---\n"
"Key: 'input.cpp:30'\n"
"FilePath: input.cpp\n"
"Error: ''\n"
"InsertedHeaders: \n"
"RemovedHeaders: \n"
"Replacements: \n" // Extra whitespace here!
" - FilePath: input.cpp\n"
" Offset: 30\n"
" Length: 1\n"
" ReplacementText: b\n"
"...\n",
YAMLString.c_str());
}
// When one of the requirements is not satisfied, invoke should return a
// valid error.
{
RefactoringRuleContext RefContext(Context.Sources);
Expected<AtomicChanges> ErrorOrResult =
createReplacements(Rule, RefContext);
ASSERT_TRUE(!ErrorOrResult);
unsigned DiagID;
llvm::handleAllErrors(ErrorOrResult.takeError(),
[&](DiagnosticError &Error) {
DiagID = Error.getDiagnostic().second.getDiagID();
});
EXPECT_EQ(DiagID, diag::err_refactor_no_selection);
}
}
TEST_F(RefactoringActionRulesTest, ReturnError) {
class ErrorRule : public SourceChangeRefactoringRule {
public:
static Expected<ErrorRule> initiate(RefactoringRuleContext &,
SourceRange R) {
return ErrorRule(R);
}
ErrorRule(SourceRange R) {}
Expected<AtomicChanges> createSourceReplacements(RefactoringRuleContext &) {
return llvm::make_error<llvm::StringError>(
"Error", llvm::make_error_code(llvm::errc::invalid_argument));
}
};
auto Rule =
createRefactoringActionRule<ErrorRule>(SourceRangeSelectionRequirement());
RefactoringRuleContext RefContext(Context.Sources);
SourceLocation Cursor =
Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
RefContext.setSelectionRange({Cursor, Cursor});
Expected<AtomicChanges> Result = createReplacements(Rule, RefContext);
ASSERT_TRUE(!Result);
std::string Message;
llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
Message = Error.getMessage();
});
EXPECT_EQ(Message, "Error");
}
Optional<SymbolOccurrences> findOccurrences(RefactoringActionRule &Rule,
RefactoringRuleContext &Context) {
class Consumer final : public RefactoringResultConsumer {
void handleError(llvm::Error) override {}
void handle(SymbolOccurrences Occurrences) override {
Result = std::move(Occurrences);
}
void handle(AtomicChanges Changes) override {
RefactoringResultConsumer::handle(std::move(Changes));
}
public:
Optional<SymbolOccurrences> Result;
};
Consumer C;
Rule.invoke(C, Context);
return std::move(C.Result);
}
TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) {
class FindOccurrences : public FindSymbolOccurrencesRefactoringRule {
SourceRange Selection;
public:
FindOccurrences(SourceRange Selection) : Selection(Selection) {}
static Expected<FindOccurrences> initiate(RefactoringRuleContext &,
SourceRange Selection) {
return FindOccurrences(Selection);
}
Expected<SymbolOccurrences>
findSymbolOccurrences(RefactoringRuleContext &) override {
SymbolOccurrences Occurrences;
Occurrences.push_back(SymbolOccurrence(SymbolName("test"),
SymbolOccurrence::MatchingSymbol,
Selection.getBegin()));
return std::move(Occurrences);
}
};
auto Rule = createRefactoringActionRule<FindOccurrences>(
SourceRangeSelectionRequirement());
RefactoringRuleContext RefContext(Context.Sources);
SourceLocation Cursor =
Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
RefContext.setSelectionRange({Cursor, Cursor});
Optional<SymbolOccurrences> Result = findOccurrences(*Rule, RefContext);
ASSERT_FALSE(!Result);
SymbolOccurrences Occurrences = std::move(*Result);
EXPECT_EQ(Occurrences.size(), 1u);
EXPECT_EQ(Occurrences[0].getKind(), SymbolOccurrence::MatchingSymbol);
EXPECT_EQ(Occurrences[0].getNameRanges().size(), 1u);
EXPECT_EQ(Occurrences[0].getNameRanges()[0],
SourceRange(Cursor, Cursor.getLocWithOffset(strlen("test"))));
}
TEST_F(RefactoringActionRulesTest, EditorCommandBinding) {
const RefactoringDescriptor &Descriptor = ExtractFunction::describe();
EXPECT_EQ(Descriptor.Name, "extract-function");
EXPECT_EQ(
Descriptor.Description,
"(WIP action; use with caution!) Extracts code into a new function");
EXPECT_EQ(Descriptor.Title, "Extract Function");
}
} // end anonymous namespace

View File

@@ -0,0 +1,153 @@
//===- unittest/Tooling/RefactoringCallbacksTest.cpp ----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "RewriterTestContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/RefactoringCallbacks.h"
#include "gtest/gtest.h"
namespace clang {
namespace tooling {
using namespace ast_matchers;
template <typename T>
void expectRewritten(const std::string &Code, const std::string &Expected,
const T &AMatcher, RefactoringCallback &Callback) {
std::map<std::string, Replacements> FileToReplace;
ASTMatchRefactorer Finder(FileToReplace);
Finder.addMatcher(AMatcher, &Callback);
std::unique_ptr<tooling::FrontendActionFactory> Factory(
tooling::newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
<< "Parsing error in \"" << Code << "\"";
RewriterTestContext Context;
FileID ID = Context.createInMemoryFile("input.cc", Code);
EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
Context.Rewrite));
EXPECT_EQ(Expected, Context.getRewrittenText(ID));
}
TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { ; }";
ReplaceStmtWithText Callback("id", ";");
expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
std::string Code = "#define A void f() { int i = 1; }\nA";
std::string Expected = "#define A void f() { ; }\nA";
ReplaceStmtWithText Callback("id", ";");
expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
std::string Code = "#define A void f() { int i = 1; }";
std::string Expected = "#define A void f() { int i = 1; }";
ReplaceStmtWithText Callback("id", ";");
expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, ReplacesInteger) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { int i = 2; }";
ReplaceStmtWithText Callback("id", "2");
expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
}
TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
std::string Code = "void f() { int i = false ? 1 : i * 2; }";
std::string Expected = "void f() { int i = i * 2; }";
ReplaceStmtWithStmt Callback("always-false", "should-be");
expectRewritten(
Code, Expected,
id("always-false",
conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
hasFalseExpression(id("should-be", expr())))),
Callback);
}
TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
std::string Expected = "bool a; void f() { f(); }";
ReplaceIfStmtWithItsBody Callback("id", true);
expectRewritten(
Code, Expected,
id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
declRefExpr(to(varDecl(hasName("a"))))))))),
Callback);
}
TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
std::string Code = "void f() { if (false) int i = 0; }";
std::string Expected = "void f() { }";
ReplaceIfStmtWithItsBody Callback("id", false);
expectRewritten(Code, Expected,
id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false))))),
Callback);
}
TEST(RefactoringCallbacksTest, TemplateJustText) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { FOO }";
auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
EXPECT_FALSE(Callback.takeError());
expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
}
TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { long x = 1; }";
auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
EXPECT_FALSE(Callback.takeError());
expectRewritten(Code, Expected,
id("decl", varDecl(hasInitializer(id("init", expr())))),
**Callback);
}
TEST(RefactoringCallbacksTest, TemplateLiteral) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { string x = \"$-1\"; }";
auto Callback = ReplaceNodeWithTemplate::create("decl",
"string x = \"$$-${init}\"");
EXPECT_FALSE(Callback.takeError());
expectRewritten(Code, Expected,
id("decl", varDecl(hasInitializer(id("init", expr())))),
**Callback);
}
static void ExpectStringError(const std::string &Expected,
llvm::Error E) {
std::string Found;
handleAllErrors(std::move(E), [&](const llvm::StringError &SE) {
llvm::raw_string_ostream Stream(Found);
SE.log(Stream);
});
EXPECT_EQ(Expected, Found);
}
TEST(RefactoringCallbacksTest, TemplateUnterminated) {
auto Callback = ReplaceNodeWithTemplate::create("decl",
"string x = \"$$-${init\"");
ExpectStringError("Unterminated ${...} in replacement template near ${init\"",
Callback.takeError());
}
TEST(RefactoringCallbacksTest, TemplateUnknownDollar) {
auto Callback = ReplaceNodeWithTemplate::create("decl",
"string x = \"$<");
ExpectStringError("Invalid $ in replacement template near $<",
Callback.takeError());
}
} // end namespace ast_matchers
} // end namespace clang

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
//===- unittest/Tooling/ReplacementTest.h - Replacements related test------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines utility class and function for Replacement related tests.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
#define LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H
#include "RewriterTestContext.h"
#include "clang/Tooling/Core/Replacement.h"
#include "gtest/gtest.h"
namespace clang {
namespace tooling {
/// \brief Converts a set of replacements to Replacements class.
/// \return A Replacements class containing \p Replaces on success; otherwise,
/// an empty Replacements is returned.
inline tooling::Replacements
toReplacements(const std::set<tooling::Replacement> &Replaces) {
tooling::Replacements Result;
for (const auto &R : Replaces) {
auto Err = Result.add(R);
EXPECT_TRUE(!Err);
if (Err) {
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
return tooling::Replacements();
}
}
return Result;
}
/// \brief A utility class for replacement related tests.
class ReplacementTest : public ::testing::Test {
protected:
tooling::Replacement createReplacement(SourceLocation Start, unsigned Length,
llvm::StringRef ReplacementText) {
return tooling::Replacement(Context.Sources, Start, Length,
ReplacementText);
}
RewriterTestContext Context;
};
} // namespace tooling
} // namespace clang
#endif // LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H

Some files were not shown because too many files have changed in this diff Show More