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