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 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 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 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 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 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 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 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