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