1 //===- unittest/Tooling/TransformerTest.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/Refactoring/Transformer.h" 10 #include "clang/ASTMatchers/ASTMatchers.h" 11 #include "clang/Tooling/Refactoring/RangeSelector.h" 12 #include "clang/Tooling/Tooling.h" 13 #include "llvm/Support/Errc.h" 14 #include "llvm/Support/Error.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 18 using namespace clang; 19 using namespace tooling; 20 using namespace ast_matchers; 21 22 namespace { 23 using ::testing::IsEmpty; 24 25 constexpr char KHeaderContents[] = R"cc( 26 struct string { 27 string(const char*); 28 char* c_str(); 29 int size(); 30 }; 31 int strlen(const char*); 32 33 namespace proto { 34 struct PCFProto { 35 int foo(); 36 }; 37 struct ProtoCommandLineFlag : PCFProto { 38 PCFProto& GetProto(); 39 }; 40 } // namespace proto 41 class Logger {}; 42 void operator<<(Logger& l, string msg); 43 Logger& log(int level); 44 )cc"; 45 46 static ast_matchers::internal::Matcher<clang::QualType> 47 isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) { 48 return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher)); 49 } 50 51 static std::string format(StringRef Code) { 52 const std::vector<Range> Ranges(1, Range(0, Code.size())); 53 auto Style = format::getLLVMStyle(); 54 const auto Replacements = format::reformat(Style, Code, Ranges); 55 auto Formatted = applyAllReplacements(Code, Replacements); 56 if (!Formatted) { 57 ADD_FAILURE() << "Could not format code: " 58 << llvm::toString(Formatted.takeError()); 59 return std::string(); 60 } 61 return *Formatted; 62 } 63 64 static void compareSnippets(StringRef Expected, 65 const llvm::Optional<std::string> &MaybeActual) { 66 ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected; 67 auto Actual = *MaybeActual; 68 std::string HL = "#include \"header.h\"\n"; 69 auto I = Actual.find(HL); 70 if (I != std::string::npos) 71 Actual.erase(I, HL.size()); 72 EXPECT_EQ(format(Expected), format(Actual)); 73 } 74 75 // FIXME: consider separating this class into its own file(s). 76 class ClangRefactoringTestBase : public testing::Test { 77 protected: 78 void appendToHeader(StringRef S) { FileContents[0].second += S; } 79 80 void addFile(StringRef Filename, StringRef Content) { 81 FileContents.emplace_back(Filename, Content); 82 } 83 84 llvm::Optional<std::string> rewrite(StringRef Input) { 85 std::string Code = ("#include \"header.h\"\n" + Input).str(); 86 auto Factory = newFrontendActionFactory(&MatchFinder); 87 if (!runToolOnCodeWithArgs( 88 Factory->create(), Code, std::vector<std::string>(), "input.cc", 89 "clang-tool", std::make_shared<PCHContainerOperations>(), 90 FileContents)) { 91 llvm::errs() << "Running tool failed.\n"; 92 return None; 93 } 94 if (ErrorCount != 0) { 95 llvm::errs() << "Generating changes failed.\n"; 96 return None; 97 } 98 auto ChangedCode = 99 applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec()); 100 if (!ChangedCode) { 101 llvm::errs() << "Applying changes failed: " 102 << llvm::toString(ChangedCode.takeError()) << "\n"; 103 return None; 104 } 105 return *ChangedCode; 106 } 107 108 Transformer::ChangeConsumer consumer() { 109 return [this](Expected<AtomicChange> C) { 110 if (C) { 111 Changes.push_back(std::move(*C)); 112 } else { 113 consumeError(C.takeError()); 114 ++ErrorCount; 115 } 116 }; 117 } 118 119 template <typename R> 120 void testRule(R Rule, StringRef Input, StringRef Expected) { 121 Transformer T(std::move(Rule), consumer()); 122 T.registerMatchers(&MatchFinder); 123 compareSnippets(Expected, rewrite(Input)); 124 } 125 126 clang::ast_matchers::MatchFinder MatchFinder; 127 // Records whether any errors occurred in individual changes. 128 int ErrorCount = 0; 129 AtomicChanges Changes; 130 131 private: 132 FileContentMappings FileContents = {{"header.h", ""}}; 133 }; 134 135 class TransformerTest : public ClangRefactoringTestBase { 136 protected: 137 TransformerTest() { appendToHeader(KHeaderContents); } 138 }; 139 140 // Given string s, change strlen($s.c_str()) to $s.size(). 141 static RewriteRule ruleStrlenSize() { 142 StringRef StringExpr = "strexpr"; 143 auto StringType = namedDecl(hasAnyName("::basic_string", "::string")); 144 auto R = makeRule( 145 callExpr(callee(functionDecl(hasName("strlen"))), 146 hasArgument(0, cxxMemberCallExpr( 147 on(expr(hasType(isOrPointsTo(StringType))) 148 .bind(StringExpr)), 149 callee(cxxMethodDecl(hasName("c_str")))))), 150 change(text("REPLACED")), text("Use size() method directly on string.")); 151 return R; 152 } 153 154 TEST_F(TransformerTest, StrlenSize) { 155 std::string Input = "int f(string s) { return strlen(s.c_str()); }"; 156 std::string Expected = "int f(string s) { return REPLACED; }"; 157 testRule(ruleStrlenSize(), Input, Expected); 158 } 159 160 // Tests that no change is applied when a match is not expected. 161 TEST_F(TransformerTest, NoMatch) { 162 std::string Input = "int f(string s) { return s.size(); }"; 163 testRule(ruleStrlenSize(), Input, Input); 164 } 165 166 // Tests that expressions in macro arguments are rewritten (when applicable). 167 TEST_F(TransformerTest, StrlenSizeMacro) { 168 std::string Input = R"cc( 169 #define ID(e) e 170 int f(string s) { return ID(strlen(s.c_str())); })cc"; 171 std::string Expected = R"cc( 172 #define ID(e) e 173 int f(string s) { return ID(REPLACED); })cc"; 174 testRule(ruleStrlenSize(), Input, Expected); 175 } 176 177 // Tests replacing an expression. 178 TEST_F(TransformerTest, Flag) { 179 StringRef Flag = "flag"; 180 RewriteRule Rule = makeRule( 181 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl( 182 hasName("proto::ProtoCommandLineFlag")))) 183 .bind(Flag)), 184 unless(callee(cxxMethodDecl(hasName("GetProto"))))), 185 change(node(Flag), text("EXPR"))); 186 187 std::string Input = R"cc( 188 proto::ProtoCommandLineFlag flag; 189 int x = flag.foo(); 190 int y = flag.GetProto().foo(); 191 )cc"; 192 std::string Expected = R"cc( 193 proto::ProtoCommandLineFlag flag; 194 int x = EXPR.foo(); 195 int y = flag.GetProto().foo(); 196 )cc"; 197 198 testRule(std::move(Rule), Input, Expected); 199 } 200 201 TEST_F(TransformerTest, NodePartNameNamedDecl) { 202 StringRef Fun = "fun"; 203 RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun), 204 change(name(Fun), text("good"))); 205 206 std::string Input = R"cc( 207 int bad(int x); 208 int bad(int x) { return x * x; } 209 )cc"; 210 std::string Expected = R"cc( 211 int good(int x); 212 int good(int x) { return x * x; } 213 )cc"; 214 215 testRule(Rule, Input, Expected); 216 } 217 218 TEST_F(TransformerTest, NodePartNameDeclRef) { 219 std::string Input = R"cc( 220 template <typename T> 221 T bad(T x) { 222 return x; 223 } 224 int neutral(int x) { return bad<int>(x) * x; } 225 )cc"; 226 std::string Expected = R"cc( 227 template <typename T> 228 T bad(T x) { 229 return x; 230 } 231 int neutral(int x) { return good<int>(x) * x; } 232 )cc"; 233 234 StringRef Ref = "ref"; 235 testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref), 236 change(name(Ref), text("good"))), 237 Input, Expected); 238 } 239 240 TEST_F(TransformerTest, NodePartNameDeclRefFailure) { 241 std::string Input = R"cc( 242 struct Y { 243 int operator*(); 244 }; 245 int neutral(int x) { 246 Y y; 247 int (Y::*ptr)() = &Y::operator*; 248 return *y + x; 249 } 250 )cc"; 251 252 StringRef Ref = "ref"; 253 Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref), 254 change(name(Ref), text("good"))), 255 consumer()); 256 T.registerMatchers(&MatchFinder); 257 EXPECT_FALSE(rewrite(Input)); 258 } 259 260 TEST_F(TransformerTest, NodePartMember) { 261 StringRef E = "expr"; 262 RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E), 263 change(member(E), text("good"))); 264 265 std::string Input = R"cc( 266 struct S { 267 int bad; 268 }; 269 int g() { 270 S s; 271 return s.bad; 272 } 273 )cc"; 274 std::string Expected = R"cc( 275 struct S { 276 int bad; 277 }; 278 int g() { 279 S s; 280 return s.good; 281 } 282 )cc"; 283 284 testRule(Rule, Input, Expected); 285 } 286 287 TEST_F(TransformerTest, NodePartMemberQualified) { 288 std::string Input = R"cc( 289 struct S { 290 int bad; 291 int good; 292 }; 293 struct T : public S { 294 int bad; 295 }; 296 int g() { 297 T t; 298 return t.S::bad; 299 } 300 )cc"; 301 std::string Expected = R"cc( 302 struct S { 303 int bad; 304 int good; 305 }; 306 struct T : public S { 307 int bad; 308 }; 309 int g() { 310 T t; 311 return t.S::good; 312 } 313 )cc"; 314 315 StringRef E = "expr"; 316 testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))), 317 Input, Expected); 318 } 319 320 TEST_F(TransformerTest, NodePartMemberMultiToken) { 321 std::string Input = R"cc( 322 struct Y { 323 int operator*(); 324 int good(); 325 template <typename T> void foo(T t); 326 }; 327 int neutral(int x) { 328 Y y; 329 y.template foo<int>(3); 330 return y.operator *(); 331 } 332 )cc"; 333 std::string Expected = R"cc( 334 struct Y { 335 int operator*(); 336 int good(); 337 template <typename T> void foo(T t); 338 }; 339 int neutral(int x) { 340 Y y; 341 y.template good<int>(3); 342 return y.good(); 343 } 344 )cc"; 345 346 StringRef MemExpr = "member"; 347 testRule(makeRule(memberExpr().bind(MemExpr), 348 change(member(MemExpr), text("good"))), 349 Input, Expected); 350 } 351 352 TEST_F(TransformerTest, InsertBeforeEdit) { 353 std::string Input = R"cc( 354 int f() { 355 return 7; 356 } 357 )cc"; 358 std::string Expected = R"cc( 359 int f() { 360 int y = 3; 361 return 7; 362 } 363 )cc"; 364 365 StringRef Ret = "return"; 366 testRule(makeRule(returnStmt().bind(Ret), 367 insertBefore(statement(Ret), text("int y = 3;"))), 368 Input, Expected); 369 } 370 371 TEST_F(TransformerTest, InsertAfterEdit) { 372 std::string Input = R"cc( 373 int f() { 374 int x = 5; 375 return 7; 376 } 377 )cc"; 378 std::string Expected = R"cc( 379 int f() { 380 int x = 5; 381 int y = 3; 382 return 7; 383 } 384 )cc"; 385 386 StringRef Decl = "decl"; 387 testRule(makeRule(declStmt().bind(Decl), 388 insertAfter(statement(Decl), text("int y = 3;"))), 389 Input, Expected); 390 } 391 392 TEST_F(TransformerTest, RemoveEdit) { 393 std::string Input = R"cc( 394 int f() { 395 int x = 5; 396 return 7; 397 } 398 )cc"; 399 std::string Expected = R"cc( 400 int f() { 401 return 7; 402 } 403 )cc"; 404 405 StringRef Decl = "decl"; 406 testRule(makeRule(declStmt().bind(Decl), remove(statement(Decl))), Input, 407 Expected); 408 } 409 410 TEST_F(TransformerTest, MultiChange) { 411 std::string Input = R"cc( 412 void foo() { 413 if (10 > 1.0) 414 log(1) << "oh no!"; 415 else 416 log(0) << "ok"; 417 } 418 )cc"; 419 std::string Expected = R"( 420 void foo() { 421 if (true) { /* then */ } 422 else { /* else */ } 423 } 424 )"; 425 426 StringRef C = "C", T = "T", E = "E"; 427 testRule(makeRule(ifStmt(hasCondition(expr().bind(C)), 428 hasThen(stmt().bind(T)), hasElse(stmt().bind(E))), 429 {change(node(C), text("true")), 430 change(statement(T), text("{ /* then */ }")), 431 change(statement(E), text("{ /* else */ }"))}), 432 Input, Expected); 433 } 434 435 TEST_F(TransformerTest, OrderedRuleUnrelated) { 436 StringRef Flag = "flag"; 437 RewriteRule FlagRule = makeRule( 438 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl( 439 hasName("proto::ProtoCommandLineFlag")))) 440 .bind(Flag)), 441 unless(callee(cxxMethodDecl(hasName("GetProto"))))), 442 change(node(Flag), text("PROTO"))); 443 444 std::string Input = R"cc( 445 proto::ProtoCommandLineFlag flag; 446 int x = flag.foo(); 447 int y = flag.GetProto().foo(); 448 int f(string s) { return strlen(s.c_str()); } 449 )cc"; 450 std::string Expected = R"cc( 451 proto::ProtoCommandLineFlag flag; 452 int x = PROTO.foo(); 453 int y = flag.GetProto().foo(); 454 int f(string s) { return REPLACED; } 455 )cc"; 456 457 testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected); 458 } 459 460 // Version of ruleStrlenSizeAny that inserts a method with a different name than 461 // ruleStrlenSize, so we can tell their effect apart. 462 RewriteRule ruleStrlenSizeDistinct() { 463 StringRef S; 464 return makeRule( 465 callExpr(callee(functionDecl(hasName("strlen"))), 466 hasArgument(0, cxxMemberCallExpr( 467 on(expr().bind(S)), 468 callee(cxxMethodDecl(hasName("c_str")))))), 469 change(text("DISTINCT"))); 470 } 471 472 TEST_F(TransformerTest, OrderedRuleRelated) { 473 std::string Input = R"cc( 474 namespace foo { 475 struct mystring { 476 char* c_str(); 477 }; 478 int f(mystring s) { return strlen(s.c_str()); } 479 } // namespace foo 480 int g(string s) { return strlen(s.c_str()); } 481 )cc"; 482 std::string Expected = R"cc( 483 namespace foo { 484 struct mystring { 485 char* c_str(); 486 }; 487 int f(mystring s) { return DISTINCT; } 488 } // namespace foo 489 int g(string s) { return REPLACED; } 490 )cc"; 491 492 testRule(applyFirst({ruleStrlenSize(), ruleStrlenSizeDistinct()}), Input, 493 Expected); 494 } 495 496 // Change the order of the rules to get a different result. 497 TEST_F(TransformerTest, OrderedRuleRelatedSwapped) { 498 std::string Input = R"cc( 499 namespace foo { 500 struct mystring { 501 char* c_str(); 502 }; 503 int f(mystring s) { return strlen(s.c_str()); } 504 } // namespace foo 505 int g(string s) { return strlen(s.c_str()); } 506 )cc"; 507 std::string Expected = R"cc( 508 namespace foo { 509 struct mystring { 510 char* c_str(); 511 }; 512 int f(mystring s) { return DISTINCT; } 513 } // namespace foo 514 int g(string s) { return DISTINCT; } 515 )cc"; 516 517 testRule(applyFirst({ruleStrlenSizeDistinct(), ruleStrlenSize()}), Input, 518 Expected); 519 } 520 521 // 522 // Negative tests (where we expect no transformation to occur). 523 // 524 525 // Tests for a conflict in edits from a single match for a rule. 526 TEST_F(TransformerTest, TextGeneratorFailure) { 527 std::string Input = "int conflictOneRule() { return 3 + 7; }"; 528 // Try to change the whole binary-operator expression AND one its operands: 529 StringRef O = "O"; 530 auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &) 531 -> llvm::Expected<std::string> { 532 return llvm::createStringError(llvm::errc::invalid_argument, "ERROR"); 533 }; 534 Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)), 535 consumer()); 536 T.registerMatchers(&MatchFinder); 537 EXPECT_FALSE(rewrite(Input)); 538 EXPECT_THAT(Changes, IsEmpty()); 539 EXPECT_EQ(ErrorCount, 1); 540 } 541 542 // Tests for a conflict in edits from a single match for a rule. 543 TEST_F(TransformerTest, OverlappingEditsInRule) { 544 std::string Input = "int conflictOneRule() { return 3 + 7; }"; 545 // Try to change the whole binary-operator expression AND one its operands: 546 StringRef O = "O", L = "L"; 547 Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O), 548 {change(node(O), text("DELETE_OP")), 549 change(node(L), text("DELETE_LHS"))}), 550 consumer()); 551 T.registerMatchers(&MatchFinder); 552 EXPECT_FALSE(rewrite(Input)); 553 EXPECT_THAT(Changes, IsEmpty()); 554 EXPECT_EQ(ErrorCount, 1); 555 } 556 557 // Tests for a conflict in edits across multiple matches (of the same rule). 558 TEST_F(TransformerTest, OverlappingEditsMultipleMatches) { 559 std::string Input = "int conflictOneRule() { return -7; }"; 560 // Try to change the whole binary-operator expression AND one its operands: 561 StringRef E = "E"; 562 Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))), 563 consumer()); 564 T.registerMatchers(&MatchFinder); 565 // The rewrite process fails because the changes conflict with each other... 566 EXPECT_FALSE(rewrite(Input)); 567 // ... but two changes were produced. 568 EXPECT_EQ(Changes.size(), 2u); 569 EXPECT_EQ(ErrorCount, 0); 570 } 571 572 TEST_F(TransformerTest, ErrorOccurredMatchSkipped) { 573 // Syntax error in the function body: 574 std::string Input = "void errorOccurred() { 3 }"; 575 Transformer T(makeRule(functionDecl(hasName("errorOccurred")), 576 change(text("DELETED;"))), 577 consumer()); 578 T.registerMatchers(&MatchFinder); 579 // The rewrite process itself fails... 580 EXPECT_FALSE(rewrite(Input)); 581 // ... and no changes or errors are produced in the process. 582 EXPECT_THAT(Changes, IsEmpty()); 583 EXPECT_EQ(ErrorCount, 0); 584 } 585 586 TEST_F(TransformerTest, NoTransformationInMacro) { 587 std::string Input = R"cc( 588 #define MACRO(str) strlen((str).c_str()) 589 int f(string s) { return MACRO(s); })cc"; 590 testRule(ruleStrlenSize(), Input, Input); 591 } 592 593 // This test handles the corner case where a macro called within another macro 594 // expands to matching code, but the matched code is an argument to the nested 595 // macro. A simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() 596 // will get this wrong, and transform the code. This test verifies that no such 597 // transformation occurs. 598 TEST_F(TransformerTest, NoTransformationInNestedMacro) { 599 std::string Input = R"cc( 600 #define NESTED(e) e 601 #define MACRO(str) NESTED(strlen((str).c_str())) 602 int f(string s) { return MACRO(s); })cc"; 603 testRule(ruleStrlenSize(), Input, Input); 604 } 605 } // namespace 606