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 "TestVisitor.h" 10 #include "clang/Basic/Diagnostic.h" 11 #include "clang/Tooling/FixIt.h" 12 13 using namespace clang; 14 15 using tooling::fixit::getText; 16 using tooling::fixit::getExtendedText; 17 using tooling::fixit::createRemoval; 18 using tooling::fixit::createReplacement; 19 20 namespace { 21 22 struct CallsVisitor : TestVisitor<CallsVisitor> { 23 bool VisitCallExpr(CallExpr *Expr) { 24 OnCall(Expr, Context); 25 return true; 26 } 27 28 std::function<void(CallExpr *, ASTContext *Context)> OnCall; 29 }; 30 31 std::string LocationToString(SourceLocation Loc, ASTContext *Context) { 32 return Loc.printToString(Context->getSourceManager()); 33 } 34 35 TEST(FixItTest, getText) { 36 CallsVisitor Visitor; 37 38 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 39 EXPECT_EQ("foo(x, y)", getText(*CE, *Context)); 40 EXPECT_EQ("foo(x, y)", getText(CE->getSourceRange(), *Context)); 41 42 Expr *P0 = CE->getArg(0); 43 Expr *P1 = CE->getArg(1); 44 EXPECT_EQ("x", getText(*P0, *Context)); 45 EXPECT_EQ("y", getText(*P1, *Context)); 46 }; 47 Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); 48 49 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 50 EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context)); 51 }; 52 Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" 53 "void foo(int x, int y) { APPLY(foo, x, y); }"); 54 } 55 56 TEST(FixItTest, getTextWithMacro) { 57 CallsVisitor Visitor; 58 59 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 60 EXPECT_EQ("F OO", getText(*CE, *Context)); 61 Expr *P0 = CE->getArg(0); 62 Expr *P1 = CE->getArg(1); 63 EXPECT_EQ("", getText(*P0, *Context)); 64 EXPECT_EQ("", getText(*P1, *Context)); 65 }; 66 Visitor.runOver("#define F foo(\n" 67 "#define OO x, y)\n" 68 "void foo(int x, int y) { F OO ; }"); 69 70 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 71 EXPECT_EQ("", getText(*CE, *Context)); 72 Expr *P0 = CE->getArg(0); 73 Expr *P1 = CE->getArg(1); 74 EXPECT_EQ("x", getText(*P0, *Context)); 75 EXPECT_EQ("y", getText(*P1, *Context)); 76 }; 77 Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" 78 "void foo(int x, int y) { FOO(x,y) }"); 79 } 80 81 TEST(FixItTest, getExtendedText) { 82 CallsVisitor Visitor; 83 84 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 85 EXPECT_EQ("foo(x, y);", 86 getExtendedText(*CE, tok::TokenKind::semi, *Context)); 87 88 Expr *P0 = CE->getArg(0); 89 Expr *P1 = CE->getArg(1); 90 EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); 91 EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); 92 EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); 93 }; 94 Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); 95 Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); 96 Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); 97 Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); 98 Visitor.runOver( 99 "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); 100 101 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 102 EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); 103 }; 104 Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); 105 Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); 106 Visitor.runOver("int foo() { return foo() + 3; }"); 107 } 108 109 TEST(FixItTest, createRemoval) { 110 CallsVisitor Visitor; 111 112 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 113 FixItHint Hint = createRemoval(*CE); 114 EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), *Context)); 115 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 116 EXPECT_TRUE(Hint.CodeToInsert.empty()); 117 118 Expr *P0 = CE->getArg(0); 119 FixItHint Hint0 = createRemoval(*P0); 120 EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context)); 121 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); 122 EXPECT_TRUE(Hint0.CodeToInsert.empty()); 123 124 Expr *P1 = CE->getArg(1); 125 FixItHint Hint1 = createRemoval(*P1); 126 EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context)); 127 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); 128 EXPECT_TRUE(Hint1.CodeToInsert.empty()); 129 }; 130 Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); 131 132 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 133 Expr *P0 = CE->getArg(0); 134 FixItHint Hint0 = createRemoval(*P0); 135 EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), *Context)); 136 137 Expr *P1 = CE->getArg(1); 138 FixItHint Hint1 = createRemoval(*P1); 139 EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), *Context)); 140 }; 141 Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }"); 142 } 143 144 TEST(FixItTest, createRemovalWithMacro) { 145 CallsVisitor Visitor; 146 147 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 148 FixItHint Hint = createRemoval(*CE); 149 EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), *Context)); 150 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 151 EXPECT_TRUE(Hint.CodeToInsert.empty()); 152 153 Expr *P0 = CE->getArg(0); 154 FixItHint Hint0 = createRemoval(*P0); 155 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", 156 LocationToString(Hint0.RemoveRange.getBegin(), Context)); 157 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", 158 LocationToString(Hint0.RemoveRange.getEnd(), Context)); 159 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); 160 EXPECT_TRUE(Hint0.CodeToInsert.empty()); 161 162 Expr *P1 = CE->getArg(1); 163 FixItHint Hint1 = createRemoval(*P1); 164 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>", 165 LocationToString(Hint1.RemoveRange.getBegin(), Context)); 166 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>", 167 LocationToString(Hint1.RemoveRange.getEnd(), Context)); 168 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); 169 EXPECT_TRUE(Hint1.CodeToInsert.empty()); 170 }; 171 Visitor.runOver("#define FOO foo(1, 1)\n" 172 "void foo(int x, int y) { FOO; }"); 173 174 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 175 FixItHint Hint = createRemoval(*CE); 176 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:37>", 177 LocationToString(Hint.RemoveRange.getBegin(), Context)); 178 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:45>", 179 LocationToString(Hint.RemoveRange.getEnd(), Context)); 180 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 181 EXPECT_TRUE(Hint.CodeToInsert.empty()); 182 }; 183 Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" 184 "void foo(int x, int y) { FOO(x,y) }"); 185 } 186 187 TEST(FixItTest, createReplacement) { 188 CallsVisitor Visitor; 189 190 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 191 Expr *P0 = CE->getArg(0); 192 Expr *P1 = CE->getArg(1); 193 FixItHint Hint0 = createReplacement(*P0, *P1, *Context); 194 FixItHint Hint1 = createReplacement(*P1, *P0, *Context); 195 196 // Validate Hint0 fields. 197 EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context)); 198 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); 199 EXPECT_EQ(Hint0.CodeToInsert, "y"); 200 201 // Validate Hint1 fields. 202 EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context)); 203 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); 204 EXPECT_EQ(Hint1.CodeToInsert, "x"); 205 }; 206 207 Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); 208 209 Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" 210 "void foo(int x, int y) { APPLY(foo, x, y); }"); 211 212 Visitor.runOver("#define APPLY(f, P) f(P)\n" 213 "#define PAIR(x, y) x, y\n" 214 "void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n"); 215 } 216 217 TEST(FixItTest, createReplacementWithMacro) { 218 CallsVisitor Visitor; 219 220 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 221 Expr *P0 = CE->getArg(0); 222 Expr *P1 = CE->getArg(1); 223 FixItHint Hint = createReplacement(*P0, *P1, *Context); 224 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", 225 LocationToString(Hint.RemoveRange.getBegin(), Context)); 226 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", 227 LocationToString(Hint.RemoveRange.getEnd(), Context)); 228 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 229 EXPECT_TRUE(Hint.CodeToInsert.empty()); 230 }; 231 232 Visitor.runOver("#define FOO foo(1, 1)\n" 233 "void foo(int x, int y) { FOO; }"); 234 235 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 236 Expr *P0 = CE->getArg(0); 237 Expr *P1 = CE->getArg(1); 238 FixItHint Hint = createReplacement(*P0, *P1, *Context); 239 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>", 240 LocationToString(Hint.RemoveRange.getBegin(), Context)); 241 EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>", 242 LocationToString(Hint.RemoveRange.getEnd(), Context)); 243 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 244 EXPECT_EQ("y", Hint.CodeToInsert); 245 }; 246 Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" 247 "void foo(int x, int y) { FOO(x,y) }"); 248 249 Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { 250 Expr *P0 = CE->getArg(0); 251 Expr *P1 = CE->getArg(1); 252 FixItHint Hint = createReplacement(*P0, *P1, *Context); 253 EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), *Context)); 254 EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); 255 EXPECT_EQ("y + x", Hint.CodeToInsert); 256 }; 257 Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }"); 258 } 259 260 } // end anonymous namespace 261