blob: 64f511b5fabeab6ea7ce12faa1e5ee1f1f993ac9 [file] [log] [blame]
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +00001//===- 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"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000010#include "clang/ASTMatchers/ASTMatchers.h"
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +000011#include "clang/Tooling/Refactoring/RangeSelector.h"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000012#include "clang/Tooling/Tooling.h"
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000013#include "llvm/Support/Errc.h"
14#include "llvm/Support/Error.h"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000015#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +000018using namespace clang;
19using namespace tooling;
20using namespace ast_matchers;
21
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000022namespace {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000023using ::testing::IsEmpty;
24
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000025constexpr 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
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +000041 class Logger {};
42 void operator<<(Logger& l, string msg);
43 Logger& log(int level);
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000044)cc";
45
46static ast_matchers::internal::Matcher<clang::QualType>
47isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
48 return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
49}
50
51static 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
64static 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).
76class ClangRefactoringTestBase : public testing::Test {
77protected:
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)) {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000091 llvm::errs() << "Running tool failed.\n";
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000092 return None;
93 }
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000094 if (ErrorCount != 0) {
95 llvm::errs() << "Generating changes failed.\n";
96 return None;
97 }
98 auto ChangedCode =
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000099 applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000100 if (!ChangedCode) {
101 llvm::errs() << "Applying changes failed: "
102 << llvm::toString(ChangedCode.takeError()) << "\n";
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000103 return None;
104 }
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000105 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 };
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000117 }
118
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000119 template <typename R>
120 void testRule(R Rule, StringRef Input, StringRef Expected) {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000121 Transformer T(std::move(Rule), consumer());
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000122 T.registerMatchers(&MatchFinder);
123 compareSnippets(Expected, rewrite(Input));
124 }
125
126 clang::ast_matchers::MatchFinder MatchFinder;
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000127 // Records whether any errors occurred in individual changes.
128 int ErrorCount = 0;
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000129 AtomicChanges Changes;
130
131private:
132 FileContentMappings FileContents = {{"header.h", ""}};
133};
134
135class TransformerTest : public ClangRefactoringTestBase {
136protected:
137 TransformerTest() { appendToHeader(KHeaderContents); }
138};
139
140// Given string s, change strlen($s.c_str()) to $s.size().
141static RewriteRule ruleStrlenSize() {
142 StringRef StringExpr = "strexpr";
143 auto StringType = namedDecl(hasAnyName("::basic_string", "::string"));
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000144 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")))))),
Yitzhak Mandelbaumfab72052019-05-24 15:11:45 +0000150 change(text("REPLACED")), text("Use size() method directly on string."));
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000151 return R;
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000152}
153
154TEST_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.
161TEST_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).
167TEST_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.
178TEST_F(TransformerTest, Flag) {
179 StringRef Flag = "flag";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000180 RewriteRule Rule = makeRule(
181 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
182 hasName("proto::ProtoCommandLineFlag"))))
183 .bind(Flag)),
184 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000185 change(node(Flag), text("EXPR")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000186
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
Yitzhak Mandelbaum727bdcb2019-07-02 13:11:04 +0000201TEST_F(TransformerTest, AddIncludeQuoted) {
202 RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
203 change(text("other()")));
204 addInclude(Rule, "clang/OtherLib.h");
205
206 std::string Input = R"cc(
207 int f(int x);
208 int h(int x) { return f(x); }
209 )cc";
210 std::string Expected = R"cc(#include "clang/OtherLib.h"
211
212 int f(int x);
213 int h(int x) { return other(); }
214 )cc";
215
216 testRule(Rule, Input, Expected);
217}
218
219TEST_F(TransformerTest, AddIncludeAngled) {
220 RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
221 change(text("other()")));
222 addInclude(Rule, "clang/OtherLib.h", IncludeFormat::Angled);
223
224 std::string Input = R"cc(
225 int f(int x);
226 int h(int x) { return f(x); }
227 )cc";
228 std::string Expected = R"cc(#include <clang/OtherLib.h>
229
230 int f(int x);
231 int h(int x) { return other(); }
232 )cc";
233
234 testRule(Rule, Input, Expected);
235}
236
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000237TEST_F(TransformerTest, NodePartNameNamedDecl) {
238 StringRef Fun = "fun";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000239 RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun),
240 change(name(Fun), text("good")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000241
242 std::string Input = R"cc(
243 int bad(int x);
244 int bad(int x) { return x * x; }
245 )cc";
246 std::string Expected = R"cc(
247 int good(int x);
248 int good(int x) { return x * x; }
249 )cc";
250
251 testRule(Rule, Input, Expected);
252}
253
254TEST_F(TransformerTest, NodePartNameDeclRef) {
255 std::string Input = R"cc(
256 template <typename T>
257 T bad(T x) {
258 return x;
259 }
260 int neutral(int x) { return bad<int>(x) * x; }
261 )cc";
262 std::string Expected = R"cc(
263 template <typename T>
264 T bad(T x) {
265 return x;
266 }
267 int neutral(int x) { return good<int>(x) * x; }
268 )cc";
269
270 StringRef Ref = "ref";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000271 testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000272 change(name(Ref), text("good"))),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000273 Input, Expected);
274}
275
276TEST_F(TransformerTest, NodePartNameDeclRefFailure) {
277 std::string Input = R"cc(
278 struct Y {
279 int operator*();
280 };
281 int neutral(int x) {
282 Y y;
283 int (Y::*ptr)() = &Y::operator*;
284 return *y + x;
285 }
286 )cc";
287
288 StringRef Ref = "ref";
Yitzhak Mandelbauma5dadbe2019-04-30 17:24:36 +0000289 Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000290 change(name(Ref), text("good"))),
Yitzhak Mandelbauma5dadbe2019-04-30 17:24:36 +0000291 consumer());
292 T.registerMatchers(&MatchFinder);
293 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000294}
295
296TEST_F(TransformerTest, NodePartMember) {
297 StringRef E = "expr";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000298 RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000299 change(member(E), text("good")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000300
301 std::string Input = R"cc(
302 struct S {
303 int bad;
304 };
305 int g() {
306 S s;
307 return s.bad;
308 }
309 )cc";
310 std::string Expected = R"cc(
311 struct S {
312 int bad;
313 };
314 int g() {
315 S s;
316 return s.good;
317 }
318 )cc";
319
320 testRule(Rule, Input, Expected);
321}
322
323TEST_F(TransformerTest, NodePartMemberQualified) {
324 std::string Input = R"cc(
325 struct S {
326 int bad;
327 int good;
328 };
329 struct T : public S {
330 int bad;
331 };
332 int g() {
333 T t;
334 return t.S::bad;
335 }
336 )cc";
337 std::string Expected = R"cc(
338 struct S {
339 int bad;
340 int good;
341 };
342 struct T : public S {
343 int bad;
344 };
345 int g() {
346 T t;
347 return t.S::good;
348 }
349 )cc";
350
351 StringRef E = "expr";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000352 testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000353 Input, Expected);
354}
355
356TEST_F(TransformerTest, NodePartMemberMultiToken) {
357 std::string Input = R"cc(
358 struct Y {
359 int operator*();
360 int good();
361 template <typename T> void foo(T t);
362 };
363 int neutral(int x) {
364 Y y;
365 y.template foo<int>(3);
366 return y.operator *();
367 }
368 )cc";
369 std::string Expected = R"cc(
370 struct Y {
371 int operator*();
372 int good();
373 template <typename T> void foo(T t);
374 };
375 int neutral(int x) {
376 Y y;
377 y.template good<int>(3);
378 return y.good();
379 }
380 )cc";
381
382 StringRef MemExpr = "member";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000383 testRule(makeRule(memberExpr().bind(MemExpr),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000384 change(member(MemExpr), text("good"))),
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000385 Input, Expected);
386}
387
Yitzhak Mandelbaum2e4a6282019-06-06 14:20:29 +0000388TEST_F(TransformerTest, InsertBeforeEdit) {
389 std::string Input = R"cc(
390 int f() {
391 return 7;
392 }
393 )cc";
394 std::string Expected = R"cc(
395 int f() {
396 int y = 3;
397 return 7;
398 }
399 )cc";
400
401 StringRef Ret = "return";
402 testRule(makeRule(returnStmt().bind(Ret),
403 insertBefore(statement(Ret), text("int y = 3;"))),
404 Input, Expected);
405}
406
407TEST_F(TransformerTest, InsertAfterEdit) {
408 std::string Input = R"cc(
409 int f() {
410 int x = 5;
411 return 7;
412 }
413 )cc";
414 std::string Expected = R"cc(
415 int f() {
416 int x = 5;
417 int y = 3;
418 return 7;
419 }
420 )cc";
421
422 StringRef Decl = "decl";
423 testRule(makeRule(declStmt().bind(Decl),
424 insertAfter(statement(Decl), text("int y = 3;"))),
425 Input, Expected);
426}
427
428TEST_F(TransformerTest, RemoveEdit) {
429 std::string Input = R"cc(
430 int f() {
431 int x = 5;
432 return 7;
433 }
434 )cc";
435 std::string Expected = R"cc(
436 int f() {
437 return 7;
438 }
439 )cc";
440
441 StringRef Decl = "decl";
442 testRule(makeRule(declStmt().bind(Decl), remove(statement(Decl))), Input,
443 Expected);
444}
445
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000446TEST_F(TransformerTest, MultiChange) {
447 std::string Input = R"cc(
448 void foo() {
449 if (10 > 1.0)
450 log(1) << "oh no!";
451 else
452 log(0) << "ok";
453 }
454 )cc";
455 std::string Expected = R"(
456 void foo() {
457 if (true) { /* then */ }
458 else { /* else */ }
459 }
460 )";
461
462 StringRef C = "C", T = "T", E = "E";
463 testRule(makeRule(ifStmt(hasCondition(expr().bind(C)),
464 hasThen(stmt().bind(T)), hasElse(stmt().bind(E))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000465 {change(node(C), text("true")),
466 change(statement(T), text("{ /* then */ }")),
467 change(statement(E), text("{ /* else */ }"))}),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000468 Input, Expected);
469}
470
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000471TEST_F(TransformerTest, OrderedRuleUnrelated) {
472 StringRef Flag = "flag";
473 RewriteRule FlagRule = makeRule(
474 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
475 hasName("proto::ProtoCommandLineFlag"))))
476 .bind(Flag)),
477 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000478 change(node(Flag), text("PROTO")));
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000479
480 std::string Input = R"cc(
481 proto::ProtoCommandLineFlag flag;
482 int x = flag.foo();
483 int y = flag.GetProto().foo();
484 int f(string s) { return strlen(s.c_str()); }
485 )cc";
486 std::string Expected = R"cc(
487 proto::ProtoCommandLineFlag flag;
488 int x = PROTO.foo();
489 int y = flag.GetProto().foo();
490 int f(string s) { return REPLACED; }
491 )cc";
492
493 testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);
494}
495
496// Version of ruleStrlenSizeAny that inserts a method with a different name than
497// ruleStrlenSize, so we can tell their effect apart.
498RewriteRule ruleStrlenSizeDistinct() {
499 StringRef S;
500 return makeRule(
501 callExpr(callee(functionDecl(hasName("strlen"))),
502 hasArgument(0, cxxMemberCallExpr(
503 on(expr().bind(S)),
504 callee(cxxMethodDecl(hasName("c_str")))))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000505 change(text("DISTINCT")));
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000506}
507
508TEST_F(TransformerTest, OrderedRuleRelated) {
509 std::string Input = R"cc(
510 namespace foo {
511 struct mystring {
512 char* c_str();
513 };
514 int f(mystring s) { return strlen(s.c_str()); }
515 } // namespace foo
516 int g(string s) { return strlen(s.c_str()); }
517 )cc";
518 std::string Expected = R"cc(
519 namespace foo {
520 struct mystring {
521 char* c_str();
522 };
523 int f(mystring s) { return DISTINCT; }
524 } // namespace foo
525 int g(string s) { return REPLACED; }
526 )cc";
527
528 testRule(applyFirst({ruleStrlenSize(), ruleStrlenSizeDistinct()}), Input,
529 Expected);
530}
531
532// Change the order of the rules to get a different result.
533TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {
534 std::string Input = R"cc(
535 namespace foo {
536 struct mystring {
537 char* c_str();
538 };
539 int f(mystring s) { return strlen(s.c_str()); }
540 } // namespace foo
541 int g(string s) { return strlen(s.c_str()); }
542 )cc";
543 std::string Expected = R"cc(
544 namespace foo {
545 struct mystring {
546 char* c_str();
547 };
548 int f(mystring s) { return DISTINCT; }
549 } // namespace foo
550 int g(string s) { return DISTINCT; }
551 )cc";
552
553 testRule(applyFirst({ruleStrlenSizeDistinct(), ruleStrlenSize()}), Input,
554 Expected);
555}
556
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000557//
558// Negative tests (where we expect no transformation to occur).
559//
560
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000561// Tests for a conflict in edits from a single match for a rule.
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000562TEST_F(TransformerTest, TextGeneratorFailure) {
563 std::string Input = "int conflictOneRule() { return 3 + 7; }";
564 // Try to change the whole binary-operator expression AND one its operands:
565 StringRef O = "O";
566 auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &)
567 -> llvm::Expected<std::string> {
568 return llvm::createStringError(llvm::errc::invalid_argument, "ERROR");
569 };
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000570 Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000571 consumer());
572 T.registerMatchers(&MatchFinder);
573 EXPECT_FALSE(rewrite(Input));
574 EXPECT_THAT(Changes, IsEmpty());
575 EXPECT_EQ(ErrorCount, 1);
576}
577
578// Tests for a conflict in edits from a single match for a rule.
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000579TEST_F(TransformerTest, OverlappingEditsInRule) {
580 std::string Input = "int conflictOneRule() { return 3 + 7; }";
581 // Try to change the whole binary-operator expression AND one its operands:
582 StringRef O = "O", L = "L";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000583 Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O),
584 {change(node(O), text("DELETE_OP")),
585 change(node(L), text("DELETE_LHS"))}),
586 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000587 T.registerMatchers(&MatchFinder);
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000588 EXPECT_FALSE(rewrite(Input));
589 EXPECT_THAT(Changes, IsEmpty());
590 EXPECT_EQ(ErrorCount, 1);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000591}
592
593// Tests for a conflict in edits across multiple matches (of the same rule).
594TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {
595 std::string Input = "int conflictOneRule() { return -7; }";
596 // Try to change the whole binary-operator expression AND one its operands:
597 StringRef E = "E";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000598 Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000599 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000600 T.registerMatchers(&MatchFinder);
601 // The rewrite process fails because the changes conflict with each other...
602 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000603 // ... but two changes were produced.
604 EXPECT_EQ(Changes.size(), 2u);
605 EXPECT_EQ(ErrorCount, 0);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000606}
607
608TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
609 // Syntax error in the function body:
610 std::string Input = "void errorOccurred() { 3 }";
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000611 Transformer T(makeRule(functionDecl(hasName("errorOccurred")),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000612 change(text("DELETED;"))),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000613 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000614 T.registerMatchers(&MatchFinder);
615 // The rewrite process itself fails...
616 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000617 // ... and no changes or errors are produced in the process.
618 EXPECT_THAT(Changes, IsEmpty());
619 EXPECT_EQ(ErrorCount, 0);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000620}
621
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000622TEST_F(TransformerTest, NoTransformationInMacro) {
623 std::string Input = R"cc(
624#define MACRO(str) strlen((str).c_str())
625 int f(string s) { return MACRO(s); })cc";
626 testRule(ruleStrlenSize(), Input, Input);
627}
628
629// This test handles the corner case where a macro called within another macro
630// expands to matching code, but the matched code is an argument to the nested
631// macro. A simple check of isMacroArgExpansion() vs. isMacroBodyExpansion()
632// will get this wrong, and transform the code. This test verifies that no such
633// transformation occurs.
634TEST_F(TransformerTest, NoTransformationInNestedMacro) {
635 std::string Input = R"cc(
636#define NESTED(e) e
637#define MACRO(str) NESTED(strlen((str).c_str()))
638 int f(string s) { return MACRO(s); })cc";
639 testRule(ruleStrlenSize(), Input, Input);
640}
641} // namespace