xref: /llvm-project/clang/unittests/Tooling/FixItTest.cpp (revision a7691dee2d3c0ea3f9f4d14c7e52f1359f23671c)
1 //===- unittest/Tooling/FixitTest.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Tooling/FixIt.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Testing/TestAST.h"
13 #include "gtest/gtest.h"
14 
15 using namespace clang;
16 
17 using tooling::fixit::getText;
18 using tooling::fixit::createRemoval;
19 using tooling::fixit::createReplacement;
20 
21 namespace {
22 
onlyCall(ASTContext & Ctx)23 const CallExpr &onlyCall(ASTContext &Ctx) {
24   using namespace ast_matchers;
25   auto Calls = match(callExpr().bind(""), Ctx);
26   EXPECT_EQ(Calls.size(), 1u);
27   return *Calls.front().getNodeAs<CallExpr>("");
28 }
29 
TEST(FixItTest,getText)30 TEST(FixItTest, getText) {
31   TestAST AST("void foo(int x, int y) { foo(x, y); }");
32   const CallExpr &CE = onlyCall(AST.context());
33   EXPECT_EQ("foo(x, y)", getText(CE, AST.context()));
34   EXPECT_EQ("foo(x, y)", getText(CE.getSourceRange(), AST.context()));
35   EXPECT_EQ("x", getText(*CE.getArg(0), AST.context()));
36   EXPECT_EQ("y", getText(*CE.getArg(1), AST.context()));
37 
38   AST = TestAST("#define APPLY(f, x, y) f(x, y)\n"
39                 "void foo(int x, int y) { APPLY(foo, x, y); }");
40   const CallExpr &CE2 = onlyCall(AST.context());
41   EXPECT_EQ("APPLY(foo, x, y)", getText(CE2, AST.context()));
42 }
43 
TEST(FixItTest,getTextWithMacro)44 TEST(FixItTest, getTextWithMacro) {
45   TestAST AST("#define F foo(\n"
46               "#define OO x, y)\n"
47               "void foo(int x, int y) { F OO ; }");
48   const CallExpr &CE = onlyCall(AST.context());
49   EXPECT_EQ("F OO", getText(CE, AST.context()));
50   EXPECT_EQ("", getText(*CE.getArg(0), AST.context()));
51   EXPECT_EQ("", getText(*CE.getArg(1), AST.context()));
52 
53   AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
54                 "void foo(int x, int y) { FOO(x,y) }");
55   const CallExpr &CE2 = onlyCall(AST.context());
56   EXPECT_EQ("", getText(CE2, AST.context()));
57   EXPECT_EQ("x", getText(*CE2.getArg(0), AST.context()));
58   EXPECT_EQ("y", getText(*CE2.getArg(1), AST.context()));
59 }
60 
TEST(FixItTest,createRemoval)61 TEST(FixItTest, createRemoval) {
62   TestAST AST("void foo(int x, int y) { foo(x, y); }");
63   const CallExpr &CE = onlyCall(AST.context());
64 
65   FixItHint Hint = createRemoval(CE);
66   EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), AST.context()));
67   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
68   EXPECT_TRUE(Hint.CodeToInsert.empty());
69 
70   FixItHint Hint0 = createRemoval(*CE.getArg(0));
71   EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), AST.context()));
72   EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
73   EXPECT_TRUE(Hint0.CodeToInsert.empty());
74 
75   FixItHint Hint1 = createRemoval(*CE.getArg(1));
76   EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), AST.context()));
77   EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
78   EXPECT_TRUE(Hint1.CodeToInsert.empty());
79 
80   AST = TestAST("void foo(int x, int y) { foo(x + y, y + x); }");
81   const CallExpr &CE2 = onlyCall(AST.context());
82   Hint0 = createRemoval(*CE2.getArg(0));
83   EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), AST.context()));
84 
85   Hint1 = createRemoval(*CE2.getArg(1));
86   EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), AST.context()));
87 }
88 
TEST(FixItTest,createRemovalWithMacro)89 TEST(FixItTest, createRemovalWithMacro) {
90   TestAST AST("#define FOO foo(1, 1)\n"
91               "void foo(int x, int y) { FOO; }");
92   const CallExpr &CE = onlyCall(AST.context());
93   FixItHint Hint = createRemoval(CE);
94   EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), AST.context()));
95   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
96   EXPECT_TRUE(Hint.CodeToInsert.empty());
97 
98   FixItHint Hint0 = createRemoval(*CE.getArg(0));
99   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",
100             Hint0.RemoveRange.getBegin().printToString(AST.sourceManager()));
101   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",
102             Hint0.RemoveRange.getEnd().printToString(AST.sourceManager()));
103   EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
104   EXPECT_TRUE(Hint0.CodeToInsert.empty());
105 
106   FixItHint Hint1 = createRemoval(*CE.getArg(1));
107   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:20>",
108             Hint1.RemoveRange.getBegin().printToString(AST.sourceManager()));
109   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:20>",
110             Hint1.RemoveRange.getEnd().printToString(AST.sourceManager()));
111   EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
112   EXPECT_TRUE(Hint1.CodeToInsert.empty());
113 
114   AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
115                 "void foo(int x, int y) { FOO(x,y) }");
116   const CallExpr &CE2 = onlyCall(AST.context());
117   Hint = createRemoval(CE2);
118   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:37>",
119             Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));
120   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:45>",
121             Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));
122   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
123   EXPECT_TRUE(Hint.CodeToInsert.empty());
124 }
125 
TEST(FixItTest,createReplacement)126 TEST(FixItTest, createReplacement) {
127   for (const char *Code : {
128            "void foo(int x, int y) { foo(x, y); }",
129 
130            "#define APPLY(f, x, y) f(x, y)\n"
131            "void foo(int x, int y) { APPLY(foo, x, y); }",
132 
133            "#define APPLY(f, P) f(P)\n"
134            "#define PAIR(x, y) x, y\n"
135            "void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n",
136        }) {
137     TestAST AST(Code);
138     const CallExpr &CE = onlyCall(AST.context());
139     const Expr *P0 = CE.getArg(0);
140     const Expr *P1 = CE.getArg(1);
141     FixItHint Hint0 = createReplacement(*P0, *P1, AST.context());
142     FixItHint Hint1 = createReplacement(*P1, *P0, AST.context());
143 
144     // Validate Hint0 fields.
145     EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), AST.context()));
146     EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
147     EXPECT_EQ(Hint0.CodeToInsert, "y");
148 
149     // Validate Hint1 fields.
150     EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), AST.context()));
151     EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
152     EXPECT_EQ(Hint1.CodeToInsert, "x");
153   }
154 }
155 
TEST(FixItTest,createReplacementWithMacro)156 TEST(FixItTest, createReplacementWithMacro) {
157   TestAST AST("#define FOO foo(1, 1)\n"
158               "void foo(int x, int y) { FOO; }");
159   const CallExpr &CE = onlyCall(AST.context());
160   FixItHint Hint =
161       createReplacement(*CE.getArg(0), *CE.getArg(1), AST.context());
162   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",
163             Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));
164   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",
165             Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));
166   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
167   EXPECT_TRUE(Hint.CodeToInsert.empty());
168 
169   AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
170                 "void foo(int x, int y) { FOO(x,y) }");
171   const CallExpr &CE2 = onlyCall(AST.context());
172   Hint = createReplacement(*CE2.getArg(0), *CE2.getArg(1), AST.context());
173   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:2:30>",
174             Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));
175   EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:2:30>",
176             Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));
177   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
178   EXPECT_EQ("y", Hint.CodeToInsert);
179 
180   AST = TestAST("void foo(int x, int y) { foo(x + y, y + x); }");
181   const CallExpr &CE3 = onlyCall(AST.context());
182   Hint = createReplacement(*CE3.getArg(0), *CE3.getArg(1), AST.context());
183   EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), AST.context()));
184   EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
185   EXPECT_EQ("y + x", Hint.CodeToInsert);
186 }
187 
188 } // end anonymous namespace
189