//===- unittest/Tooling/RefactoringCallbacksTest.cpp ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "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 void expectRewritten(const std::string &Code, const std::string &Expected, const T &AMatcher, RefactoringCallback &Callback) { std::map FileToReplace; ASTMatchRefactorer Finder(FileToReplace); Finder.addMatcher(AMatcher, &Callback); std::unique_ptr 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, declStmt().bind("id"), 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, declStmt().bind("id"), 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, declStmt().bind("id"), 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, expr(integerLiteral()).bind("id"), 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, conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))), hasFalseExpression(expr().bind("should-be"))) .bind("always-false"), 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, ifStmt(hasCondition(implicitCastExpr(hasSourceExpression( declRefExpr(to(varDecl(hasName("a")))))))) .bind("id"), 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, ifStmt(hasCondition(cxxBoolLiteral(equals(false)))).bind("id"), 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, declStmt().bind("id"), **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, varDecl(hasInitializer(expr().bind("init"))).bind("decl"), **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, varDecl(hasInitializer(expr().bind("init"))).bind("decl"), **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