blob: 5dd5c02c5778bdc6be1bad7d4ac8fb0ef96f8766 [file] [log] [blame]
Manuel Klimek3f001342012-05-23 16:29:20 +00001//===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Eric Liu40ef2fb2016-08-01 10:16:37 +000010#include "ReplacementTest.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000011#include "RewriterTestContext.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000012#include "clang/AST/ASTConsumer.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000013#include "clang/AST/ASTContext.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000014#include "clang/AST/DeclCXX.h"
15#include "clang/AST/DeclGroup.h"
16#include "clang/AST/RecursiveASTVisitor.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000017#include "clang/Basic/Diagnostic.h"
Douglas Gregor275e8832012-10-23 22:55:10 +000018#include "clang/Basic/DiagnosticOptions.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000019#include "clang/Basic/FileManager.h"
20#include "clang/Basic/LangOptions.h"
21#include "clang/Basic/SourceManager.h"
Eric Liu4c1ef97a2016-03-29 16:31:53 +000022#include "clang/Format/Format.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000023#include "clang/Frontend/CompilerInstance.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000024#include "clang/Frontend/FrontendAction.h"
25#include "clang/Frontend/TextDiagnosticPrinter.h"
Ted Kremenekcdf81492012-09-01 05:09:24 +000026#include "clang/Rewrite/Core/Rewriter.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000027#include "clang/Tooling/Refactoring.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000028#include "clang/Tooling/Tooling.h"
29#include "llvm/ADT/SmallString.h"
Manuel Klimek3f001342012-05-23 16:29:20 +000030#include "gtest/gtest.h"
31
32namespace clang {
33namespace tooling {
34
Manuel Klimek3f001342012-05-23 16:29:20 +000035TEST_F(ReplacementTest, CanDeleteAllText) {
36 FileID ID = Context.createInMemoryFile("input.cpp", "text");
37 SourceLocation Location = Context.getLocation(ID, 1, 1);
38 Replacement Replace(createReplacement(Location, 4, ""));
39 EXPECT_TRUE(Replace.apply(Context.Rewrite));
40 EXPECT_EQ("", Context.getRewrittenText(ID));
41}
42
43TEST_F(ReplacementTest, CanDeleteAllTextInTextWithNewlines) {
44 FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3");
45 SourceLocation Location = Context.getLocation(ID, 1, 1);
46 Replacement Replace(createReplacement(Location, 17, ""));
47 EXPECT_TRUE(Replace.apply(Context.Rewrite));
48 EXPECT_EQ("", Context.getRewrittenText(ID));
49}
50
51TEST_F(ReplacementTest, CanAddText) {
52 FileID ID = Context.createInMemoryFile("input.cpp", "");
53 SourceLocation Location = Context.getLocation(ID, 1, 1);
54 Replacement Replace(createReplacement(Location, 0, "result"));
55 EXPECT_TRUE(Replace.apply(Context.Rewrite));
56 EXPECT_EQ("result", Context.getRewrittenText(ID));
57}
58
59TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
60 FileID ID = Context.createInMemoryFile("input.cpp",
61 "line1\nline2\nline3\nline4");
62 SourceLocation Location = Context.getLocation(ID, 2, 3);
63 Replacement Replace(createReplacement(Location, 12, "x"));
64 EXPECT_TRUE(Replace.apply(Context.Rewrite));
65 EXPECT_EQ("line1\nlixne4", Context.getRewrittenText(ID));
66}
67
68TEST_F(ReplacementTest, CanReplaceTextAtPositionMultipleTimes) {
69 FileID ID = Context.createInMemoryFile("input.cpp",
70 "line1\nline2\nline3\nline4");
71 SourceLocation Location1 = Context.getLocation(ID, 2, 3);
72 Replacement Replace1(createReplacement(Location1, 12, "x\ny\n"));
73 EXPECT_TRUE(Replace1.apply(Context.Rewrite));
74 EXPECT_EQ("line1\nlix\ny\nne4", Context.getRewrittenText(ID));
75
76 // Since the original source has not been modified, the (4, 4) points to the
77 // 'e' in the original content.
78 SourceLocation Location2 = Context.getLocation(ID, 4, 4);
79 Replacement Replace2(createReplacement(Location2, 1, "f"));
80 EXPECT_TRUE(Replace2.apply(Context.Rewrite));
81 EXPECT_EQ("line1\nlix\ny\nnf4", Context.getRewrittenText(ID));
82}
83
84TEST_F(ReplacementTest, ApplyFailsForNonExistentLocation) {
85 Replacement Replace("nonexistent-file.cpp", 0, 1, "");
86 EXPECT_FALSE(Replace.apply(Context.Rewrite));
87}
88
89TEST_F(ReplacementTest, CanRetrivePath) {
90 Replacement Replace("/path/to/file.cpp", 0, 1, "");
91 EXPECT_EQ("/path/to/file.cpp", Replace.getFilePath());
92}
93
94TEST_F(ReplacementTest, ReturnsInvalidPath) {
95 Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
96 EXPECT_TRUE(Replace1.getFilePath().empty());
97
98 Replacement Replace2;
99 EXPECT_TRUE(Replace2.getFilePath().empty());
100}
101
Eric Liu40ef2fb2016-08-01 10:16:37 +0000102TEST_F(ReplacementTest, FailAddReplacements) {
103 Replacements Replaces;
104 auto Err = Replaces.add(Replacement("x.cc", 0, 10, "3"));
105 EXPECT_TRUE(!Err);
106 llvm::consumeError(std::move(Err));
107 Err = Replaces.add(Replacement("x.cc", 0, 2, ""));
108 EXPECT_TRUE((bool)Err);
109 llvm::consumeError(std::move(Err));
110 Err = Replaces.add(Replacement("x.cc", 2, 2, ""));
111 EXPECT_TRUE((bool)Err);
112 llvm::consumeError(std::move(Err));
113 Err = Replaces.add(Replacement("y.cc", 20, 2, ""));
114 EXPECT_TRUE((bool)Err);
115 llvm::consumeError(std::move(Err));
116}
117
Manuel Klimekdcb910b2016-08-03 14:12:17 +0000118TEST_F(ReplacementTest, FailAddOverlappingInsertions) {
119 Replacements Replaces;
120 // Test adding an insertion at the offset of an existing replacement.
121 auto Err = Replaces.add(Replacement("x.cc", 10, 3, "replace"));
122 EXPECT_TRUE(!Err);
123 llvm::consumeError(std::move(Err));
124 Err = Replaces.add(Replacement("x.cc", 10, 0, "insert"));
125 EXPECT_TRUE((bool)Err);
126 llvm::consumeError(std::move(Err));
127
128 Replaces.clear();
129 // Test overlap with an existing insertion.
130 Err = Replaces.add(Replacement("x.cc", 10, 0, "insert"));
131 EXPECT_TRUE(!Err);
132 llvm::consumeError(std::move(Err));
133 Err = Replaces.add(Replacement("x.cc", 10, 3, "replace"));
134 EXPECT_TRUE((bool)Err);
135 llvm::consumeError(std::move(Err));
136}
137
138TEST_F(ReplacementTest, FailAddRegression) {
139 Replacements Replaces;
140 // Create two replacements, where the second one is an insertion of the empty
141 // string exactly at the end of the first one.
142 auto Err = Replaces.add(Replacement("x.cc", 0, 10, "1"));
143 EXPECT_TRUE(!Err);
144 llvm::consumeError(std::move(Err));
145 Err = Replaces.add(Replacement("x.cc", 10, 0, ""));
146 EXPECT_TRUE(!Err);
147 llvm::consumeError(std::move(Err));
148
149 // Make sure we find the overlap with the first entry when inserting a
150 // replacement that ends exactly at the seam of the existing replacements.
151 Err = Replaces.add(Replacement("x.cc", 5, 5, "fail"));
152 EXPECT_TRUE((bool)Err);
153 llvm::consumeError(std::move(Err));
154
155 Err = Replaces.add(Replacement("x.cc", 10, 0, ""));
156 EXPECT_TRUE((bool)Err);
157 llvm::consumeError(std::move(Err));
158}
159
Manuel Klimek16c6d0a2016-08-03 15:12:00 +0000160TEST_F(ReplacementTest, FailAddInsertAtOffsetOfReplacement) {
161 Replacements Replaces;
162 auto Err = Replaces.add(Replacement("x.cc", 10, 2, ""));
163 EXPECT_TRUE(!Err);
164 llvm::consumeError(std::move(Err));
165 Err = Replaces.add(Replacement("x.cc", 10, 0, ""));
166 EXPECT_TRUE((bool)Err);
167 llvm::consumeError(std::move(Err));
168}
169
170TEST_F(ReplacementTest, FailAddInsertAtOtherInsert) {
171 Replacements Replaces;
172 auto Err = Replaces.add(Replacement("x.cc", 10, 0, "a"));
173 EXPECT_TRUE(!Err);
174 llvm::consumeError(std::move(Err));
175 Err = Replaces.add(Replacement("x.cc", 10, 0, "b"));
176 EXPECT_TRUE((bool)Err);
177 llvm::consumeError(std::move(Err));
178}
179
Manuel Klimek3f001342012-05-23 16:29:20 +0000180TEST_F(ReplacementTest, CanApplyReplacements) {
181 FileID ID = Context.createInMemoryFile("input.cpp",
182 "line1\nline2\nline3\nline4");
Eric Liu40ef2fb2016-08-01 10:16:37 +0000183 Replacements Replaces =
184 toReplacements({Replacement(Context.Sources,
185 Context.getLocation(ID, 2, 1), 5, "replaced"),
186 Replacement(Context.Sources,
187 Context.getLocation(ID, 3, 1), 5, "other")});
Edwin Vane349e1c12013-08-13 17:38:19 +0000188 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
189 EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
190}
191
Manuel Klimek3f001342012-05-23 16:29:20 +0000192TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
193 FileID ID = Context.createInMemoryFile("input.cpp",
194 "line1\nline2\nline3\nline4");
Eric Liu40ef2fb2016-08-01 10:16:37 +0000195 auto Replaces = toReplacements({Replacement(
196 Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
197
198 auto Err = Replaces.add(Replacement(
199 Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced"));
200 EXPECT_TRUE((bool)Err);
201 llvm::consumeError(std::move(Err));
202
203 Err = Replaces.add(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
204 5, "replaced"));
205 EXPECT_TRUE((bool)Err);
206 llvm::consumeError(std::move(Err));
207
Manuel Klimek3f001342012-05-23 16:29:20 +0000208 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
209 EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
210}
211
Eric Liu40ef2fb2016-08-01 10:16:37 +0000212TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) {
213 Replacements Replaces =
214 toReplacements({Replacement(Context.Sources, SourceLocation(), 5, "2")});
215
Manuel Klimek3f001342012-05-23 16:29:20 +0000216 EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
Manuel Klimek3f001342012-05-23 16:29:20 +0000217}
218
Eric Liu4c1ef97a2016-03-29 16:31:53 +0000219TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
220 // Column limit is 20.
221 std::string Code1 = "Long *a =\n"
222 " new Long();\n"
223 "long x = 1;";
224 std::string Expected1 = "auto a = new Long();\n"
225 "long x =\n"
226 " 12345678901;";
227 std::string Code2 = "int x = 123;\n"
228 "int y = 0;";
229 std::string Expected2 = "int x =\n"
230 " 1234567890123;\n"
231 "int y = 10;";
Eric Liu40ef2fb2016-08-01 10:16:37 +0000232 StringRef File1 = "format_1.cpp";
233 StringRef File2 = "format_2.cpp";
234 FileID ID1 = Context.createInMemoryFile(File1, Code1);
235 FileID ID2 = Context.createInMemoryFile(File2, Code2);
Eric Liu4c1ef97a2016-03-29 16:31:53 +0000236
Eric Liu4c1ef97a2016-03-29 16:31:53 +0000237 // Scrambled the order of replacements.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000238 std::map<std::string, Replacements> FileToReplaces;
239 FileToReplaces[File1] = toReplacements(
240 {tooling::Replacement(Context.Sources, Context.getLocation(ID1, 1, 1), 6,
241 "auto "),
242 tooling::Replacement(Context.Sources, Context.getLocation(ID1, 3, 10), 1,
243 "12345678901")});
244 FileToReplaces[File2] = toReplacements(
245 {tooling::Replacement(Context.Sources, Context.getLocation(ID2, 1, 12), 0,
246 "4567890123"),
247 tooling::Replacement(Context.Sources, Context.getLocation(ID2, 2, 9), 1,
248 "10")});
249 EXPECT_TRUE(
250 formatAndApplyAllReplacements(FileToReplaces, Context.Rewrite,
251 "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
Eric Liu4c1ef97a2016-03-29 16:31:53 +0000252 EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
253 EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
254}
255
Daniel Jasper2a250b82013-05-21 12:21:39 +0000256TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000257 Replacements Replaces =
258 toReplacements({Replacement("", 0, 1, ""), Replacement("", 4, 3, " ")});
Daniel Jasper2a250b82013-05-21 12:21:39 +0000259 // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000260 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int i;
261 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); // |nt i;
262 EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); // i|t i;
263 EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); // in| i;
264 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); // int| i;
265 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); // int | i;
266 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); // int |i;
267 EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); // int |;
268 EXPECT_EQ(5u, Replaces.getShiftedCodePosition(8)); // int i|
Edwin Vane18e503c2013-08-27 15:44:26 +0000269}
270
Daniel Jasper2a250b82013-05-21 12:21:39 +0000271TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000272 Replacements Replaces = toReplacements({Replacement("", 4, 0, "\"\n\"")});
Daniel Jasper2a250b82013-05-21 12:21:39 +0000273 // Assume '"12345678"' is turned into '"1234"\n"5678"'.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000274 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678"
275 EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678"
276 EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78"
Daniel Jasper3fed9452015-11-23 08:33:48 +0000277}
278
279TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) {
Daniel Jasper3fed9452015-11-23 08:33:48 +0000280 // Replace the first four characters with "abcd".
Eric Liu40ef2fb2016-08-01 10:16:37 +0000281 auto Replaces = toReplacements({Replacement("", 0, 4, "abcd")});
Daniel Jasper3fed9452015-11-23 08:33:48 +0000282 for (unsigned i = 0; i < 3; ++i)
Eric Liu40ef2fb2016-08-01 10:16:37 +0000283 EXPECT_EQ(i, Replaces.getShiftedCodePosition(i));
284}
285
286TEST(ShiftedCodePositionTest, NoReplacementText) {
287 Replacements Replaces = toReplacements({Replacement("", 0, 42, "")});
288 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0));
289 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(39));
290 EXPECT_EQ(3u, Replaces.getShiftedCodePosition(45));
291 EXPECT_EQ(0u, Replaces.getShiftedCodePosition(42));
Daniel Jasper2a250b82013-05-21 12:21:39 +0000292}
293
Manuel Klimek3f001342012-05-23 16:29:20 +0000294class FlushRewrittenFilesTest : public ::testing::Test {
Rafael Espindola641c6a12013-06-26 15:01:50 +0000295public:
296 FlushRewrittenFilesTest() {}
Manuel Klimek3f001342012-05-23 16:29:20 +0000297
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000298 ~FlushRewrittenFilesTest() override {
Rafael Espindola641c6a12013-06-26 15:01:50 +0000299 for (llvm::StringMap<std::string>::iterator I = TemporaryFiles.begin(),
300 E = TemporaryFiles.end();
301 I != E; ++I) {
302 llvm::StringRef Name = I->second;
Rafael Espindolac0809172014-06-12 14:02:15 +0000303 std::error_code EC = llvm::sys::fs::remove(Name);
Rafael Espindola641c6a12013-06-26 15:01:50 +0000304 (void)EC;
305 assert(!EC);
306 }
Manuel Klimek3f001342012-05-23 16:29:20 +0000307 }
308
309 FileID createFile(llvm::StringRef Name, llvm::StringRef Content) {
Rafael Espindola641c6a12013-06-26 15:01:50 +0000310 SmallString<1024> Path;
311 int FD;
Rafael Espindolac0809172014-06-12 14:02:15 +0000312 std::error_code EC = llvm::sys::fs::createTemporaryFile(Name, "", FD, Path);
Rafael Espindola641c6a12013-06-26 15:01:50 +0000313 assert(!EC);
314 (void)EC;
315
316 llvm::raw_fd_ostream OutStream(FD, true);
Manuel Klimek3f001342012-05-23 16:29:20 +0000317 OutStream << Content;
318 OutStream.close();
319 const FileEntry *File = Context.Files.getFile(Path);
Craig Topper416fa342014-06-08 08:38:12 +0000320 assert(File != nullptr);
Rafael Espindola641c6a12013-06-26 15:01:50 +0000321
David Blaikie13156b62014-11-19 03:06:06 +0000322 StringRef Found =
323 TemporaryFiles.insert(std::make_pair(Name, Path.str())).first->second;
Rafael Espindola641c6a12013-06-26 15:01:50 +0000324 assert(Found == Path);
325 (void)Found;
Manuel Klimek3f001342012-05-23 16:29:20 +0000326 return Context.Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
327 }
328
329 std::string getFileContentFromDisk(llvm::StringRef Name) {
Rafael Espindola641c6a12013-06-26 15:01:50 +0000330 std::string Path = TemporaryFiles.lookup(Name);
331 assert(!Path.empty());
Manuel Klimek3f001342012-05-23 16:29:20 +0000332 // We need to read directly from the FileManager without relaying through
333 // a FileEntry, as otherwise we'd read through an already opened file
334 // descriptor, which might not see the changes made.
335 // FIXME: Figure out whether there is a way to get the SourceManger to
336 // reopen the file.
Benjamin Kramera8857962014-10-26 22:44:13 +0000337 auto FileBuffer = Context.Files.getBufferForFile(Path);
338 return (*FileBuffer)->getBuffer();
Manuel Klimek3f001342012-05-23 16:29:20 +0000339 }
340
Rafael Espindola641c6a12013-06-26 15:01:50 +0000341 llvm::StringMap<std::string> TemporaryFiles;
Manuel Klimek3f001342012-05-23 16:29:20 +0000342 RewriterTestContext Context;
343};
344
345TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
346 FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
Eric Liu40ef2fb2016-08-01 10:16:37 +0000347 Replacements Replaces = toReplacements({Replacement(
348 Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")});
Manuel Klimek3f001342012-05-23 16:29:20 +0000349 EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
350 EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
351 EXPECT_EQ("line1\nreplaced\nline3\nline4",
352 getFileContentFromDisk("input.cpp"));
353}
354
355namespace {
356template <typename T>
357class TestVisitor : public clang::RecursiveASTVisitor<T> {
358public:
359 bool runOver(StringRef Code) {
360 return runToolOnCode(new TestAction(this), Code);
361 }
362
363protected:
364 clang::SourceManager *SM;
Manuel Klimek94a89232015-06-03 13:10:41 +0000365 clang::ASTContext *Context;
Manuel Klimek3f001342012-05-23 16:29:20 +0000366
367private:
368 class FindConsumer : public clang::ASTConsumer {
369 public:
370 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
371
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000372 void HandleTranslationUnit(clang::ASTContext &Context) override {
Manuel Klimek3f001342012-05-23 16:29:20 +0000373 Visitor->TraverseDecl(Context.getTranslationUnitDecl());
374 }
375
376 private:
377 TestVisitor *Visitor;
378 };
379
380 class TestAction : public clang::ASTFrontendAction {
381 public:
382 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
383
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000384 std::unique_ptr<clang::ASTConsumer>
David Blaikie6beb6aa2014-08-10 19:56:51 +0000385 CreateASTConsumer(clang::CompilerInstance &compiler,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000386 llvm::StringRef dummy) override {
Manuel Klimek3f001342012-05-23 16:29:20 +0000387 Visitor->SM = &compiler.getSourceManager();
Manuel Klimek94a89232015-06-03 13:10:41 +0000388 Visitor->Context = &compiler.getASTContext();
Manuel Klimek3f001342012-05-23 16:29:20 +0000389 /// TestConsumer will be deleted by the framework calling us.
David Blaikie6beb6aa2014-08-10 19:56:51 +0000390 return llvm::make_unique<FindConsumer>(Visitor);
Manuel Klimek3f001342012-05-23 16:29:20 +0000391 }
392
393 private:
394 TestVisitor *Visitor;
395 };
396};
397} // end namespace
398
399void expectReplacementAt(const Replacement &Replace,
400 StringRef File, unsigned Offset, unsigned Length) {
401 ASSERT_TRUE(Replace.isApplicable());
402 EXPECT_EQ(File, Replace.getFilePath());
403 EXPECT_EQ(Offset, Replace.getOffset());
404 EXPECT_EQ(Length, Replace.getLength());
405}
406
407class ClassDeclXVisitor : public TestVisitor<ClassDeclXVisitor> {
408public:
409 bool VisitCXXRecordDecl(CXXRecordDecl *Record) {
410 if (Record->getName() == "X") {
411 Replace = Replacement(*SM, Record, "");
412 }
413 return true;
414 }
415 Replacement Replace;
416};
417
418TEST(Replacement, CanBeConstructedFromNode) {
419 ClassDeclXVisitor ClassDeclX;
420 EXPECT_TRUE(ClassDeclX.runOver(" class X;"));
421 expectReplacementAt(ClassDeclX.Replace, "input.cc", 5, 7);
422}
423
424TEST(Replacement, ReplacesAtSpellingLocation) {
425 ClassDeclXVisitor ClassDeclX;
426 EXPECT_TRUE(ClassDeclX.runOver("#define A(Y) Y\nA(class X);"));
427 expectReplacementAt(ClassDeclX.Replace, "input.cc", 17, 7);
428}
429
430class CallToFVisitor : public TestVisitor<CallToFVisitor> {
431public:
432 bool VisitCallExpr(CallExpr *Call) {
433 if (Call->getDirectCallee()->getName() == "F") {
434 Replace = Replacement(*SM, Call, "");
435 }
436 return true;
437 }
438 Replacement Replace;
439};
440
441TEST(Replacement, FunctionCall) {
442 CallToFVisitor CallToF;
443 EXPECT_TRUE(CallToF.runOver("void F(); void G() { F(); }"));
444 expectReplacementAt(CallToF.Replace, "input.cc", 21, 3);
445}
446
447TEST(Replacement, TemplatedFunctionCall) {
448 CallToFVisitor CallToF;
449 EXPECT_TRUE(CallToF.runOver(
450 "template <typename T> void F(); void G() { F<int>(); }"));
451 expectReplacementAt(CallToF.Replace, "input.cc", 43, 8);
452}
453
Manuel Klimek94a89232015-06-03 13:10:41 +0000454class NestedNameSpecifierAVisitor
455 : public TestVisitor<NestedNameSpecifierAVisitor> {
456public:
457 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLoc) {
458 if (NNSLoc.getNestedNameSpecifier()) {
459 if (const NamespaceDecl* NS = NNSLoc.getNestedNameSpecifier()->getAsNamespace()) {
460 if (NS->getName() == "a") {
461 Replace = Replacement(*SM, &NNSLoc, "", Context->getLangOpts());
462 }
463 }
464 }
465 return TestVisitor<NestedNameSpecifierAVisitor>::TraverseNestedNameSpecifierLoc(
466 NNSLoc);
467 }
468 Replacement Replace;
469};
470
471TEST(Replacement, ColonColon) {
472 NestedNameSpecifierAVisitor VisitNNSA;
473 EXPECT_TRUE(VisitNNSA.runOver("namespace a { void f() { ::a::f(); } }"));
474 expectReplacementAt(VisitNNSA.Replace, "input.cc", 25, 5);
475}
476
Manuel Klimekdce23472013-07-19 12:12:36 +0000477TEST(Range, overlaps) {
478 EXPECT_TRUE(Range(10, 10).overlapsWith(Range(0, 11)));
479 EXPECT_TRUE(Range(0, 11).overlapsWith(Range(10, 10)));
480 EXPECT_FALSE(Range(10, 10).overlapsWith(Range(0, 10)));
481 EXPECT_FALSE(Range(0, 10).overlapsWith(Range(10, 10)));
482 EXPECT_TRUE(Range(0, 10).overlapsWith(Range(2, 6)));
Edwin Vanec5148482013-08-13 18:11:16 +0000483 EXPECT_TRUE(Range(2, 6).overlapsWith(Range(0, 10)));
Manuel Klimekdce23472013-07-19 12:12:36 +0000484}
485
486TEST(Range, contains) {
487 EXPECT_TRUE(Range(0, 10).contains(Range(0, 10)));
488 EXPECT_TRUE(Range(0, 10).contains(Range(2, 6)));
489 EXPECT_FALSE(Range(2, 6).contains(Range(0, 10)));
490 EXPECT_FALSE(Range(0, 10).contains(Range(0, 11)));
491}
492
Manuel Klimekb12e5a52016-03-01 12:37:30 +0000493TEST(Range, CalculateRangesOfReplacements) {
494 // Before: aaaabbbbbbz
495 // After : bbbbbbzzzzzzoooooooooooooooo
Eric Liu40ef2fb2016-08-01 10:16:37 +0000496 Replacements Replaces = toReplacements(
497 {Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"),
498 Replacement("foo", 11, 0, "oooooooooooooooo")});
Manuel Klimekb12e5a52016-03-01 12:37:30 +0000499
Eric Liu40ef2fb2016-08-01 10:16:37 +0000500 std::vector<Range> Ranges = Replaces.getAffectedRanges();
Manuel Klimekb12e5a52016-03-01 12:37:30 +0000501
Eric Liu8b636db2016-06-21 17:56:31 +0000502 EXPECT_EQ(2ul, Ranges.size());
Manuel Klimekb12e5a52016-03-01 12:37:30 +0000503 EXPECT_TRUE(Ranges[0].getOffset() == 0);
504 EXPECT_TRUE(Ranges[0].getLength() == 0);
505 EXPECT_TRUE(Ranges[1].getOffset() == 6);
Eric Liu8b636db2016-06-21 17:56:31 +0000506 EXPECT_TRUE(Ranges[1].getLength() == 22);
507}
508
509TEST(Range, RangesAfterReplacements) {
510 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000511 Replacements Replaces = toReplacements({Replacement("foo", 0, 2, "1234")});
Eric Liu8b636db2016-06-21 17:56:31 +0000512 std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
513 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
514}
515
516TEST(Range, RangesBeforeReplacements) {
517 std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000518 Replacements Replaces = toReplacements({Replacement("foo", 20, 2, "1234")});
Eric Liu8b636db2016-06-21 17:56:31 +0000519 std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
520 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
521}
522
523TEST(Range, NotAffectedByReplacements) {
524 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000525 Replacements Replaces = toReplacements({Replacement("foo", 3, 2, "12"),
526 Replacement("foo", 12, 2, "12"),
527 Replacement("foo", 20, 5, "")});
Eric Liu8b636db2016-06-21 17:56:31 +0000528 std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
529 Range(20, 0)};
530 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
531}
532
533TEST(Range, RangesWithNonOverlappingReplacements) {
534 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000535 Replacements Replaces = toReplacements({Replacement("foo", 3, 1, ""),
536 Replacement("foo", 6, 1, "123"),
537 Replacement("foo", 20, 2, "12345")});
Eric Liu8b636db2016-06-21 17:56:31 +0000538 std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
539 Range(11, 5), Range(21, 5)};
540 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
541}
542
543TEST(Range, RangesWithOverlappingReplacements) {
544 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
545 Range(30, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000546 Replacements Replaces = toReplacements(
547 {Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
548 Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")});
Eric Liu8b636db2016-06-21 17:56:31 +0000549 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
550 Range(22, 0)};
551 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
552}
553
554TEST(Range, MergeIntoOneRange) {
555 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000556 Replacements Replaces =
557 toReplacements({Replacement("foo", 1, 15, "1234567890")});
Eric Liu8b636db2016-06-21 17:56:31 +0000558 std::vector<Range> Expected = {Range(0, 15)};
559 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
560}
561
562TEST(Range, ReplacementsStartingAtRangeOffsets) {
563 std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000564 Replacements Replaces = toReplacements(
565 {Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
566 Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")});
Eric Liu8b636db2016-06-21 17:56:31 +0000567 std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
568 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
569}
570
571TEST(Range, ReplacementsEndingAtRangeEnds) {
572 std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000573 Replacements Replaces = toReplacements(
574 {Replacement("foo", 6, 1, "123"), Replacement("foo", 17, 3, "12")});
Eric Liu8b636db2016-06-21 17:56:31 +0000575 std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
576 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
577}
578
579TEST(Range, AjacentReplacements) {
580 std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000581 Replacements Replaces = toReplacements(
582 {Replacement("foo", 1, 2, "123"), Replacement("foo", 12, 3, "1234")});
Eric Liu8b636db2016-06-21 17:56:31 +0000583 std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
584 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
585}
586
587TEST(Range, MergeRangesAfterReplacements) {
588 std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
Eric Liu40ef2fb2016-08-01 10:16:37 +0000589 Replacements Replaces = toReplacements({Replacement("foo", 1, 3, ""),
590 Replacement("foo", 7, 0, "12"),
591 Replacement("foo", 9, 2, "")});
592 std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0),
593 Range(8, 0)};
Eric Liu8b636db2016-06-21 17:56:31 +0000594 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
Manuel Klimekb12e5a52016-03-01 12:37:30 +0000595}
596
Eric Liu40ef2fb2016-08-01 10:16:37 +0000597TEST(Range, ConflictingRangesBeforeReplacements) {
598 std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)};
599 Replacements Replaces = toReplacements({Replacement("foo", 1, 3, "")});
600 std::vector<Range> Expected = {Range(1, 0), Range(2, 6)};
601 EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
Edwin Vane938f6882013-08-08 13:31:14 +0000602}
603
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000604class MergeReplacementsTest : public ::testing::Test {
605protected:
606 void mergeAndTestRewrite(StringRef Code, StringRef Intermediate,
607 StringRef Result, const Replacements &First,
608 const Replacements &Second) {
609 // These are mainly to verify the test itself and make it easier to read.
Eric Liu4f8d9942016-07-11 13:53:12 +0000610 auto AfterFirst = applyAllReplacements(Code, First);
611 EXPECT_TRUE(static_cast<bool>(AfterFirst));
612 auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
613 EXPECT_TRUE(static_cast<bool>(InSequenceRewrite));
614 EXPECT_EQ(Intermediate, *AfterFirst);
615 EXPECT_EQ(Result, *InSequenceRewrite);
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000616
Eric Liu40ef2fb2016-08-01 10:16:37 +0000617 tooling::Replacements Merged = First.merge(Second);
Eric Liu4f8d9942016-07-11 13:53:12 +0000618 auto MergedRewrite = applyAllReplacements(Code, Merged);
619 EXPECT_TRUE(static_cast<bool>(MergedRewrite));
620 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
621 if (*InSequenceRewrite != *MergedRewrite)
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000622 for (tooling::Replacement M : Merged)
623 llvm::errs() << M.getOffset() << " " << M.getLength() << " "
624 << M.getReplacementText() << "\n";
625 }
626 void mergeAndTestRewrite(StringRef Code, const Replacements &First,
627 const Replacements &Second) {
Eric Liu4f8d9942016-07-11 13:53:12 +0000628 auto AfterFirst = applyAllReplacements(Code, First);
629 EXPECT_TRUE(static_cast<bool>(AfterFirst));
630 auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
Eric Liu40ef2fb2016-08-01 10:16:37 +0000631 tooling::Replacements Merged = First.merge(Second);
Eric Liu4f8d9942016-07-11 13:53:12 +0000632 auto MergedRewrite = applyAllReplacements(Code, Merged);
633 EXPECT_TRUE(static_cast<bool>(MergedRewrite));
634 EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
635 if (*InSequenceRewrite != *MergedRewrite)
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000636 for (tooling::Replacement M : Merged)
637 llvm::errs() << M.getOffset() << " " << M.getLength() << " "
638 << M.getReplacementText() << "\n";
639 }
640};
641
642TEST_F(MergeReplacementsTest, Offsets) {
643 mergeAndTestRewrite("aaa", "aabab", "cacabab",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000644 toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
645 toReplacements({{"", 0, 0, "c"}, {"", 1, 0, "c"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000646 mergeAndTestRewrite("aaa", "babaa", "babacac",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000647 toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "b"}}),
648 toReplacements({{"", 4, 0, "c"}, {"", 5, 0, "c"}}));
649 mergeAndTestRewrite("aaaa", "aaa", "aac", toReplacements({{"", 1, 1, ""}}),
650 toReplacements({{"", 2, 1, "c"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000651
652 mergeAndTestRewrite("aa", "bbabba", "bbabcba",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000653 toReplacements({{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}),
654 toReplacements({{"", 4, 0, "c"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000655}
656
657TEST_F(MergeReplacementsTest, Concatenations) {
658 // Basic concatenations. It is important to merge these into a single
659 // replacement to ensure the correct order.
Eric Liu40ef2fb2016-08-01 10:16:37 +0000660 {
661 auto First = toReplacements({{"", 0, 0, "a"}});
662 auto Second = toReplacements({{"", 1, 0, "b"}});
663 EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second));
664 }
665 {
666 auto First = toReplacements({{"", 0, 0, "a"}});
667 auto Second = toReplacements({{"", 0, 0, "b"}});
668 EXPECT_EQ(toReplacements({{"", 0, 0, "ba"}}), First.merge(Second));
669 }
670 mergeAndTestRewrite("", "a", "ab", toReplacements({{"", 0, 0, "a"}}),
671 toReplacements({{"", 1, 0, "b"}}));
672 mergeAndTestRewrite("", "a", "ba", toReplacements({{"", 0, 0, "a"}}),
673 toReplacements({{"", 0, 0, "b"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000674}
675
676TEST_F(MergeReplacementsTest, NotChangingLengths) {
Eric Liu40ef2fb2016-08-01 10:16:37 +0000677 mergeAndTestRewrite("aaaa", "abba", "acca",
678 toReplacements({{"", 1, 2, "bb"}}),
679 toReplacements({{"", 1, 2, "cc"}}));
680 mergeAndTestRewrite("aaaa", "abba", "abcc",
681 toReplacements({{"", 1, 2, "bb"}}),
682 toReplacements({{"", 2, 2, "cc"}}));
683 mergeAndTestRewrite("aaaa", "abba", "ccba",
684 toReplacements({{"", 1, 2, "bb"}}),
685 toReplacements({{"", 0, 2, "cc"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000686 mergeAndTestRewrite("aaaaaa", "abbdda", "abccda",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000687 toReplacements({{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}),
688 toReplacements({{"", 2, 2, "cc"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000689}
690
691TEST_F(MergeReplacementsTest, OverlappingRanges) {
692 mergeAndTestRewrite("aaa", "bbd", "bcbcd",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000693 toReplacements({{"", 0, 1, "bb"}, {"", 1, 2, "d"}}),
694 toReplacements({{"", 1, 0, "c"}, {"", 2, 0, "c"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000695
Eric Liu40ef2fb2016-08-01 10:16:37 +0000696 mergeAndTestRewrite("aaaa", "aabbaa", "acccca",
697 toReplacements({{"", 2, 0, "bb"}}),
698 toReplacements({{"", 1, 4, "cccc"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000699 mergeAndTestRewrite("aaaa", "aababa", "acccca",
Eric Liu40ef2fb2016-08-01 10:16:37 +0000700 toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}),
701 toReplacements({{"", 1, 4, "cccc"}}));
702 mergeAndTestRewrite("aaaaaa", "abbbba", "abba",
703 toReplacements({{"", 1, 4, "bbbb"}}),
704 toReplacements({{"", 2, 2, ""}}));
705 mergeAndTestRewrite("aaaa", "aa", "cc",
706 toReplacements({{"", 1, 1, ""}, {"", 2, 1, ""}}),
707 toReplacements({{"", 0, 2, "cc"}}));
708 mergeAndTestRewrite("aa", "abbba", "abcbcba",
709 toReplacements({{"", 1, 0, "bbb"}}),
710 toReplacements({{"", 2, 0, "c"}, {"", 3, 0, "c"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000711
Eric Liu40ef2fb2016-08-01 10:16:37 +0000712 mergeAndTestRewrite(
713 "aaa", "abbab", "ccdd",
714 toReplacements({{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}),
715 toReplacements({{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}));
716 mergeAndTestRewrite(
717 "aa", "babbab", "ccdd",
718 toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}),
719 toReplacements({{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}));
Daniel Jasperd89ae9d2015-09-23 08:30:47 +0000720}
721
Manuel Klimek3f001342012-05-23 16:29:20 +0000722} // end namespace tooling
723} // end namespace clang