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