File: | tools/clang/lib/ARCMigrate/ARCMT.cpp |
Warning: | line 522, column 1 Potential leak of memory pointed to by field 'Obj' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- ARCMT.cpp - Migration to ARC mode --------------------------------===// | |||
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 | ||||
10 | #include "Internals.h" | |||
11 | #include "clang/AST/ASTConsumer.h" | |||
12 | #include "clang/Basic/DiagnosticCategories.h" | |||
13 | #include "clang/Frontend/ASTUnit.h" | |||
14 | #include "clang/Frontend/CompilerInstance.h" | |||
15 | #include "clang/Frontend/FrontendAction.h" | |||
16 | #include "clang/Frontend/TextDiagnosticPrinter.h" | |||
17 | #include "clang/Frontend/Utils.h" | |||
18 | #include "clang/Lex/Preprocessor.h" | |||
19 | #include "clang/Lex/PreprocessorOptions.h" | |||
20 | #include "clang/Rewrite/Core/Rewriter.h" | |||
21 | #include "clang/Sema/SemaDiagnostic.h" | |||
22 | #include "clang/Serialization/ASTReader.h" | |||
23 | #include "llvm/ADT/Triple.h" | |||
24 | #include "llvm/Support/MemoryBuffer.h" | |||
25 | #include <utility> | |||
26 | using namespace clang; | |||
27 | using namespace arcmt; | |||
28 | ||||
29 | bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs, | |||
30 | SourceRange range) { | |||
31 | if (range.isInvalid()) | |||
32 | return false; | |||
33 | ||||
34 | bool cleared = false; | |||
35 | ListTy::iterator I = List.begin(); | |||
36 | while (I != List.end()) { | |||
37 | FullSourceLoc diagLoc = I->getLocation(); | |||
38 | if ((IDs.empty() || // empty means clear all diagnostics in the range. | |||
39 | std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && | |||
40 | !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && | |||
41 | (diagLoc == range.getEnd() || | |||
42 | diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { | |||
43 | cleared = true; | |||
44 | ListTy::iterator eraseS = I++; | |||
45 | if (eraseS->getLevel() != DiagnosticsEngine::Note) | |||
46 | while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note) | |||
47 | ++I; | |||
48 | // Clear the diagnostic and any notes following it. | |||
49 | I = List.erase(eraseS, I); | |||
50 | continue; | |||
51 | } | |||
52 | ||||
53 | ++I; | |||
54 | } | |||
55 | ||||
56 | return cleared; | |||
57 | } | |||
58 | ||||
59 | bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs, | |||
60 | SourceRange range) const { | |||
61 | if (range.isInvalid()) | |||
62 | return false; | |||
63 | ||||
64 | ListTy::const_iterator I = List.begin(); | |||
65 | while (I != List.end()) { | |||
66 | FullSourceLoc diagLoc = I->getLocation(); | |||
67 | if ((IDs.empty() || // empty means any diagnostic in the range. | |||
68 | std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && | |||
69 | !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && | |||
70 | (diagLoc == range.getEnd() || | |||
71 | diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { | |||
72 | return true; | |||
73 | } | |||
74 | ||||
75 | ++I; | |||
76 | } | |||
77 | ||||
78 | return false; | |||
79 | } | |||
80 | ||||
81 | void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const { | |||
82 | for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) | |||
83 | Diags.Report(*I); | |||
84 | } | |||
85 | ||||
86 | bool CapturedDiagList::hasErrors() const { | |||
87 | for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) | |||
88 | if (I->getLevel() >= DiagnosticsEngine::Error) | |||
89 | return true; | |||
90 | ||||
91 | return false; | |||
92 | } | |||
93 | ||||
94 | namespace { | |||
95 | ||||
96 | class CaptureDiagnosticConsumer : public DiagnosticConsumer { | |||
97 | DiagnosticsEngine &Diags; | |||
98 | DiagnosticConsumer &DiagClient; | |||
99 | CapturedDiagList &CapturedDiags; | |||
100 | bool HasBegunSourceFile; | |||
101 | public: | |||
102 | CaptureDiagnosticConsumer(DiagnosticsEngine &diags, | |||
103 | DiagnosticConsumer &client, | |||
104 | CapturedDiagList &capturedDiags) | |||
105 | : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags), | |||
106 | HasBegunSourceFile(false) { } | |||
107 | ||||
108 | void BeginSourceFile(const LangOptions &Opts, | |||
109 | const Preprocessor *PP) override { | |||
110 | // Pass BeginSourceFile message onto DiagClient on first call. | |||
111 | // The corresponding EndSourceFile call will be made from an | |||
112 | // explicit call to FinishCapture. | |||
113 | if (!HasBegunSourceFile) { | |||
114 | DiagClient.BeginSourceFile(Opts, PP); | |||
115 | HasBegunSourceFile = true; | |||
116 | } | |||
117 | } | |||
118 | ||||
119 | void FinishCapture() { | |||
120 | // Call EndSourceFile on DiagClient on completion of capture to | |||
121 | // enable VerifyDiagnosticConsumer to check diagnostics *after* | |||
122 | // it has received the diagnostic list. | |||
123 | if (HasBegunSourceFile) { | |||
124 | DiagClient.EndSourceFile(); | |||
125 | HasBegunSourceFile = false; | |||
126 | } | |||
127 | } | |||
128 | ||||
129 | ~CaptureDiagnosticConsumer() override { | |||
130 | assert(!HasBegunSourceFile && "FinishCapture not called!")(static_cast <bool> (!HasBegunSourceFile && "FinishCapture not called!" ) ? void (0) : __assert_fail ("!HasBegunSourceFile && \"FinishCapture not called!\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 130, __extension__ __PRETTY_FUNCTION__)); | |||
131 | } | |||
132 | ||||
133 | void HandleDiagnostic(DiagnosticsEngine::Level level, | |||
134 | const Diagnostic &Info) override { | |||
135 | if (DiagnosticIDs::isARCDiagnostic(Info.getID()) || | |||
136 | level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) { | |||
137 | if (Info.getLocation().isValid()) | |||
138 | CapturedDiags.push_back(StoredDiagnostic(level, Info)); | |||
139 | return; | |||
140 | } | |||
141 | ||||
142 | // Non-ARC warnings are ignored. | |||
143 | Diags.setLastDiagnosticIgnored(); | |||
144 | } | |||
145 | }; | |||
146 | ||||
147 | } // end anonymous namespace | |||
148 | ||||
149 | static bool HasARCRuntime(CompilerInvocation &origCI) { | |||
150 | // This duplicates some functionality from Darwin::AddDeploymentTarget | |||
151 | // but this function is well defined, so keep it decoupled from the driver | |||
152 | // and avoid unrelated complications. | |||
153 | llvm::Triple triple(origCI.getTargetOpts().Triple); | |||
154 | ||||
155 | if (triple.isiOS()) | |||
156 | return triple.getOSMajorVersion() >= 5; | |||
157 | ||||
158 | if (triple.isWatchOS()) | |||
159 | return true; | |||
160 | ||||
161 | if (triple.getOS() == llvm::Triple::Darwin) | |||
162 | return triple.getOSMajorVersion() >= 11; | |||
163 | ||||
164 | if (triple.getOS() == llvm::Triple::MacOSX) { | |||
165 | unsigned Major, Minor, Micro; | |||
166 | triple.getOSVersion(Major, Minor, Micro); | |||
167 | return Major > 10 || (Major == 10 && Minor >= 7); | |||
168 | } | |||
169 | ||||
170 | return false; | |||
171 | } | |||
172 | ||||
173 | static CompilerInvocation * | |||
174 | createInvocationForMigration(CompilerInvocation &origCI, | |||
175 | const PCHContainerReader &PCHContainerRdr) { | |||
176 | std::unique_ptr<CompilerInvocation> CInvok; | |||
177 | CInvok.reset(new CompilerInvocation(origCI)); | |||
178 | PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); | |||
179 | if (!PPOpts.ImplicitPCHInclude.empty()) { | |||
180 | // We can't use a PCH because it was likely built in non-ARC mode and we | |||
181 | // want to parse in ARC. Include the original header. | |||
182 | FileManager FileMgr(origCI.getFileSystemOpts()); | |||
183 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
184 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
185 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), | |||
186 | new IgnoringDiagConsumer())); | |||
187 | std::string OriginalFile = ASTReader::getOriginalSourceFile( | |||
188 | PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags); | |||
189 | if (!OriginalFile.empty()) | |||
190 | PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile); | |||
191 | PPOpts.ImplicitPCHInclude.clear(); | |||
192 | } | |||
193 | // FIXME: Get the original header of a PTH as well. | |||
194 | CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear(); | |||
195 | std::string define = getARCMTMacroName(); | |||
196 | define += '='; | |||
197 | CInvok->getPreprocessorOpts().addMacroDef(define); | |||
198 | CInvok->getLangOpts()->ObjCAutoRefCount = true; | |||
199 | CInvok->getLangOpts()->setGC(LangOptions::NonGC); | |||
200 | CInvok->getDiagnosticOpts().ErrorLimit = 0; | |||
201 | CInvok->getDiagnosticOpts().PedanticErrors = 0; | |||
202 | ||||
203 | // Ignore -Werror flags when migrating. | |||
204 | std::vector<std::string> WarnOpts; | |||
205 | for (std::vector<std::string>::iterator | |||
206 | I = CInvok->getDiagnosticOpts().Warnings.begin(), | |||
207 | E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) { | |||
208 | if (!StringRef(*I).startswith("error")) | |||
209 | WarnOpts.push_back(*I); | |||
210 | } | |||
211 | WarnOpts.push_back("error=arc-unsafe-retained-assign"); | |||
212 | CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts); | |||
213 | ||||
214 | CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI); | |||
215 | CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime; | |||
216 | ||||
217 | return CInvok.release(); | |||
218 | } | |||
219 | ||||
220 | static void emitPremigrationErrors(const CapturedDiagList &arcDiags, | |||
221 | DiagnosticOptions *diagOpts, | |||
222 | Preprocessor &PP) { | |||
223 | TextDiagnosticPrinter printer(llvm::errs(), diagOpts); | |||
224 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
225 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
226 | new DiagnosticsEngine(DiagID, diagOpts, &printer, | |||
227 | /*ShouldOwnClient=*/false)); | |||
228 | Diags->setSourceManager(&PP.getSourceManager()); | |||
229 | ||||
230 | printer.BeginSourceFile(PP.getLangOpts(), &PP); | |||
231 | arcDiags.reportDiagnostics(*Diags); | |||
232 | printer.EndSourceFile(); | |||
233 | } | |||
234 | ||||
235 | //===----------------------------------------------------------------------===// | |||
236 | // checkForManualIssues. | |||
237 | //===----------------------------------------------------------------------===// | |||
238 | ||||
239 | bool arcmt::checkForManualIssues( | |||
240 | CompilerInvocation &origCI, const FrontendInputFile &Input, | |||
241 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, | |||
242 | DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors, | |||
243 | StringRef plistOut) { | |||
244 | if (!origCI.getLangOpts()->ObjC1) | |||
245 | return false; | |||
246 | ||||
247 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); | |||
248 | bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; | |||
249 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; | |||
250 | ||||
251 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, | |||
252 | NoFinalizeRemoval); | |||
253 | assert(!transforms.empty())(static_cast <bool> (!transforms.empty()) ? void (0) : __assert_fail ("!transforms.empty()", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 253, __extension__ __PRETTY_FUNCTION__)); | |||
254 | ||||
255 | std::unique_ptr<CompilerInvocation> CInvok; | |||
256 | CInvok.reset( | |||
257 | createInvocationForMigration(origCI, PCHContainerOps->getRawReader())); | |||
258 | CInvok->getFrontendOpts().Inputs.clear(); | |||
259 | CInvok->getFrontendOpts().Inputs.push_back(Input); | |||
260 | ||||
261 | CapturedDiagList capturedDiags; | |||
262 | ||||
263 | assert(DiagClient)(static_cast <bool> (DiagClient) ? void (0) : __assert_fail ("DiagClient", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 263, __extension__ __PRETTY_FUNCTION__)); | |||
264 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
265 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
266 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), | |||
267 | DiagClient, /*ShouldOwnClient=*/false)); | |||
268 | ||||
269 | // Filter of all diagnostics. | |||
270 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); | |||
271 | Diags->setClient(&errRec, /*ShouldOwnClient=*/false); | |||
272 | ||||
273 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( | |||
274 | std::move(CInvok), PCHContainerOps, Diags)); | |||
275 | if (!Unit) { | |||
276 | errRec.FinishCapture(); | |||
277 | return true; | |||
278 | } | |||
279 | ||||
280 | // Don't filter diagnostics anymore. | |||
281 | Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); | |||
282 | ||||
283 | ASTContext &Ctx = Unit->getASTContext(); | |||
284 | ||||
285 | if (Diags->hasFatalErrorOccurred()) { | |||
286 | Diags->Reset(); | |||
287 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); | |||
288 | capturedDiags.reportDiagnostics(*Diags); | |||
289 | DiagClient->EndSourceFile(); | |||
290 | errRec.FinishCapture(); | |||
291 | return true; | |||
292 | } | |||
293 | ||||
294 | if (emitPremigrationARCErrors) | |||
295 | emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(), | |||
296 | Unit->getPreprocessor()); | |||
297 | if (!plistOut.empty()) { | |||
298 | SmallVector<StoredDiagnostic, 8> arcDiags; | |||
299 | for (CapturedDiagList::iterator | |||
300 | I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I) | |||
301 | arcDiags.push_back(*I); | |||
302 | writeARCDiagsToPlist(plistOut, arcDiags, | |||
303 | Ctx.getSourceManager(), Ctx.getLangOpts()); | |||
304 | } | |||
305 | ||||
306 | // After parsing of source files ended, we want to reuse the | |||
307 | // diagnostics objects to emit further diagnostics. | |||
308 | // We call BeginSourceFile because DiagnosticConsumer requires that | |||
309 | // diagnostics with source range information are emitted only in between | |||
310 | // BeginSourceFile() and EndSourceFile(). | |||
311 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); | |||
312 | ||||
313 | // No macros will be added since we are just checking and we won't modify | |||
314 | // source code. | |||
315 | std::vector<SourceLocation> ARCMTMacroLocs; | |||
316 | ||||
317 | TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); | |||
318 | MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags, | |||
319 | ARCMTMacroLocs); | |||
320 | pass.setNoFinalizeRemoval(NoFinalizeRemoval); | |||
321 | if (!NoNSAllocReallocError) | |||
322 | Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error, | |||
323 | SourceLocation()); | |||
324 | ||||
325 | for (unsigned i=0, e = transforms.size(); i != e; ++i) | |||
326 | transforms[i](pass); | |||
327 | ||||
328 | capturedDiags.reportDiagnostics(*Diags); | |||
329 | ||||
330 | DiagClient->EndSourceFile(); | |||
331 | errRec.FinishCapture(); | |||
332 | ||||
333 | return capturedDiags.hasErrors() || testAct.hasReportedErrors(); | |||
334 | } | |||
335 | ||||
336 | //===----------------------------------------------------------------------===// | |||
337 | // applyTransformations. | |||
338 | //===----------------------------------------------------------------------===// | |||
339 | ||||
340 | static bool | |||
341 | applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input, | |||
342 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, | |||
343 | DiagnosticConsumer *DiagClient, StringRef outputDir, | |||
344 | bool emitPremigrationARCErrors, StringRef plistOut) { | |||
345 | if (!origCI.getLangOpts()->ObjC1) | |||
346 | return false; | |||
347 | ||||
348 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); | |||
349 | ||||
350 | // Make sure checking is successful first. | |||
351 | CompilerInvocation CInvokForCheck(origCI); | |||
352 | if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps, | |||
353 | DiagClient, emitPremigrationARCErrors, | |||
354 | plistOut)) | |||
355 | return true; | |||
356 | ||||
357 | CompilerInvocation CInvok(origCI); | |||
358 | CInvok.getFrontendOpts().Inputs.clear(); | |||
359 | CInvok.getFrontendOpts().Inputs.push_back(Input); | |||
360 | ||||
361 | MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir); | |||
362 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; | |||
363 | ||||
364 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, | |||
365 | NoFinalizeRemoval); | |||
366 | assert(!transforms.empty())(static_cast <bool> (!transforms.empty()) ? void (0) : __assert_fail ("!transforms.empty()", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 366, __extension__ __PRETTY_FUNCTION__)); | |||
367 | ||||
368 | for (unsigned i=0, e = transforms.size(); i != e; ++i) { | |||
369 | bool err = migration.applyTransform(transforms[i]); | |||
370 | if (err) return true; | |||
371 | } | |||
372 | ||||
373 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
374 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
375 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), | |||
376 | DiagClient, /*ShouldOwnClient=*/false)); | |||
377 | ||||
378 | if (outputDir.empty()) { | |||
379 | origCI.getLangOpts()->ObjCAutoRefCount = true; | |||
380 | return migration.getRemapper().overwriteOriginal(*Diags); | |||
381 | } else { | |||
382 | return migration.getRemapper().flushToDisk(outputDir, *Diags); | |||
383 | } | |||
384 | } | |||
385 | ||||
386 | bool arcmt::applyTransformations( | |||
387 | CompilerInvocation &origCI, const FrontendInputFile &Input, | |||
388 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, | |||
389 | DiagnosticConsumer *DiagClient) { | |||
390 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, | |||
391 | StringRef(), false, StringRef()); | |||
392 | } | |||
393 | ||||
394 | bool arcmt::migrateWithTemporaryFiles( | |||
395 | CompilerInvocation &origCI, const FrontendInputFile &Input, | |||
396 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, | |||
397 | DiagnosticConsumer *DiagClient, StringRef outputDir, | |||
398 | bool emitPremigrationARCErrors, StringRef plistOut) { | |||
399 | assert(!outputDir.empty() && "Expected output directory path")(static_cast <bool> (!outputDir.empty() && "Expected output directory path" ) ? void (0) : __assert_fail ("!outputDir.empty() && \"Expected output directory path\"" , "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 399, __extension__ __PRETTY_FUNCTION__)); | |||
400 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir, | |||
401 | emitPremigrationARCErrors, plistOut); | |||
402 | } | |||
403 | ||||
404 | bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & | |||
405 | remap, | |||
406 | StringRef outputDir, | |||
407 | DiagnosticConsumer *DiagClient) { | |||
408 | assert(!outputDir.empty())(static_cast <bool> (!outputDir.empty()) ? void (0) : __assert_fail ("!outputDir.empty()", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 408, __extension__ __PRETTY_FUNCTION__)); | |||
409 | ||||
410 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
411 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
412 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, | |||
413 | DiagClient, /*ShouldOwnClient=*/false)); | |||
414 | ||||
415 | FileRemapper remapper; | |||
416 | bool err = remapper.initFromDisk(outputDir, *Diags, | |||
417 | /*ignoreIfFilesChanged=*/true); | |||
418 | if (err) | |||
419 | return true; | |||
420 | ||||
421 | PreprocessorOptions PPOpts; | |||
422 | remapper.applyMappings(PPOpts); | |||
423 | remap = PPOpts.RemappedFiles; | |||
424 | ||||
425 | return false; | |||
426 | } | |||
427 | ||||
428 | ||||
429 | //===----------------------------------------------------------------------===// | |||
430 | // CollectTransformActions. | |||
431 | //===----------------------------------------------------------------------===// | |||
432 | ||||
433 | namespace { | |||
434 | ||||
435 | class ARCMTMacroTrackerPPCallbacks : public PPCallbacks { | |||
436 | std::vector<SourceLocation> &ARCMTMacroLocs; | |||
437 | ||||
438 | public: | |||
439 | ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) | |||
440 | : ARCMTMacroLocs(ARCMTMacroLocs) { } | |||
441 | ||||
442 | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, | |||
443 | SourceRange Range, const MacroArgs *Args) override { | |||
444 | if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) | |||
445 | ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); | |||
446 | } | |||
447 | }; | |||
448 | ||||
449 | class ARCMTMacroTrackerAction : public ASTFrontendAction { | |||
450 | std::vector<SourceLocation> &ARCMTMacroLocs; | |||
451 | ||||
452 | public: | |||
453 | ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) | |||
454 | : ARCMTMacroLocs(ARCMTMacroLocs) { } | |||
455 | ||||
456 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, | |||
457 | StringRef InFile) override { | |||
458 | CI.getPreprocessor().addPPCallbacks( | |||
459 | llvm::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs)); | |||
460 | return llvm::make_unique<ASTConsumer>(); | |||
461 | } | |||
462 | }; | |||
463 | ||||
464 | class RewritesApplicator : public TransformActions::RewriteReceiver { | |||
465 | Rewriter &rewriter; | |||
466 | MigrationProcess::RewriteListener *Listener; | |||
467 | ||||
468 | public: | |||
469 | RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, | |||
470 | MigrationProcess::RewriteListener *listener) | |||
471 | : rewriter(rewriter), Listener(listener) { | |||
472 | if (Listener) | |||
473 | Listener->start(ctx); | |||
474 | } | |||
475 | ~RewritesApplicator() override { | |||
476 | if (Listener) | |||
477 | Listener->finish(); | |||
478 | } | |||
479 | ||||
480 | void insert(SourceLocation loc, StringRef text) override { | |||
481 | bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true, | |||
482 | /*indentNewLines=*/true); | |||
483 | if (!err && Listener) | |||
484 | Listener->insert(loc, text); | |||
485 | } | |||
486 | ||||
487 | void remove(CharSourceRange range) override { | |||
488 | Rewriter::RewriteOptions removeOpts; | |||
489 | removeOpts.IncludeInsertsAtBeginOfRange = false; | |||
490 | removeOpts.IncludeInsertsAtEndOfRange = false; | |||
491 | removeOpts.RemoveLineIfEmpty = true; | |||
492 | ||||
493 | bool err = rewriter.RemoveText(range, removeOpts); | |||
494 | if (!err && Listener) | |||
495 | Listener->remove(range); | |||
496 | } | |||
497 | ||||
498 | void increaseIndentation(CharSourceRange range, | |||
499 | SourceLocation parentIndent) override { | |||
500 | rewriter.IncreaseIndentation(range, parentIndent); | |||
501 | } | |||
502 | }; | |||
503 | ||||
504 | } // end anonymous namespace. | |||
505 | ||||
506 | /// Anchor for VTable. | |||
507 | MigrationProcess::RewriteListener::~RewriteListener() { } | |||
508 | ||||
509 | MigrationProcess::MigrationProcess( | |||
510 | const CompilerInvocation &CI, | |||
511 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, | |||
512 | DiagnosticConsumer *diagClient, StringRef outputDir) | |||
513 | : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)), | |||
514 | DiagClient(diagClient), HadARCErrors(false) { | |||
515 | if (!outputDir.empty()) { | |||
| ||||
516 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
517 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
518 | new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), | |||
519 | DiagClient, /*ShouldOwnClient=*/false)); | |||
520 | Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); | |||
521 | } | |||
522 | } | |||
| ||||
523 | ||||
524 | bool MigrationProcess::applyTransform(TransformFn trans, | |||
525 | RewriteListener *listener) { | |||
526 | std::unique_ptr<CompilerInvocation> CInvok; | |||
527 | CInvok.reset( | |||
528 | createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader())); | |||
529 | CInvok->getDiagnosticOpts().IgnoreWarnings = true; | |||
530 | ||||
531 | Remapper.applyMappings(CInvok->getPreprocessorOpts()); | |||
532 | ||||
533 | CapturedDiagList capturedDiags; | |||
534 | std::vector<SourceLocation> ARCMTMacroLocs; | |||
535 | ||||
536 | assert(DiagClient)(static_cast <bool> (DiagClient) ? void (0) : __assert_fail ("DiagClient", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 536, __extension__ __PRETTY_FUNCTION__)); | |||
537 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); | |||
538 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( | |||
539 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, | |||
540 | DiagClient, /*ShouldOwnClient=*/false)); | |||
541 | ||||
542 | // Filter of all diagnostics. | |||
543 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); | |||
544 | Diags->setClient(&errRec, /*ShouldOwnClient=*/false); | |||
545 | ||||
546 | std::unique_ptr<ARCMTMacroTrackerAction> ASTAction; | |||
547 | ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); | |||
548 | ||||
549 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( | |||
550 | std::move(CInvok), PCHContainerOps, Diags, ASTAction.get())); | |||
551 | if (!Unit) { | |||
552 | errRec.FinishCapture(); | |||
553 | return true; | |||
554 | } | |||
555 | Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. | |||
556 | ||||
557 | HadARCErrors = HadARCErrors || capturedDiags.hasErrors(); | |||
558 | ||||
559 | // Don't filter diagnostics anymore. | |||
560 | Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); | |||
561 | ||||
562 | ASTContext &Ctx = Unit->getASTContext(); | |||
563 | ||||
564 | if (Diags->hasFatalErrorOccurred()) { | |||
565 | Diags->Reset(); | |||
566 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); | |||
567 | capturedDiags.reportDiagnostics(*Diags); | |||
568 | DiagClient->EndSourceFile(); | |||
569 | errRec.FinishCapture(); | |||
570 | return true; | |||
571 | } | |||
572 | ||||
573 | // After parsing of source files ended, we want to reuse the | |||
574 | // diagnostics objects to emit further diagnostics. | |||
575 | // We call BeginSourceFile because DiagnosticConsumer requires that | |||
576 | // diagnostics with source range information are emitted only in between | |||
577 | // BeginSourceFile() and EndSourceFile(). | |||
578 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); | |||
579 | ||||
580 | Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); | |||
581 | TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); | |||
582 | MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(), | |||
583 | Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs); | |||
584 | ||||
585 | trans(pass); | |||
586 | ||||
587 | { | |||
588 | RewritesApplicator applicator(rewriter, Ctx, listener); | |||
589 | TA.applyRewrites(applicator); | |||
590 | } | |||
591 | ||||
592 | DiagClient->EndSourceFile(); | |||
593 | errRec.FinishCapture(); | |||
594 | ||||
595 | if (DiagClient->getNumErrors()) | |||
596 | return true; | |||
597 | ||||
598 | for (Rewriter::buffer_iterator | |||
599 | I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { | |||
600 | FileID FID = I->first; | |||
601 | RewriteBuffer &buf = I->second; | |||
602 | const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); | |||
603 | assert(file)(static_cast <bool> (file) ? void (0) : __assert_fail ( "file", "/build/llvm-toolchain-snapshot-7~svn338205/tools/clang/lib/ARCMigrate/ARCMT.cpp" , 603, __extension__ __PRETTY_FUNCTION__)); | |||
604 | std::string newFname = file->getName(); | |||
605 | newFname += "-trans"; | |||
606 | SmallString<512> newText; | |||
607 | llvm::raw_svector_ostream vecOS(newText); | |||
608 | buf.write(vecOS); | |||
609 | std::unique_ptr<llvm::MemoryBuffer> memBuf( | |||
610 | llvm::MemoryBuffer::getMemBufferCopy( | |||
611 | StringRef(newText.data(), newText.size()), newFname)); | |||
612 | SmallString<64> filePath(file->getName()); | |||
613 | Unit->getFileManager().FixupRelativePath(filePath); | |||
614 | Remapper.remap(filePath.str(), std::move(memBuf)); | |||
615 | } | |||
616 | ||||
617 | return false; | |||
618 | } |