1 | //===--- ARCMT.cpp - Migration to ARC mode --------------------------------===// |
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/ARCMigrate/ARCMT.h" |
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/Support/MemoryBuffer.h" |
24 | #include "llvm/TargetParser/Triple.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 | llvm::is_contained(Range&: IDs, Element: I->getID())) && |
40 | !diagLoc.isBeforeInTranslationUnitThan(Loc: range.getBegin()) && |
41 | (diagLoc == range.getEnd() || |
42 | diagLoc.isBeforeInTranslationUnitThan(Loc: 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(first: eraseS, last: 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 | llvm::is_contained(Range&: IDs, Element: I->getID())) && |
69 | !diagLoc.isBeforeInTranslationUnitThan(Loc: range.getBegin()) && |
70 | (diagLoc == range.getEnd() || |
71 | diagLoc.isBeforeInTranslationUnitThan(Loc: 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(storedDiag: *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(LangOpts: 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!" ); |
131 | } |
132 | |
133 | void HandleDiagnostic(DiagnosticsEngine::Level level, |
134 | const Diagnostic &Info) override { |
135 | if (DiagnosticIDs::isARCDiagnostic(DiagID: Info.getID()) || |
136 | level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) { |
137 | if (Info.getLocation().isValid()) |
138 | CapturedDiags.push_back(diag: StoredDiagnostic(level, Info)); |
139 | return; |
140 | } |
141 | |
142 | // Non-ARC warnings are ignored. |
143 | Diags.setLastDiagnosticIgnored(true); |
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 | return triple.getOSVersion() >= VersionTuple(10, 7); |
166 | } |
167 | |
168 | return false; |
169 | } |
170 | |
171 | static CompilerInvocation * |
172 | createInvocationForMigration(CompilerInvocation &origCI, |
173 | const PCHContainerReader &PCHContainerRdr) { |
174 | std::unique_ptr<CompilerInvocation> CInvok; |
175 | CInvok.reset(p: new CompilerInvocation(origCI)); |
176 | PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); |
177 | if (!PPOpts.ImplicitPCHInclude.empty()) { |
178 | // We can't use a PCH because it was likely built in non-ARC mode and we |
179 | // want to parse in ARC. Include the original header. |
180 | FileManager FileMgr(origCI.getFileSystemOpts()); |
181 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
182 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
183 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
184 | new IgnoringDiagConsumer())); |
185 | std::string OriginalFile = ASTReader::getOriginalSourceFile( |
186 | ASTFileName: PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, Diags&: *Diags); |
187 | if (!OriginalFile.empty()) |
188 | PPOpts.Includes.insert(position: PPOpts.Includes.begin(), x: OriginalFile); |
189 | PPOpts.ImplicitPCHInclude.clear(); |
190 | } |
191 | std::string define = std::string(getARCMTMacroName()); |
192 | define += '='; |
193 | CInvok->getPreprocessorOpts().addMacroDef(Name: define); |
194 | CInvok->getLangOpts().ObjCAutoRefCount = true; |
195 | CInvok->getLangOpts().setGC(LangOptions::NonGC); |
196 | CInvok->getDiagnosticOpts().ErrorLimit = 0; |
197 | CInvok->getDiagnosticOpts().PedanticErrors = 0; |
198 | |
199 | // Ignore -Werror flags when migrating. |
200 | std::vector<std::string> WarnOpts; |
201 | for (std::vector<std::string>::iterator |
202 | I = CInvok->getDiagnosticOpts().Warnings.begin(), |
203 | E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) { |
204 | if (!StringRef(*I).starts_with(Prefix: "error" )) |
205 | WarnOpts.push_back(x: *I); |
206 | } |
207 | WarnOpts.push_back(x: "error=arc-unsafe-retained-assign" ); |
208 | CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts); |
209 | |
210 | CInvok->getLangOpts().ObjCWeakRuntime = HasARCRuntime(origCI); |
211 | CInvok->getLangOpts().ObjCWeak = CInvok->getLangOpts().ObjCWeakRuntime; |
212 | |
213 | return CInvok.release(); |
214 | } |
215 | |
216 | static void emitPremigrationErrors(const CapturedDiagList &arcDiags, |
217 | DiagnosticOptions *diagOpts, |
218 | Preprocessor &PP) { |
219 | TextDiagnosticPrinter printer(llvm::errs(), diagOpts); |
220 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
221 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
222 | new DiagnosticsEngine(DiagID, diagOpts, &printer, |
223 | /*ShouldOwnClient=*/false)); |
224 | Diags->setSourceManager(&PP.getSourceManager()); |
225 | |
226 | printer.BeginSourceFile(LO: PP.getLangOpts(), PP: &PP); |
227 | arcDiags.reportDiagnostics(Diags&: *Diags); |
228 | printer.EndSourceFile(); |
229 | } |
230 | |
231 | //===----------------------------------------------------------------------===// |
232 | // checkForManualIssues. |
233 | //===----------------------------------------------------------------------===// |
234 | |
235 | bool arcmt::checkForManualIssues( |
236 | CompilerInvocation &origCI, const FrontendInputFile &Input, |
237 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
238 | DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors, |
239 | StringRef plistOut) { |
240 | if (!origCI.getLangOpts().ObjC) |
241 | return false; |
242 | |
243 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC(); |
244 | bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; |
245 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; |
246 | |
247 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, |
248 | NoFinalizeRemoval); |
249 | assert(!transforms.empty()); |
250 | |
251 | std::unique_ptr<CompilerInvocation> CInvok; |
252 | CInvok.reset( |
253 | p: createInvocationForMigration(origCI, PCHContainerRdr: PCHContainerOps->getRawReader())); |
254 | CInvok->getFrontendOpts().Inputs.clear(); |
255 | CInvok->getFrontendOpts().Inputs.push_back(Elt: Input); |
256 | |
257 | CapturedDiagList capturedDiags; |
258 | |
259 | assert(DiagClient); |
260 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
261 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
262 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
263 | DiagClient, /*ShouldOwnClient=*/false)); |
264 | |
265 | // Filter of all diagnostics. |
266 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); |
267 | Diags->setClient(client: &errRec, /*ShouldOwnClient=*/false); |
268 | |
269 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
270 | CI: std::move(CInvok), PCHContainerOps, Diags)); |
271 | if (!Unit) { |
272 | errRec.FinishCapture(); |
273 | return true; |
274 | } |
275 | |
276 | // Don't filter diagnostics anymore. |
277 | Diags->setClient(client: DiagClient, /*ShouldOwnClient=*/false); |
278 | |
279 | ASTContext &Ctx = Unit->getASTContext(); |
280 | |
281 | if (Diags->hasFatalErrorOccurred()) { |
282 | Diags->Reset(); |
283 | DiagClient->BeginSourceFile(LangOpts: Ctx.getLangOpts(), PP: &Unit->getPreprocessor()); |
284 | capturedDiags.reportDiagnostics(Diags&: *Diags); |
285 | DiagClient->EndSourceFile(); |
286 | errRec.FinishCapture(); |
287 | return true; |
288 | } |
289 | |
290 | if (emitPremigrationARCErrors) |
291 | emitPremigrationErrors(arcDiags: capturedDiags, diagOpts: &origCI.getDiagnosticOpts(), |
292 | PP&: Unit->getPreprocessor()); |
293 | if (!plistOut.empty()) { |
294 | SmallVector<StoredDiagnostic, 8> arcDiags; |
295 | for (CapturedDiagList::iterator |
296 | I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I) |
297 | arcDiags.push_back(Elt: *I); |
298 | writeARCDiagsToPlist(outPath: std::string(plistOut), diags: arcDiags, |
299 | SM&: Ctx.getSourceManager(), LangOpts: Ctx.getLangOpts()); |
300 | } |
301 | |
302 | // After parsing of source files ended, we want to reuse the |
303 | // diagnostics objects to emit further diagnostics. |
304 | // We call BeginSourceFile because DiagnosticConsumer requires that |
305 | // diagnostics with source range information are emitted only in between |
306 | // BeginSourceFile() and EndSourceFile(). |
307 | DiagClient->BeginSourceFile(LangOpts: Ctx.getLangOpts(), PP: &Unit->getPreprocessor()); |
308 | |
309 | // No macros will be added since we are just checking and we won't modify |
310 | // source code. |
311 | std::vector<SourceLocation> ARCMTMacroLocs; |
312 | |
313 | TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); |
314 | MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags, |
315 | ARCMTMacroLocs); |
316 | pass.setNoFinalizeRemoval(NoFinalizeRemoval); |
317 | if (!NoNSAllocReallocError) |
318 | Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error, |
319 | SourceLocation()); |
320 | |
321 | for (unsigned i=0, e = transforms.size(); i != e; ++i) |
322 | transforms[i](pass); |
323 | |
324 | capturedDiags.reportDiagnostics(Diags&: *Diags); |
325 | |
326 | DiagClient->EndSourceFile(); |
327 | errRec.FinishCapture(); |
328 | |
329 | return capturedDiags.hasErrors() || testAct.hasReportedErrors(); |
330 | } |
331 | |
332 | //===----------------------------------------------------------------------===// |
333 | // applyTransformations. |
334 | //===----------------------------------------------------------------------===// |
335 | |
336 | static bool |
337 | applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input, |
338 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
339 | DiagnosticConsumer *DiagClient, StringRef outputDir, |
340 | bool emitPremigrationARCErrors, StringRef plistOut) { |
341 | if (!origCI.getLangOpts().ObjC) |
342 | return false; |
343 | |
344 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC(); |
345 | |
346 | // Make sure checking is successful first. |
347 | CompilerInvocation CInvokForCheck(origCI); |
348 | if (arcmt::checkForManualIssues(origCI&: CInvokForCheck, Input, PCHContainerOps, |
349 | DiagClient, emitPremigrationARCErrors, |
350 | plistOut)) |
351 | return true; |
352 | |
353 | CompilerInvocation CInvok(origCI); |
354 | CInvok.getFrontendOpts().Inputs.clear(); |
355 | CInvok.getFrontendOpts().Inputs.push_back(Elt: Input); |
356 | |
357 | MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir); |
358 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; |
359 | |
360 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, |
361 | NoFinalizeRemoval); |
362 | assert(!transforms.empty()); |
363 | |
364 | for (unsigned i=0, e = transforms.size(); i != e; ++i) { |
365 | bool err = migration.applyTransform(trans: transforms[i]); |
366 | if (err) return true; |
367 | } |
368 | |
369 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
370 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
371 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
372 | DiagClient, /*ShouldOwnClient=*/false)); |
373 | |
374 | if (outputDir.empty()) { |
375 | origCI.getLangOpts().ObjCAutoRefCount = true; |
376 | return migration.getRemapper().overwriteOriginal(Diag&: *Diags); |
377 | } else { |
378 | return migration.getRemapper().flushToDisk(outputDir, Diag&: *Diags); |
379 | } |
380 | } |
381 | |
382 | bool arcmt::applyTransformations( |
383 | CompilerInvocation &origCI, const FrontendInputFile &Input, |
384 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
385 | DiagnosticConsumer *DiagClient) { |
386 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, |
387 | outputDir: StringRef(), emitPremigrationARCErrors: false, plistOut: StringRef()); |
388 | } |
389 | |
390 | bool arcmt::migrateWithTemporaryFiles( |
391 | CompilerInvocation &origCI, const FrontendInputFile &Input, |
392 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
393 | DiagnosticConsumer *DiagClient, StringRef outputDir, |
394 | bool emitPremigrationARCErrors, StringRef plistOut) { |
395 | assert(!outputDir.empty() && "Expected output directory path" ); |
396 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir, |
397 | emitPremigrationARCErrors, plistOut); |
398 | } |
399 | |
400 | bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & |
401 | remap, |
402 | StringRef outputDir, |
403 | DiagnosticConsumer *DiagClient) { |
404 | assert(!outputDir.empty()); |
405 | |
406 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
407 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
408 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, |
409 | DiagClient, /*ShouldOwnClient=*/false)); |
410 | |
411 | FileRemapper remapper; |
412 | bool err = remapper.initFromDisk(outputDir, Diag&: *Diags, |
413 | /*ignoreIfFilesChanged=*/true); |
414 | if (err) |
415 | return true; |
416 | |
417 | remapper.forEachMapping( |
418 | CaptureFile: [&](StringRef From, StringRef To) { |
419 | remap.push_back(x: std::make_pair(x: From.str(), y: To.str())); |
420 | }, |
421 | CaptureBuffer: [](StringRef, const llvm::MemoryBufferRef &) {}); |
422 | |
423 | return false; |
424 | } |
425 | |
426 | |
427 | //===----------------------------------------------------------------------===// |
428 | // CollectTransformActions. |
429 | //===----------------------------------------------------------------------===// |
430 | |
431 | namespace { |
432 | |
433 | class ARCMTMacroTrackerPPCallbacks : public PPCallbacks { |
434 | std::vector<SourceLocation> &ARCMTMacroLocs; |
435 | |
436 | public: |
437 | ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) |
438 | : ARCMTMacroLocs(ARCMTMacroLocs) { } |
439 | |
440 | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
441 | SourceRange Range, const MacroArgs *Args) override { |
442 | if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) |
443 | ARCMTMacroLocs.push_back(x: MacroNameTok.getLocation()); |
444 | } |
445 | }; |
446 | |
447 | class ARCMTMacroTrackerAction : public ASTFrontendAction { |
448 | std::vector<SourceLocation> &ARCMTMacroLocs; |
449 | |
450 | public: |
451 | ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) |
452 | : ARCMTMacroLocs(ARCMTMacroLocs) { } |
453 | |
454 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, |
455 | StringRef InFile) override { |
456 | CI.getPreprocessor().addPPCallbacks( |
457 | C: std::make_unique<ARCMTMacroTrackerPPCallbacks>(args&: ARCMTMacroLocs)); |
458 | return std::make_unique<ASTConsumer>(); |
459 | } |
460 | }; |
461 | |
462 | class RewritesApplicator : public TransformActions::RewriteReceiver { |
463 | Rewriter &rewriter; |
464 | MigrationProcess::RewriteListener *Listener; |
465 | |
466 | public: |
467 | RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, |
468 | MigrationProcess::RewriteListener *listener) |
469 | : rewriter(rewriter), Listener(listener) { |
470 | if (Listener) |
471 | Listener->start(Ctx&: ctx); |
472 | } |
473 | ~RewritesApplicator() override { |
474 | if (Listener) |
475 | Listener->finish(); |
476 | } |
477 | |
478 | void insert(SourceLocation loc, StringRef text) override { |
479 | bool err = rewriter.InsertText(Loc: loc, Str: text, /*InsertAfter=*/true, |
480 | /*indentNewLines=*/true); |
481 | if (!err && Listener) |
482 | Listener->insert(loc, text); |
483 | } |
484 | |
485 | void remove(CharSourceRange range) override { |
486 | Rewriter::RewriteOptions removeOpts; |
487 | removeOpts.IncludeInsertsAtBeginOfRange = false; |
488 | removeOpts.IncludeInsertsAtEndOfRange = false; |
489 | removeOpts.RemoveLineIfEmpty = true; |
490 | |
491 | bool err = rewriter.RemoveText(range, opts: removeOpts); |
492 | if (!err && Listener) |
493 | Listener->remove(range); |
494 | } |
495 | |
496 | void increaseIndentation(CharSourceRange range, |
497 | SourceLocation parentIndent) override { |
498 | rewriter.IncreaseIndentation(range, parentIndent); |
499 | } |
500 | }; |
501 | |
502 | } // end anonymous namespace. |
503 | |
504 | /// Anchor for VTable. |
505 | MigrationProcess::RewriteListener::~RewriteListener() { } |
506 | |
507 | MigrationProcess::MigrationProcess( |
508 | CompilerInvocation &CI, |
509 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
510 | DiagnosticConsumer *diagClient, StringRef outputDir) |
511 | : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)), |
512 | DiagClient(diagClient), HadARCErrors(false) { |
513 | if (!outputDir.empty()) { |
514 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
515 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
516 | new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), |
517 | DiagClient, /*ShouldOwnClient=*/false)); |
518 | Remapper.initFromDisk(outputDir, Diag&: *Diags, /*ignoreIfFilesChanged=*/true); |
519 | } |
520 | } |
521 | |
522 | bool MigrationProcess::applyTransform(TransformFn trans, |
523 | RewriteListener *listener) { |
524 | std::unique_ptr<CompilerInvocation> CInvok; |
525 | CInvok.reset( |
526 | p: createInvocationForMigration(origCI&: OrigCI, PCHContainerRdr: PCHContainerOps->getRawReader())); |
527 | CInvok->getDiagnosticOpts().IgnoreWarnings = true; |
528 | |
529 | Remapper.applyMappings(PPOpts&: CInvok->getPreprocessorOpts()); |
530 | |
531 | CapturedDiagList capturedDiags; |
532 | std::vector<SourceLocation> ARCMTMacroLocs; |
533 | |
534 | assert(DiagClient); |
535 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
536 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
537 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, |
538 | DiagClient, /*ShouldOwnClient=*/false)); |
539 | |
540 | // Filter of all diagnostics. |
541 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); |
542 | Diags->setClient(client: &errRec, /*ShouldOwnClient=*/false); |
543 | |
544 | std::unique_ptr<ARCMTMacroTrackerAction> ASTAction; |
545 | ASTAction.reset(p: new ARCMTMacroTrackerAction(ARCMTMacroLocs)); |
546 | |
547 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
548 | CI: std::move(CInvok), PCHContainerOps, Diags, Action: ASTAction.get())); |
549 | if (!Unit) { |
550 | errRec.FinishCapture(); |
551 | return true; |
552 | } |
553 | Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. |
554 | |
555 | HadARCErrors = HadARCErrors || capturedDiags.hasErrors(); |
556 | |
557 | // Don't filter diagnostics anymore. |
558 | Diags->setClient(client: DiagClient, /*ShouldOwnClient=*/false); |
559 | |
560 | ASTContext &Ctx = Unit->getASTContext(); |
561 | |
562 | if (Diags->hasFatalErrorOccurred()) { |
563 | Diags->Reset(); |
564 | DiagClient->BeginSourceFile(LangOpts: Ctx.getLangOpts(), PP: &Unit->getPreprocessor()); |
565 | capturedDiags.reportDiagnostics(Diags&: *Diags); |
566 | DiagClient->EndSourceFile(); |
567 | errRec.FinishCapture(); |
568 | return true; |
569 | } |
570 | |
571 | // After parsing of source files ended, we want to reuse the |
572 | // diagnostics objects to emit further diagnostics. |
573 | // We call BeginSourceFile because DiagnosticConsumer requires that |
574 | // diagnostics with source range information are emitted only in between |
575 | // BeginSourceFile() and EndSourceFile(). |
576 | DiagClient->BeginSourceFile(LangOpts: Ctx.getLangOpts(), PP: &Unit->getPreprocessor()); |
577 | |
578 | Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); |
579 | TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); |
580 | MigrationPass pass(Ctx, OrigCI.getLangOpts().getGC(), |
581 | Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs); |
582 | |
583 | trans(pass); |
584 | |
585 | { |
586 | RewritesApplicator applicator(rewriter, Ctx, listener); |
587 | TA.applyRewrites(receiver&: applicator); |
588 | } |
589 | |
590 | DiagClient->EndSourceFile(); |
591 | errRec.FinishCapture(); |
592 | |
593 | if (DiagClient->getNumErrors()) |
594 | return true; |
595 | |
596 | for (Rewriter::buffer_iterator |
597 | I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { |
598 | FileID FID = I->first; |
599 | RewriteBuffer &buf = I->second; |
600 | OptionalFileEntryRef file = |
601 | Ctx.getSourceManager().getFileEntryRefForID(FID); |
602 | assert(file); |
603 | std::string newFname = std::string(file->getName()); |
604 | newFname += "-trans" ; |
605 | SmallString<512> newText; |
606 | llvm::raw_svector_ostream vecOS(newText); |
607 | buf.write(Stream&: vecOS); |
608 | std::unique_ptr<llvm::MemoryBuffer> memBuf( |
609 | llvm::MemoryBuffer::getMemBufferCopy( |
610 | InputData: StringRef(newText.data(), newText.size()), BufferName: newFname)); |
611 | SmallString<64> filePath(file->getName()); |
612 | Unit->getFileManager().FixupRelativePath(path&: filePath); |
613 | Remapper.remap(filePath: filePath.str(), memBuf: std::move(memBuf)); |
614 | } |
615 | |
616 | return false; |
617 | } |
618 | |