1 //===- unittest/Tooling/RefactoringCallbacksTest.cpp ----------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "RewriterTestContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Tooling/RefactoringCallbacks.h"
14 #include "gtest/gtest.h"
15 
16 namespace clang {
17 namespace tooling {
18 
19 using namespace ast_matchers;
20 
21 template <typename T>
22 void expectRewritten(const std::string &Code, const std::string &Expected,
23                      const T &AMatcher, RefactoringCallback &Callback) {
24   std::map<std::string, Replacements> FileToReplace;
25   ASTMatchRefactorer Finder(FileToReplace);
26   Finder.addMatcher(AMatcher, &Callback);
27   std::unique_ptr<tooling::FrontendActionFactory> Factory(
28       tooling::newFrontendActionFactory(&Finder));
29   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
30       << "Parsing error in \"" << Code << "\"";
31   RewriterTestContext Context;
32   FileID ID = Context.createInMemoryFile("input.cc", Code);
33   EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
34                                             Context.Rewrite));
35   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
36 }
37 
38 TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
39   std::string Code = "void f() { int i = 1; }";
40   std::string Expected = "void f() { ; }";
41   ReplaceStmtWithText Callback("id", ";");
42   expectRewritten(Code, Expected, id("id", declStmt()), Callback);
43 }
44 
45 TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
46   std::string Code = "#define A void f() { int i = 1; }\nA";
47   std::string Expected = "#define A void f() { ; }\nA";
48   ReplaceStmtWithText Callback("id", ";");
49   expectRewritten(Code, Expected, id("id", declStmt()), Callback);
50 }
51 
52 TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
53   std::string Code = "#define A void f() { int i = 1; }";
54   std::string Expected = "#define A void f() { int i = 1; }";
55   ReplaceStmtWithText Callback("id", ";");
56   expectRewritten(Code, Expected, id("id", declStmt()), Callback);
57 }
58 
59 TEST(RefactoringCallbacksTest, ReplacesInteger) {
60   std::string Code = "void f() { int i = 1; }";
61   std::string Expected = "void f() { int i = 2; }";
62   ReplaceStmtWithText Callback("id", "2");
63   expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
64 }
65 
66 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
67   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
68   std::string Expected = "void f() { int i = i * 2; }";
69   ReplaceStmtWithStmt Callback("always-false", "should-be");
70   expectRewritten(
71       Code, Expected,
72       id("always-false",
73          conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
74                              hasFalseExpression(id("should-be", expr())))),
75       Callback);
76 }
77 
78 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
79   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
80   std::string Expected = "bool a; void f() { f(); }";
81   ReplaceIfStmtWithItsBody Callback("id", true);
82   expectRewritten(
83       Code, Expected,
84       id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
85                    declRefExpr(to(varDecl(hasName("a"))))))))),
86       Callback);
87 }
88 
89 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
90   std::string Code = "void f() { if (false) int i = 0; }";
91   std::string Expected = "void f() {  }";
92   ReplaceIfStmtWithItsBody Callback("id", false);
93   expectRewritten(Code, Expected,
94                   id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false))))),
95                   Callback);
96 }
97 
98 TEST(RefactoringCallbacksTest, TemplateJustText) {
99   std::string Code = "void f() { int i = 1; }";
100   std::string Expected = "void f() { FOO }";
101   auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
102   EXPECT_FALSE(Callback.takeError());
103   expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
104 }
105 
106 TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
107   std::string Code = "void f() { int i = 1; }";
108   std::string Expected = "void f() { long x = 1; }";
109   auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
110   EXPECT_FALSE(Callback.takeError());
111   expectRewritten(Code, Expected,
112                   id("decl", varDecl(hasInitializer(id("init", expr())))),
113                   **Callback);
114 }
115 
116 TEST(RefactoringCallbacksTest, TemplateLiteral) {
117   std::string Code = "void f() { int i = 1; }";
118   std::string Expected = "void f() { string x = \"$-1\"; }";
119   auto Callback = ReplaceNodeWithTemplate::create("decl",
120                                                   "string x = \"$$-${init}\"");
121   EXPECT_FALSE(Callback.takeError());
122   expectRewritten(Code, Expected,
123                   id("decl", varDecl(hasInitializer(id("init", expr())))),
124                   **Callback);
125 }
126 
127 static void ExpectStringError(const std::string &Expected,
128                               llvm::Error E) {
129   std::string Found;
130   handleAllErrors(std::move(E), [&](const llvm::StringError &SE) {
131       llvm::raw_string_ostream Stream(Found);
132       SE.log(Stream);
133     });
134   EXPECT_EQ(Expected, Found);
135 }
136 
137 TEST(RefactoringCallbacksTest, TemplateUnterminated) {
138   auto Callback = ReplaceNodeWithTemplate::create("decl",
139                                                   "string x = \"$$-${init\"");
140   ExpectStringError("Unterminated ${...} in replacement template near ${init\"",
141                     Callback.takeError());
142 }
143 
144 TEST(RefactoringCallbacksTest, TemplateUnknownDollar) {
145   auto Callback = ReplaceNodeWithTemplate::create("decl",
146                                                   "string x = \"$<");
147   ExpectStringError("Invalid $ in replacement template near $<",
148                     Callback.takeError());
149 }
150 
151 
152 } // end namespace ast_matchers
153 } // end namespace clang
154