diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/ARCMigrate')
19 files changed, 1662 insertions, 413 deletions
diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp index 6e1b0e5..9354dc3 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp @@ -10,6 +10,7 @@ #include "Internals.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/AST/ASTConsumer.h" @@ -98,7 +99,7 @@ public: virtual void HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic &Info) { - if (arcmt::isARCDiagnostic(Info.getID(), Diags) || + if (DiagnosticIDs::isARCDiagnostic(Info.getID()) || level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) { CapturedDiags.push_back(StoredDiagnostic(level, Info)); return; @@ -184,18 +185,19 @@ static bool HasARCRuntime(CompilerInvocation &origCI) { static CompilerInvocation * createInvocationForMigration(CompilerInvocation &origCI) { - llvm::OwningPtr<CompilerInvocation> CInvok; + OwningPtr<CompilerInvocation> CInvok; CInvok.reset(new CompilerInvocation(origCI)); CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string(); CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string(); std::string define = getARCMTMacroName(); define += '='; CInvok->getPreprocessorOpts().addMacroDef(define); - CInvok->getLangOpts().ObjCAutoRefCount = true; + CInvok->getLangOpts()->ObjCAutoRefCount = true; + CInvok->getLangOpts()->setGC(LangOptions::NonGC); CInvok->getDiagnosticOpts().ErrorLimit = 0; CInvok->getDiagnosticOpts().Warnings.push_back( "error=arc-unsafe-retained-assign"); - CInvok->getLangOpts().ObjCRuntimeHasWeak = HasARCRuntime(origCI); + CInvok->getLangOpts()->ObjCRuntimeHasWeak = HasARCRuntime(origCI); return CInvok.take(); } @@ -204,12 +206,12 @@ static void emitPremigrationErrors(const CapturedDiagList &arcDiags, const DiagnosticOptions &diagOpts, Preprocessor &PP) { TextDiagnosticPrinter printer(llvm::errs(), diagOpts); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, &printer, /*ShouldOwnClient=*/false)); Diags->setSourceManager(&PP.getSourceManager()); - printer.BeginSourceFile(PP.getLangOptions(), &PP); + printer.BeginSourceFile(PP.getLangOpts(), &PP); arcDiags.reportDiagnostics(*Diags); printer.EndSourceFile(); } @@ -219,33 +221,38 @@ static void emitPremigrationErrors(const CapturedDiagList &arcDiags, //===----------------------------------------------------------------------===// bool arcmt::checkForManualIssues(CompilerInvocation &origCI, - StringRef Filename, InputKind Kind, + const FrontendInputFile &Input, DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors, StringRef plistOut) { - if (!origCI.getLangOpts().ObjC1) + if (!origCI.getLangOpts()->ObjC1) return false; - std::vector<TransformFn> transforms = arcmt::getAllTransformations(); + LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); + bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; + bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; + + std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, + NoFinalizeRemoval); assert(!transforms.empty()); - llvm::OwningPtr<CompilerInvocation> CInvok; + OwningPtr<CompilerInvocation> CInvok; CInvok.reset(createInvocationForMigration(origCI)); CInvok->getFrontendOpts().Inputs.clear(); - CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename)); + CInvok->getFrontendOpts().Inputs.push_back(Input); CapturedDiagList capturedDiags; assert(DiagClient); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); // Filter of all diagnostics. CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); Diags->setClient(&errRec, /*ShouldOwnClient=*/false); - llvm::OwningPtr<ASTUnit> Unit( + OwningPtr<ASTUnit> Unit( ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags)); if (!Unit) return true; @@ -257,7 +264,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, if (Diags->hasFatalErrorOccurred()) { Diags->Reset(); - DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); capturedDiags.reportDiagnostics(*Diags); DiagClient->EndSourceFile(); return true; @@ -272,7 +279,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I) arcDiags.push_back(*I); writeARCDiagsToPlist(plistOut, arcDiags, - Ctx.getSourceManager(), Ctx.getLangOptions()); + Ctx.getSourceManager(), Ctx.getLangOpts()); } // After parsing of source files ended, we want to reuse the @@ -280,14 +287,16 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, // We call BeginSourceFile because DiagnosticConsumer requires that // diagnostics with source range information are emitted only in between // BeginSourceFile() and EndSourceFile(). - DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); // No macros will be added since we are just checking and we won't modify // source code. std::vector<SourceLocation> ARCMTMacroLocs; TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); - MigrationPass pass(Ctx, Unit->getSema(), testAct, ARCMTMacroLocs); + MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, ARCMTMacroLocs); + pass.setNSAllocReallocError(NoNSAllocReallocError); + pass.setNoFinalizeRemoval(NoFinalizeRemoval); for (unsigned i=0, e = transforms.size(); i != e; ++i) transforms[i](pass); @@ -298,7 +307,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, // If we are migrating code that gets the '-fobjc-arc' flag, make sure // to remove it so that we don't get errors from normal compilation. - origCI.getLangOpts().ObjCAutoRefCount = false; + origCI.getLangOpts()->ObjCAutoRefCount = false; return capturedDiags.hasErrors() || testAct.hasReportedErrors(); } @@ -308,27 +317,31 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, //===----------------------------------------------------------------------===// static bool applyTransforms(CompilerInvocation &origCI, - StringRef Filename, InputKind Kind, + const FrontendInputFile &Input, DiagnosticConsumer *DiagClient, StringRef outputDir, bool emitPremigrationARCErrors, StringRef plistOut) { - if (!origCI.getLangOpts().ObjC1) + if (!origCI.getLangOpts()->ObjC1) return false; + LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); + // Make sure checking is successful first. CompilerInvocation CInvokForCheck(origCI); - if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient, + if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient, emitPremigrationARCErrors, plistOut)) return true; CompilerInvocation CInvok(origCI); CInvok.getFrontendOpts().Inputs.clear(); - CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename)); + CInvok.getFrontendOpts().Inputs.push_back(Input); MigrationProcess migration(CInvok, DiagClient, outputDir); + bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; - std::vector<TransformFn> transforms = arcmt::getAllTransformations(); + std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, + NoFinalizeRemoval); assert(!transforms.empty()); for (unsigned i=0, e = transforms.size(); i != e; ++i) { @@ -336,36 +349,36 @@ static bool applyTransforms(CompilerInvocation &origCI, if (err) return true; } - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); if (outputDir.empty()) { - origCI.getLangOpts().ObjCAutoRefCount = true; + origCI.getLangOpts()->ObjCAutoRefCount = true; return migration.getRemapper().overwriteOriginal(*Diags); } else { // If we are migrating code that gets the '-fobjc-arc' flag, make sure // to remove it so that we don't get errors from normal compilation. - origCI.getLangOpts().ObjCAutoRefCount = false; + origCI.getLangOpts()->ObjCAutoRefCount = false; return migration.getRemapper().flushToDisk(outputDir, *Diags); } } bool arcmt::applyTransformations(CompilerInvocation &origCI, - StringRef Filename, InputKind Kind, + const FrontendInputFile &Input, DiagnosticConsumer *DiagClient) { - return applyTransforms(origCI, Filename, Kind, DiagClient, + return applyTransforms(origCI, Input, DiagClient, StringRef(), false, StringRef()); } bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI, - StringRef Filename, InputKind Kind, + const FrontendInputFile &Input, DiagnosticConsumer *DiagClient, StringRef outputDir, bool emitPremigrationARCErrors, StringRef plistOut) { assert(!outputDir.empty() && "Expected output directory path"); - return applyTransforms(origCI, Filename, Kind, DiagClient, + return applyTransforms(origCI, Input, DiagClient, outputDir, emitPremigrationARCErrors, plistOut); } @@ -375,8 +388,8 @@ bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & DiagnosticConsumer *DiagClient) { assert(!outputDir.empty()); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); FileRemapper remapper; @@ -385,13 +398,51 @@ bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & if (err) return true; - CompilerInvocation CI; - remapper.applyMappings(CI); - remap = CI.getPreprocessorOpts().RemappedFiles; + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + remap = PPOpts.RemappedFiles; return false; } +bool arcmt::getFileRemappingsFromFileList( + std::vector<std::pair<std::string,std::string> > &remap, + ArrayRef<StringRef> remapFiles, + DiagnosticConsumer *DiagClient) { + bool hasErrorOccurred = false; + llvm::StringMap<bool> Uniquer; + + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + for (ArrayRef<StringRef>::iterator + I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { + StringRef file = *I; + + FileRemapper remapper; + bool err = remapper.initFromFile(file, *Diags, + /*ignoreIfFilesChanged=*/true); + hasErrorOccurred = hasErrorOccurred || err; + if (err) + continue; + + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + for (PreprocessorOptions::remapped_file_iterator + RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end(); + RI != RE; ++RI) { + bool &inserted = Uniquer[RI->first]; + if (inserted) + continue; + inserted = true; + remap.push_back(*RI); + } + } + + return hasErrorOccurred; +} + //===----------------------------------------------------------------------===// // CollectTransformActions. //===----------------------------------------------------------------------===// @@ -478,8 +529,8 @@ MigrationProcess::MigrationProcess(const CompilerInvocation &CI, StringRef outputDir) : OrigCI(CI), DiagClient(diagClient) { if (!outputDir.empty()) { - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); } @@ -487,28 +538,28 @@ MigrationProcess::MigrationProcess(const CompilerInvocation &CI, bool MigrationProcess::applyTransform(TransformFn trans, RewriteListener *listener) { - llvm::OwningPtr<CompilerInvocation> CInvok; + OwningPtr<CompilerInvocation> CInvok; CInvok.reset(createInvocationForMigration(OrigCI)); CInvok->getDiagnosticOpts().IgnoreWarnings = true; - Remapper.applyMappings(*CInvok); + Remapper.applyMappings(CInvok->getPreprocessorOpts()); CapturedDiagList capturedDiags; std::vector<SourceLocation> ARCMTMacroLocs; assert(DiagClient); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); // Filter of all diagnostics. CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); Diags->setClient(&errRec, /*ShouldOwnClient=*/false); - llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction; + OwningPtr<ARCMTMacroTrackerAction> ASTAction; ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); - llvm::OwningPtr<ASTUnit> Unit( + OwningPtr<ASTUnit> Unit( ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags, ASTAction.get())); if (!Unit) @@ -522,7 +573,7 @@ bool MigrationProcess::applyTransform(TransformFn trans, if (Diags->hasFatalErrorOccurred()) { Diags->Reset(); - DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); capturedDiags.reportDiagnostics(*Diags); DiagClient->EndSourceFile(); return true; @@ -533,11 +584,12 @@ bool MigrationProcess::applyTransform(TransformFn trans, // We call BeginSourceFile because DiagnosticConsumer requires that // diagnostics with source range information are emitted only in between // BeginSourceFile() and EndSourceFile(). - DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); - Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions()); + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); - MigrationPass pass(Ctx, Unit->getSema(), TA, ARCMTMacroLocs); + MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(), + Unit->getSema(), TA, ARCMTMacroLocs); trans(pass); @@ -559,25 +611,16 @@ bool MigrationProcess::applyTransform(TransformFn trans, assert(file); std::string newFname = file->getName(); newFname += "-trans"; - llvm::SmallString<512> newText; + SmallString<512> newText; llvm::raw_svector_ostream vecOS(newText); buf.write(vecOS); vecOS.flush(); llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( StringRef(newText.data(), newText.size()), newFname); - llvm::SmallString<64> filePath(file->getName()); + SmallString<64> filePath(file->getName()); Unit->getFileManager().FixupRelativePath(filePath); Remapper.remap(filePath.str(), memBuf); } return false; } - -//===----------------------------------------------------------------------===// -// isARCDiagnostic. -//===----------------------------------------------------------------------===// - -bool arcmt::isARCDiagnostic(unsigned diagID, DiagnosticsEngine &Diag) { - return Diag.getDiagnosticIDs()->getCategoryNumberForDiag(diagID) == - diag::DiagCat_Automatic_Reference_Counting_Issue; -} diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMTActions.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMTActions.cpp index dea867a..0ed36dd 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMTActions.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMTActions.cpp @@ -15,8 +15,7 @@ using namespace clang; using namespace arcmt; bool CheckAction::BeginInvocation(CompilerInstance &CI) { - if (arcmt::checkForManualIssues(CI.getInvocation(), getCurrentFile(), - getCurrentFileKind(), + if (arcmt::checkForManualIssues(CI.getInvocation(), getCurrentInput(), CI.getDiagnostics().getClient())) return false; // errors, stop the action. @@ -29,8 +28,7 @@ CheckAction::CheckAction(FrontendAction *WrappedAction) : WrapperFrontendAction(WrappedAction) {} bool ModifyAction::BeginInvocation(CompilerInstance &CI) { - return !arcmt::applyTransformations(CI.getInvocation(), - getCurrentFile(), getCurrentFileKind(), + return !arcmt::applyTransformations(CI.getInvocation(), getCurrentInput(), CI.getDiagnostics().getClient()); } @@ -39,12 +37,11 @@ ModifyAction::ModifyAction(FrontendAction *WrappedAction) bool MigrateAction::BeginInvocation(CompilerInstance &CI) { if (arcmt::migrateWithTemporaryFiles(CI.getInvocation(), - getCurrentFile(), - getCurrentFileKind(), - CI.getDiagnostics().getClient(), - MigrateDir, - EmitPremigrationARCErros, - PlistOut)) + getCurrentInput(), + CI.getDiagnostics().getClient(), + MigrateDir, + EmitPremigrationARCErros, + PlistOut)) return false; // errors, stop the action. // We only want to see diagnostics emitted by migrateWithTemporaryFiles. diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp index c6e6ce4..474ce7d 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp @@ -8,8 +8,9 @@ //===----------------------------------------------------------------------===// #include "clang/ARCMigrate/FileRemapper.h" -#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/PreprocessorOptions.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/Diagnostic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/FileSystem.h" @@ -50,9 +51,15 @@ std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged) { + std::string infoFile = getRemapInfoFile(outputDir); + return initFromFile(infoFile, Diag, ignoreIfFilesChanged); +} + +bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, + bool ignoreIfFilesChanged) { assert(FromToMappings.empty() && "initFromDisk should be called before any remap calls"); - std::string infoFile = getRemapInfoFile(outputDir); + std::string infoFile = filePath; bool fileExists = false; llvm::sys::fs::exists(infoFile, fileExists); if (!fileExists) @@ -60,9 +67,8 @@ bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; - llvm::OwningPtr<llvm::MemoryBuffer> fileBuf; - if (llvm::error_code ec = llvm::MemoryBuffer::getFile(infoFile.c_str(), - fileBuf)) + OwningPtr<llvm::MemoryBuffer> fileBuf; + if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf)) return report("Error opening file: " + infoFile, Diag); SmallVector<StringRef, 64> lines; @@ -109,8 +115,15 @@ bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { if (fs::create_directory(outputDir, existed) != llvm::errc::success) return report("Could not create directory: " + outputDir, Diag); - std::string errMsg; std::string infoFile = getRemapInfoFile(outputDir); + return flushToFile(infoFile, Diag); +} + +bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { + using namespace llvm::sys; + + std::string errMsg; + std::string infoFile = outputPath; llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, llvm::raw_fd_ostream::F_Binary); if (!errMsg.empty()) @@ -120,18 +133,18 @@ bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { const FileEntry *origFE = I->first; - llvm::SmallString<200> origPath = StringRef(origFE->getName()); + SmallString<200> origPath = StringRef(origFE->getName()); fs::make_absolute(origPath); infoOut << origPath << '\n'; infoOut << (uint64_t)origFE->getModificationTime() << '\n'; if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { - llvm::SmallString<200> newPath = StringRef(FE->getName()); + SmallString<200> newPath = StringRef(FE->getName()); fs::make_absolute(newPath); infoOut << newPath << '\n'; } else { - llvm::SmallString<64> tempPath; + SmallString<64> tempPath; tempPath = path::filename(origFE->getName()); tempPath += "-%%%%%%%%"; tempPath += path::extension(origFE->getName()); @@ -190,8 +203,7 @@ bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, return false; } -void FileRemapper::applyMappings(CompilerInvocation &CI) const { - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); +void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { for (MappingsTy::const_iterator I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { @@ -205,8 +217,7 @@ void FileRemapper::applyMappings(CompilerInvocation &CI) const { PPOpts.RetainRemappedFileBuffers = true; } -void FileRemapper::transferMappingsAndClear(CompilerInvocation &CI) { - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); +void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) { for (MappingsTy::iterator I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { @@ -269,15 +280,12 @@ void FileRemapper::resetTarget(Target &targ) { delete oldmem; } else { const FileEntry *toFE = targ.get<const FileEntry *>(); - llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator - I = ToFromMappings.find(toFE); - if (I != ToFromMappings.end()) - ToFromMappings.erase(I); + ToFromMappings.erase(toFE); } } bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { - llvm::SmallString<128> buf; + SmallString<128> buf; unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, err.toStringRef(buf)); Diag.Report(ID); diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/Internals.h b/contrib/llvm/tools/clang/lib/ARCMigrate/Internals.h index 46f3bb6..59177c4 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/Internals.h +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/Internals.h @@ -94,6 +94,8 @@ public: void reportError(StringRef error, SourceLocation loc, SourceRange range = SourceRange()); + void reportWarning(StringRef warning, SourceLocation loc, + SourceRange range = SourceRange()); void reportNote(StringRef note, SourceLocation loc, SourceRange range = SourceRange()); @@ -137,17 +139,26 @@ public: class MigrationPass { public: ASTContext &Ctx; + LangOptions::GCMode OrigGCMode; + MigratorOptions MigOptions; Sema &SemaRef; TransformActions &TA; std::vector<SourceLocation> &ARCMTMacroLocs; - MigrationPass(ASTContext &Ctx, Sema &sema, TransformActions &TA, + MigrationPass(ASTContext &Ctx, LangOptions::GCMode OrigGCMode, + Sema &sema, TransformActions &TA, std::vector<SourceLocation> &ARCMTMacroLocs) - : Ctx(Ctx), SemaRef(sema), TA(TA), ARCMTMacroLocs(ARCMTMacroLocs) { } + : Ctx(Ctx), OrigGCMode(OrigGCMode), MigOptions(), + SemaRef(sema), TA(TA), + ARCMTMacroLocs(ARCMTMacroLocs) { } + + bool isGCMigration() const { return OrigGCMode != LangOptions::NonGC; } + bool noNSAllocReallocError() const { return MigOptions.NoNSAllocReallocError; } + void setNSAllocReallocError(bool val) { MigOptions.NoNSAllocReallocError = val; } + bool noFinalizeRemoval() const { return MigOptions.NoFinalizeRemoval; } + void setNoFinalizeRemoval(bool val) {MigOptions.NoFinalizeRemoval = val; } }; -bool isARCDiagnostic(unsigned diagID, DiagnosticsEngine &Diag); - static inline StringRef getARCMTMacroName() { return "__IMPL_ARCMT_REMOVED_EXPR__"; } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp new file mode 100644 index 0000000..e635274 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp @@ -0,0 +1,226 @@ +//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMTActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/NSAPI.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Edit/Rewriters.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; +using namespace arcmt; + +namespace { + +class ObjCMigrateASTConsumer : public ASTConsumer { + void migrateDecl(Decl *D); + +public: + std::string MigrateDir; + bool MigrateLiterals; + bool MigrateSubscripting; + llvm::OwningPtr<NSAPI> NSAPIObj; + llvm::OwningPtr<edit::EditedSource> Editor; + FileRemapper &Remapper; + FileManager &FileMgr; + const PreprocessingRecord *PPRec; + bool IsOutputFile; + + ObjCMigrateASTConsumer(StringRef migrateDir, + bool migrateLiterals, + bool migrateSubscripting, + FileRemapper &remapper, + FileManager &fileMgr, + const PreprocessingRecord *PPRec, + bool isOutputFile = false) + : MigrateDir(migrateDir), + MigrateLiterals(migrateLiterals), + MigrateSubscripting(migrateSubscripting), + Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), + IsOutputFile(isOutputFile) { } + +protected: + virtual void Initialize(ASTContext &Context) { + NSAPIObj.reset(new NSAPI(Context)); + Editor.reset(new edit::EditedSource(Context.getSourceManager(), + Context.getLangOpts(), + PPRec)); + } + + virtual bool HandleTopLevelDecl(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) + migrateDecl(*I); + return true; + } + virtual void HandleInterestingDecl(DeclGroupRef DG) { + // Ignore decls from the PCH. + } + virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { + ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); + } + + virtual void HandleTranslationUnit(ASTContext &Ctx); +}; + +} + +ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, + StringRef migrateDir, + bool migrateLiterals, + bool migrateSubscripting) + : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting), + CompInst(0) { + if (MigrateDir.empty()) + MigrateDir = "."; // user current directory if none is given. +} + +ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + ASTConsumer * + WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir, + MigrateLiterals, + MigrateSubscripting, + Remapper, + CompInst->getFileManager(), + CompInst->getPreprocessor().getPreprocessingRecord()); + ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer }; + return new MultiplexConsumer(Consumers); +} + +bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { + Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), + /*ignoreIfFilesChanges=*/true); + CompInst = &CI; + CI.getDiagnostics().setIgnoreAllWarnings(true); + CI.getPreprocessorOpts().DetailedRecord = true; + CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true; + return true; +} + +namespace { +class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { + ObjCMigrateASTConsumer &Consumer; + +public: + ObjCMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } + + bool shouldVisitTemplateInstantiations() const { return false; } + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (Consumer.MigrateLiterals) { + edit::Commit commit(*Consumer.Editor); + edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit); + Consumer.Editor->commit(commit); + } + + if (Consumer.MigrateSubscripting) { + edit::Commit commit(*Consumer.Editor); + edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); + Consumer.Editor->commit(commit); + } + + return true; + } + + bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { + // Do depth first; we want to rewrite the subexpressions first so that if + // we have to move expressions we will move them already rewritten. + for (Stmt::child_range range = E->children(); range; ++range) + if (!TraverseStmt(*range)) + return false; + + return WalkUpFromObjCMessageExpr(E); + } +}; +} + +void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { + if (!D) + return; + if (isa<ObjCMethodDecl>(D)) + return; // Wait for the ObjC container declaration. + + ObjCMigrator(*this).TraverseDecl(D); +} + +namespace { + +class RewritesReceiver : public edit::EditsReceiver { + Rewriter &Rewrite; + +public: + RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } + + virtual void insert(SourceLocation loc, StringRef text) { + Rewrite.InsertText(loc, text); + } + virtual void replace(CharSourceRange range, StringRef text) { + Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); + } +}; + +} + +void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); + RewritesReceiver Rec(rewriter); + Editor->applyRewrites(Rec); + + for (Rewriter::buffer_iterator + I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { + FileID FID = I->first; + RewriteBuffer &buf = I->second; + const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); + assert(file); + llvm::SmallString<512> newText; + llvm::raw_svector_ostream vecOS(newText); + buf.write(vecOS); + vecOS.flush(); + llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(newText.data(), newText.size()), file->getName()); + llvm::SmallString<64> filePath(file->getName()); + FileMgr.FixupRelativePath(filePath); + Remapper.remap(filePath.str(), memBuf); + } + + if (IsOutputFile) { + Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); + } else { + Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); + } +} + +bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { + CI.getPreprocessorOpts().DetailedRecord = true; + CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true; + return true; +} + +ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, + /*MigrateLiterals=*/true, + /*MigrateSubscripting=*/true, + Remapper, + CI.getFileManager(), + CI.getPreprocessor().getPreprocessingRecord(), + /*isOutputFile=*/true); +} diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransARCAssign.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransARCAssign.cpp index 1f10196..cfa6da1 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransARCAssign.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransARCAssign.cpp @@ -39,6 +39,9 @@ public: ARCAssignChecker(MigrationPass &pass) : Pass(pass) { } bool VisitBinaryOperator(BinaryOperator *Exp) { + if (Exp->getType()->isDependentType()) + return true; + Expr *E = Exp->getLHS(); SourceLocation OrigLoc = E->getExprLoc(); SourceLocation Loc = OrigLoc; diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransAutoreleasePool.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransAutoreleasePool.cpp index 08561f9..8787724 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransAutoreleasePool.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransAutoreleasePool.cpp @@ -263,10 +263,6 @@ private: return checkRef(E->getLocation(), E->getDecl()->getLocation()); } - bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { - return checkRef(E->getLocation(), E->getDecl()->getLocation()); - } - bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation()); } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp index 48c0ca9..3be8132 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp @@ -38,7 +38,7 @@ namespace { class RootBlockObjCVarRewriter : public RecursiveASTVisitor<RootBlockObjCVarRewriter> { MigrationPass &Pass; - llvm::DenseSet<VarDecl *> CheckedVars; + llvm::DenseSet<VarDecl *> &VarsToChange; class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> { VarDecl *Var; @@ -48,13 +48,13 @@ class RootBlockObjCVarRewriter : BlockVarChecker(VarDecl *var) : Var(var) { } bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) { - if (BlockDeclRefExpr * - ref = dyn_cast<BlockDeclRefExpr>(castE->getSubExpr())) { + if (DeclRefExpr * + ref = dyn_cast<DeclRefExpr>(castE->getSubExpr())) { if (ref->getDecl() == Var) { if (castE->getCastKind() == CK_LValueToRValue) return true; // Using the value of the variable. if (castE->getCastKind() == CK_NoOp && castE->isLValue() && - Var->getASTContext().getLangOptions().CPlusPlus) + Var->getASTContext().getLangOpts().CPlusPlus) return true; // Binding to const C++ reference. } } @@ -62,7 +62,7 @@ class RootBlockObjCVarRewriter : return base::TraverseImplicitCastExpr(castE); } - bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + bool VisitDeclRefExpr(DeclRefExpr *E) { if (E->getDecl() == Var) return false; // The reference of the variable, and not just its value, // is needed. @@ -71,7 +71,9 @@ class RootBlockObjCVarRewriter : }; public: - RootBlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + RootBlockObjCVarRewriter(MigrationPass &pass, + llvm::DenseSet<VarDecl *> &VarsToChange) + : Pass(pass), VarsToChange(VarsToChange) { } bool VisitBlockDecl(BlockDecl *block) { SmallVector<VarDecl *, 4> BlockVars; @@ -80,7 +82,6 @@ public: I = block->capture_begin(), E = block->capture_end(); I != E; ++I) { VarDecl *var = I->getVariable(); if (I->isByRef() && - !isAlreadyChecked(var) && var->getType()->isObjCObjectPointerType() && isImplicitStrong(var->getType())) { BlockVars.push_back(var); @@ -89,32 +90,19 @@ public: for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) { VarDecl *var = BlockVars[i]; - CheckedVars.insert(var); BlockVarChecker checker(var); bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody()); - if (onlyValueOfVarIsNeeded) { - BlocksAttr *attr = var->getAttr<BlocksAttr>(); - if(!attr) - continue; - bool useWeak = canApplyWeak(Pass.Ctx, var->getType()); - SourceManager &SM = Pass.Ctx.getSourceManager(); - Transaction Trans(Pass.TA); - Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()), - "__block", - useWeak ? "__weak" : "__unsafe_unretained"); - } - + if (onlyValueOfVarIsNeeded) + VarsToChange.insert(var); + else + VarsToChange.erase(var); } return true; } private: - bool isAlreadyChecked(VarDecl *VD) { - return CheckedVars.count(VD); - } - bool isImplicitStrong(QualType ty) { if (isa<AttributedType>(ty.getTypePtr())) return false; @@ -124,19 +112,39 @@ private: class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> { MigrationPass &Pass; + llvm::DenseSet<VarDecl *> &VarsToChange; public: - BlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + BlockObjCVarRewriter(MigrationPass &pass, + llvm::DenseSet<VarDecl *> &VarsToChange) + : Pass(pass), VarsToChange(VarsToChange) { } bool TraverseBlockDecl(BlockDecl *block) { - RootBlockObjCVarRewriter(Pass).TraverseDecl(block); + RootBlockObjCVarRewriter(Pass, VarsToChange).TraverseDecl(block); return true; } }; } // anonymous namespace -void trans::rewriteBlockObjCVariable(MigrationPass &pass) { - BlockObjCVarRewriter trans(pass); - trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) { + MigrationPass &Pass = BodyCtx.getMigrationContext().Pass; + llvm::DenseSet<VarDecl *> VarsToChange; + + BlockObjCVarRewriter trans(Pass, VarsToChange); + trans.TraverseStmt(BodyCtx.getTopStmt()); + + for (llvm::DenseSet<VarDecl *>::iterator + I = VarsToChange.begin(), E = VarsToChange.end(); I != E; ++I) { + VarDecl *var = *I; + BlocksAttr *attr = var->getAttr<BlocksAttr>(); + if(!attr) + continue; + bool useWeak = canApplyWeak(Pass.Ctx, var->getType()); + SourceManager &SM = Pass.Ctx.getSourceManager(); + Transaction Trans(Pass.TA); + Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()), + "__block", + useWeak ? "__weak" : "__unsafe_unretained"); + } } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp index 3ad05e6..0fb7141 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp @@ -196,35 +196,60 @@ static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx, return true; } -static void removeDeallocMethod(MigrationPass &pass) { +static void cleanupDeallocOrFinalize(MigrationPass &pass) { ASTContext &Ctx = pass.Ctx; TransformActions &TA = pass.TA; DeclContext *DC = Ctx.getTranslationUnitDecl(); + Selector FinalizeSel = + Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> impl_iterator; for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { + ObjCMethodDecl *DeallocM = 0; + ObjCMethodDecl *FinalizeM = 0; for (ObjCImplementationDecl::instmeth_iterator MI = (*I)->instmeth_begin(), ME = (*I)->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; + if (!MD->hasBody()) + continue; + if (MD->getMethodFamily() == OMF_dealloc) { - if (MD->hasBody() && - isBodyEmpty(MD->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { - Transaction Trans(TA); - TA.remove(MD->getSourceRange()); - } - break; + DeallocM = MD; + } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { + FinalizeM = MD; + } + } + + if (DeallocM) { + if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { + Transaction Trans(TA); + TA.remove(DeallocM->getSourceRange()); + } + + if (FinalizeM) { + Transaction Trans(TA); + TA.remove(FinalizeM->getSourceRange()); + } + + } else if (FinalizeM) { + if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { + Transaction Trans(TA); + TA.remove(FinalizeM->getSourceRange()); + } else { + Transaction Trans(TA); + TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); } } } } -void trans::removeEmptyStatementsAndDealloc(MigrationPass &pass) { +void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); - removeDeallocMethod(pass); + cleanupDeallocOrFinalize(pass); for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { Transaction Trans(pass.TA); diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCAttrs.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCAttrs.cpp new file mode 100644 index 0000000..9f6066e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCAttrs.cpp @@ -0,0 +1,358 @@ +//===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/SaveAndRestore.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/TinyPtrVector.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; + +namespace { + +/// \brief Collects all the places where GC attributes __strong/__weak occur. +class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> { + MigrationContext &MigrateCtx; + bool FullyMigratable; + std::vector<ObjCPropertyDecl *> &AllProps; + + typedef RecursiveASTVisitor<GCAttrsCollector> base; +public: + GCAttrsCollector(MigrationContext &ctx, + std::vector<ObjCPropertyDecl *> &AllProps) + : MigrateCtx(ctx), FullyMigratable(false), + AllProps(AllProps) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { + handleAttr(TL); + return true; + } + + bool TraverseDecl(Decl *D) { + if (!D || D->isImplicit()) + return true; + + SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D)); + + if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) { + lookForAttribute(PropD, PropD->getTypeSourceInfo()); + AllProps.push_back(PropD); + } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { + lookForAttribute(DD, DD->getTypeSourceInfo()); + } + return base::TraverseDecl(D); + } + + void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { + if (!TInfo) + return; + TypeLoc TL = TInfo->getTypeLoc(); + while (TL) { + if (const QualifiedTypeLoc *QL = dyn_cast<QualifiedTypeLoc>(&TL)) { + TL = QL->getUnqualifiedLoc(); + } else if (const AttributedTypeLoc * + Attr = dyn_cast<AttributedTypeLoc>(&TL)) { + if (handleAttr(*Attr, D)) + break; + TL = Attr->getModifiedLoc(); + } else if (const ArrayTypeLoc *Arr = dyn_cast<ArrayTypeLoc>(&TL)) { + TL = Arr->getElementLoc(); + } else if (const PointerTypeLoc *PT = dyn_cast<PointerTypeLoc>(&TL)) { + TL = PT->getPointeeLoc(); + } else if (const ReferenceTypeLoc *RT = dyn_cast<ReferenceTypeLoc>(&TL)) + TL = RT->getPointeeLoc(); + else + break; + } + } + + bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) { + if (TL.getAttrKind() != AttributedType::attr_objc_ownership) + return false; + + SourceLocation Loc = TL.getAttrNameLoc(); + unsigned RawLoc = Loc.getRawEncoding(); + if (MigrateCtx.AttrSet.count(RawLoc)) + return true; + + ASTContext &Ctx = MigrateCtx.Pass.Ctx; + SourceManager &SM = Ctx.getSourceManager(); + if (Loc.isMacroID()) + Loc = SM.getImmediateExpansionRange(Loc).first; + SmallString<32> Buf; + bool Invalid = false; + StringRef Spell = Lexer::getSpelling( + SM.getSpellingLoc(TL.getAttrEnumOperandLoc()), + Buf, SM, Ctx.getLangOpts(), &Invalid); + if (Invalid) + return false; + MigrationContext::GCAttrOccurrence::AttrKind Kind; + if (Spell == "strong") + Kind = MigrationContext::GCAttrOccurrence::Strong; + else if (Spell == "weak") + Kind = MigrationContext::GCAttrOccurrence::Weak; + else + return false; + + MigrateCtx.AttrSet.insert(RawLoc); + MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); + MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); + + Attr.Kind = Kind; + Attr.Loc = Loc; + Attr.ModifiedType = TL.getModifiedLoc().getType(); + Attr.Dcl = D; + Attr.FullyMigratable = FullyMigratable; + return true; + } + + bool isMigratable(Decl *D) { + if (isa<TranslationUnitDecl>(D)) + return false; + + if (isInMainFile(D)) + return true; + + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + return FD->hasBody(); + + if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) + return hasObjCImpl(ContD); + + if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { + for (CXXRecordDecl::method_iterator + MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) { + if ((*MI)->isOutOfLine()) + return true; + } + return false; + } + + return isMigratable(cast<Decl>(D->getDeclContext())); + } + + static bool hasObjCImpl(Decl *D) { + if (!D) + return false; + if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) { + if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD)) + return ID->getImplementation() != 0; + if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD)) + return CD->getImplementation() != 0; + if (isa<ObjCImplDecl>(ContD)) + return true; + return false; + } + return false; + } + + bool isInMainFile(Decl *D) { + if (!D) + return false; + + for (Decl::redecl_iterator + I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I) + if (!isInMainFile((*I)->getLocation())) + return false; + + return true; + } + + bool isInMainFile(SourceLocation Loc) { + if (Loc.isInvalid()) + return false; + + SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); + return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); + } +}; + +} // anonymous namespace + +static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) { + TransformActions &TA = MigrateCtx.Pass.TA; + + for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { + MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; + if (Attr.FullyMigratable && Attr.Dcl) { + if (Attr.ModifiedType.isNull()) + continue; + if (!Attr.ModifiedType->isObjCRetainableType()) { + TA.reportError("GC managed memory will become unmanaged in ARC", + Attr.Loc); + } + } + } +} + +static void checkWeakGCAttrs(MigrationContext &MigrateCtx) { + TransformActions &TA = MigrateCtx.Pass.TA; + + for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { + MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; + if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) { + if (Attr.ModifiedType.isNull() || + !Attr.ModifiedType->isObjCRetainableType()) + continue; + if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType, + /*AllowOnUnknownClass=*/true)) { + Transaction Trans(TA); + if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding())) + TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained"); + TA.clearDiagnostic(diag::err_arc_weak_no_runtime, + diag::err_arc_unsupported_weak_class, + Attr.Loc); + } + } + } +} + +typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; + +static void checkAllAtProps(MigrationContext &MigrateCtx, + SourceLocation AtLoc, + IndivPropsTy &IndProps) { + if (IndProps.empty()) + return; + + for (IndivPropsTy::iterator + PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { + QualType T = (*PI)->getType(); + if (T.isNull() || !T->isObjCRetainableType()) + return; + } + + SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs; + bool hasWeak = false, hasStrong = false; + ObjCPropertyDecl::PropertyAttributeKind + Attrs = ObjCPropertyDecl::OBJC_PR_noattr; + for (IndivPropsTy::iterator + PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { + ObjCPropertyDecl *PD = *PI; + Attrs = PD->getPropertyAttributesAsWritten(); + TypeSourceInfo *TInfo = PD->getTypeSourceInfo(); + if (!TInfo) + return; + TypeLoc TL = TInfo->getTypeLoc(); + if (AttributedTypeLoc *ATL = dyn_cast<AttributedTypeLoc>(&TL)) { + ATLs.push_back(std::make_pair(*ATL, PD)); + if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { + hasWeak = true; + } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) + hasStrong = true; + else + return; + } + } + if (ATLs.empty()) + return; + if (hasWeak && hasStrong) + return; + + TransformActions &TA = MigrateCtx.Pass.TA; + Transaction Trans(TA); + + if (GCAttrsCollector::hasObjCImpl( + cast<Decl>(IndProps.front()->getDeclContext()))) { + if (hasWeak) + MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding()); + + } else { + StringRef toAttr = "strong"; + if (hasWeak) { + if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(), + /*AllowOnUnkwownClass=*/true)) + toAttr = "weak"; + else + toAttr = "unsafe_unretained"; + } + if (Attrs & ObjCPropertyDecl::OBJC_PR_assign) + MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc); + else + MigrateCtx.addPropertyAttribute(toAttr, AtLoc); + } + + for (unsigned i = 0, e = ATLs.size(); i != e; ++i) { + SourceLocation Loc = ATLs[i].first.getAttrNameLoc(); + if (Loc.isMacroID()) + Loc = MigrateCtx.Pass.Ctx.getSourceManager() + .getImmediateExpansionRange(Loc).first; + TA.remove(Loc); + TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc); + TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership, + ATLs[i].second->getLocation()); + MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding()); + } +} + +static void checkAllProps(MigrationContext &MigrateCtx, + std::vector<ObjCPropertyDecl *> &AllProps) { + typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; + llvm::DenseMap<unsigned, IndivPropsTy> AtProps; + + for (unsigned i = 0, e = AllProps.size(); i != e; ++i) { + ObjCPropertyDecl *PD = AllProps[i]; + if (PD->getPropertyAttributesAsWritten() & + (ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_readonly)) { + SourceLocation AtLoc = PD->getAtLoc(); + if (AtLoc.isInvalid()) + continue; + unsigned RawAt = AtLoc.getRawEncoding(); + AtProps[RawAt].push_back(PD); + } + } + + for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator + I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { + SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first); + IndivPropsTy &IndProps = I->second; + checkAllAtProps(MigrateCtx, AtLoc, IndProps); + } +} + +void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { + std::vector<ObjCPropertyDecl *> AllProps; + GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl( + MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); + + errorForGCAttrsOnNonObjC(MigrateCtx); + checkAllProps(MigrateCtx, AllProps); + checkWeakGCAttrs(MigrateCtx); +} + +void MigrationContext::dumpGCAttrs() { + llvm::errs() << "\n################\n"; + for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) { + GCAttrOccurrence &Attr = GCAttrs[i]; + llvm::errs() << "KIND: " + << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak"); + llvm::errs() << "\nLOC: "; + Attr.Loc.dump(Pass.Ctx.getSourceManager()); + llvm::errs() << "\nTYPE: "; + Attr.ModifiedType.dump(); + if (Attr.Dcl) { + llvm::errs() << "DECL:\n"; + Attr.Dcl->dump(); + } else { + llvm::errs() << "DECL: NONE"; + } + llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; + llvm::errs() << "\n----------------\n"; + } + llvm::errs() << "\n################\n"; +} diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCCalls.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCCalls.cpp new file mode 100644 index 0000000..1be9020 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransGCCalls.cpp @@ -0,0 +1,84 @@ +//===--- TransGCCalls.cpp - Tranformations to ARC mode --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; + +namespace { + +class GCCollectableCallsChecker : + public RecursiveASTVisitor<GCCollectableCallsChecker> { + MigrationContext &MigrateCtx; + ParentMap &PMap; + IdentifierInfo *NSMakeCollectableII; + IdentifierInfo *CFMakeCollectableII; + +public: + GCCollectableCallsChecker(MigrationContext &ctx, ParentMap &map) + : MigrateCtx(ctx), PMap(map) { + IdentifierTable &Ids = MigrateCtx.Pass.Ctx.Idents; + NSMakeCollectableII = &Ids.get("NSMakeCollectable"); + CFMakeCollectableII = &Ids.get("CFMakeCollectable"); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitCallExpr(CallExpr *E) { + TransformActions &TA = MigrateCtx.Pass.TA; + + if (MigrateCtx.isGCOwnedNonObjC(E->getType())) { + if (MigrateCtx.Pass.noNSAllocReallocError()) + TA.reportWarning("call returns pointer to GC managed memory; " + "it will become unmanaged in ARC", + E->getLocStart(), E->getSourceRange()); + else + TA.reportError("call returns pointer to GC managed memory; " + "it will become unmanaged in ARC", + E->getLocStart(), E->getSourceRange()); + return true; + } + + Expr *CEE = E->getCallee()->IgnoreParenImpCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) { + if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(DRE->getDecl())) { + if (!FD->getDeclContext()->getRedeclContext()->isFileContext()) + return true; + + if (FD->getIdentifier() == NSMakeCollectableII) { + Transaction Trans(TA); + TA.clearDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + diag::err_ovl_deleted_call, // ObjC++ + DRE->getSourceRange()); + TA.replace(DRE->getSourceRange(), "CFBridgingRelease"); + + } else if (FD->getIdentifier() == CFMakeCollectableII) { + TA.reportError("CFMakeCollectable will leak the object that it " + "receives in ARC", DRE->getLocation(), + DRE->getSourceRange()); + } + } + } + + return true; + } +}; + +} // anonymous namespace + +void GCCollectableCallsTraverser::traverseBody(BodyContext &BodyCtx) { + GCCollectableCallsChecker(BodyCtx.getMigrationContext(), + BodyCtx.getParentMap()) + .TraverseStmt(BodyCtx.getTopStmt()); +} diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp index ca845b6..cc85fe2 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp @@ -44,8 +44,17 @@ using namespace trans; namespace { class PropertiesRewriter { + MigrationContext &MigrateCtx; MigrationPass &Pass; ObjCImplementationDecl *CurImplD; + + enum PropActionKind { + PropAction_None, + PropAction_RetainReplacedWithStrong, + PropAction_AssignRemoved, + PropAction_AssignRewritten, + PropAction_MaybeAddWeakOrUnsafe + }; struct PropData { ObjCPropertyDecl *PropD; @@ -58,9 +67,27 @@ class PropertiesRewriter { typedef SmallVector<PropData, 2> PropsTy; typedef std::map<unsigned, PropsTy> AtPropDeclsTy; AtPropDeclsTy AtProps; + llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; public: - PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } + explicit PropertiesRewriter(MigrationContext &MigrateCtx) + : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { } + + static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps, + AtPropDeclsTy *PrevAtProps = 0) { + for (ObjCInterfaceDecl::prop_iterator + propI = D->prop_begin(), + propE = D->prop_end(); propI != propE; ++propI) { + if (propI->getAtLoc().isInvalid()) + continue; + unsigned RawLoc = propI->getAtLoc().getRawEncoding(); + if (PrevAtProps) + if (PrevAtProps->find(RawLoc) != PrevAtProps->end()) + continue; + PropsTy &props = AtProps[RawLoc]; + props.push_back(*propI); + } + } void doTransform(ObjCImplementationDecl *D) { CurImplD = D; @@ -68,14 +95,7 @@ public: if (!iface) return; - for (ObjCInterfaceDecl::prop_iterator - propI = iface->prop_begin(), - propE = iface->prop_end(); propI != propE; ++propI) { - if (propI->getAtLoc().isInvalid()) - continue; - PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; - props.push_back(*propI); - } + collectProperties(iface, AtProps); typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> prop_impl_iterator; @@ -110,19 +130,66 @@ public: I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); PropsTy &props = I->second; - QualType ty = getPropertyType(props); - if (!ty->isObjCRetainableType()) + if (!getPropertyType(props)->isObjCRetainableType()) continue; - if (hasIvarWithExplicitOwnership(props)) + if (hasIvarWithExplicitARCOwnership(props)) continue; Transaction Trans(Pass.TA); rewriteProperty(props, atLoc); } + + AtPropDeclsTy AtExtProps; + // Look through extensions. + for (ObjCCategoryDecl *Cat = iface->getCategoryList(); + Cat; Cat = Cat->getNextClassCategory()) + if (Cat->IsClassExtension()) + collectProperties(Cat, AtExtProps, &AtProps); + + for (AtPropDeclsTy::iterator + I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { + SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); + PropsTy &props = I->second; + Transaction Trans(Pass.TA); + doActionForExtensionProp(props, atLoc); + } } private: - void rewriteProperty(PropsTy &props, SourceLocation atLoc) const { + void doPropAction(PropActionKind kind, + PropsTy &props, SourceLocation atLoc, + bool markAction = true) { + if (markAction) + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + ActionOnProp[I->PropD->getIdentifier()] = kind; + + switch (kind) { + case PropAction_None: + return; + case PropAction_RetainReplacedWithStrong: { + StringRef toAttr = "strong"; + MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc); + return; + } + case PropAction_AssignRemoved: + return removeAssignForDefaultStrong(props, atLoc); + case PropAction_AssignRewritten: + return rewriteAssign(props, atLoc); + case PropAction_MaybeAddWeakOrUnsafe: + return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); + } + } + + void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { + llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; + I = ActionOnProp.find(props[0].PropD->getIdentifier()); + if (I == ActionOnProp.end()) + return; + + doPropAction(I->second, props, atLoc, false); + } + + void rewriteProperty(PropsTy &props, SourceLocation atLoc) { ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | @@ -132,79 +199,82 @@ private: return; if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { - rewriteAttribute("retain", "strong", atLoc); - return; + // strong is the default. + return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc); } + bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); + if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { - if (hasIvarAssignedAPlusOneObject(props)) { - rewriteAttribute("assign", "strong", atLoc); - return; - } - return rewriteAssign(props, atLoc); + if (HasIvarAssignedAPlusOneObject) + return doPropAction(PropAction_AssignRemoved, props, atLoc); + return doPropAction(PropAction_AssignRewritten, props, atLoc); } - if (hasIvarAssignedAPlusOneObject(props)) - return maybeAddStrongAttr(props, atLoc); + if (HasIvarAssignedAPlusOneObject || + (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) + return; // 'strong' by default. - return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); + return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); } - void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { - bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); - - bool rewroteAttr = rewriteAttribute("assign", - canUseWeak ? "weak" : "unsafe_unretained", - atLoc); - if (!rewroteAttr) - canUseWeak = false; + void removeAssignForDefaultStrong(PropsTy &props, + SourceLocation atLoc) const { + removeAttribute("retain", atLoc); + if (!removeAttribute("assign", atLoc)) + return; for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { - if (isUserDeclared(I->IvarD)) - Pass.TA.insert(I->IvarD->getLocation(), - canUseWeak ? "__weak " : "__unsafe_unretained "); if (I->ImplD) Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, I->ImplD->getLocation()); } } - void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, - SourceLocation atLoc) const { - ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), + /*AllowOnUnknownClass=*/Pass.isGCMigration()); + const char *toWhich = + (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" : + (canUseWeak ? "weak" : "unsafe_unretained"); - bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); - if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || - !hasAllIvarsBacked(props)) { - bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", - atLoc); - if (!addedAttr) - canUseWeak = false; - } + bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc); + if (!rewroteAttr) + canUseWeak = false; for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { - if (isUserDeclared(I->IvarD)) - Pass.TA.insert(I->IvarD->getLocation(), - canUseWeak ? "__weak " : "__unsafe_unretained "); - if (I->ImplD) { + if (isUserDeclared(I->IvarD)) { + if (I->IvarD && + I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) { + const char *toWhich = + (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " : + (canUseWeak ? "__weak " : "__unsafe_unretained "); + Pass.TA.insert(I->IvarD->getLocation(), toWhich); + } + } + if (I->ImplD) Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, I->ImplD->getLocation()); - Pass.TA.clearDiagnostic( - diag::err_arc_objc_property_default_assign_on_object, - I->ImplD->getLocation()); - } } } - void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const { - ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, + SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), + /*AllowOnUnknownClass=*/Pass.isGCMigration()); - if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || - !hasAllIvarsBacked(props)) { - addAttribute("strong", atLoc); - } + bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!addedAttr) + canUseWeak = false; for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) { + if (I->IvarD && + I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + } if (I->ImplD) { Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, I->ImplD->getLocation()); @@ -215,108 +285,17 @@ private: } } + bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { + return MigrateCtx.removePropertyAttribute(fromAttr, atLoc); + } + bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, SourceLocation atLoc) const { - if (atLoc.isMacroID()) - return false; - - SourceManager &SM = Pass.Ctx.getSourceManager(); - - // Break down the source location. - std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); - - // Try to load the file buffer. - bool invalidTemp = false; - StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); - if (invalidTemp) - return false; - - const char *tokenBegin = file.data() + locInfo.second; - - // Lex from the start of the given location. - Lexer lexer(SM.getLocForStartOfFile(locInfo.first), - Pass.Ctx.getLangOptions(), - file.begin(), tokenBegin, file.end()); - Token tok; - lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::at)) return false; - lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::raw_identifier)) return false; - if (StringRef(tok.getRawIdentifierData(), tok.getLength()) - != "property") - return false; - lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::l_paren)) return false; - - lexer.LexFromRawLexer(tok); - if (tok.is(tok::r_paren)) - return false; - - while (1) { - if (tok.isNot(tok::raw_identifier)) return false; - StringRef ident(tok.getRawIdentifierData(), tok.getLength()); - if (ident == fromAttr) { - Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); - return true; - } - - do { - lexer.LexFromRawLexer(tok); - } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); - if (tok.is(tok::r_paren)) - break; - lexer.LexFromRawLexer(tok); - } - - return false; + return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); } bool addAttribute(StringRef attr, SourceLocation atLoc) const { - if (atLoc.isMacroID()) - return false; - - SourceManager &SM = Pass.Ctx.getSourceManager(); - - // Break down the source location. - std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); - - // Try to load the file buffer. - bool invalidTemp = false; - StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); - if (invalidTemp) - return false; - - const char *tokenBegin = file.data() + locInfo.second; - - // Lex from the start of the given location. - Lexer lexer(SM.getLocForStartOfFile(locInfo.first), - Pass.Ctx.getLangOptions(), - file.begin(), tokenBegin, file.end()); - Token tok; - lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::at)) return false; - lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::raw_identifier)) return false; - if (StringRef(tok.getRawIdentifierData(), tok.getLength()) - != "property") - return false; - lexer.LexFromRawLexer(tok); - - if (tok.isNot(tok::l_paren)) { - Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); - return true; - } - - lexer.LexFromRawLexer(tok); - if (tok.is(tok::r_paren)) { - Pass.TA.insert(tok.getLocation(), attr); - return true; - } - - if (tok.isNot(tok::raw_identifier)) return false; - - Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); - return true; + return MigrateCtx.addPropertyAttribute(attr, atLoc); } class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { @@ -358,7 +337,10 @@ private: return false; } - bool hasIvarWithExplicitOwnership(PropsTy &props) const { + bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { + if (Pass.isGCMigration()) + return false; + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { if (isUserDeclared(I->IvarD)) { if (isa<AttributedType>(I->IvarD->getType())) @@ -380,17 +362,26 @@ private: return true; } + // \brief Returns true if all declarations in the @property have GC __weak. + bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { + if (!Pass.isGCMigration()) + return false; + if (props.empty()) + return false; + return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); + } + bool isUserDeclared(ObjCIvarDecl *ivarD) const { return ivarD && !ivarD->getSynthesize(); } QualType getPropertyType(PropsTy &props) const { assert(!props.empty()); - QualType ty = props[0].PropD->getType(); + QualType ty = props[0].PropD->getType().getUnqualifiedType(); #ifndef NDEBUG for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) - assert(ty == I->PropD->getType()); + assert(ty == I->PropD->getType().getUnqualifiedType()); #endif return ty; @@ -411,21 +402,10 @@ private: } }; -class ImplementationChecker : - public RecursiveASTVisitor<ImplementationChecker> { - MigrationPass &Pass; - -public: - ImplementationChecker(MigrationPass &pass) : Pass(pass) { } - - bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { - PropertiesRewriter(Pass).doTransform(D); - return true; - } -}; - } // anonymous namespace -void trans::rewriteProperties(MigrationPass &pass) { - ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +void PropertyRewriteTraverser::traverseObjCImplementation( + ObjCImplementationContext &ImplCtx) { + PropertiesRewriter(ImplCtx.getMigrationContext()) + .doTransform(ImplCtx.getImplementationDecl()); } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 394f848..11a6553 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -21,6 +21,8 @@ #include "Internals.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/AST/ParentMap.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" using namespace clang; using namespace arcmt; @@ -34,15 +36,17 @@ class RetainReleaseDeallocRemover : MigrationPass &Pass; ExprSet Removables; - llvm::OwningPtr<ParentMap> StmtMap; + OwningPtr<ParentMap> StmtMap; - Selector DelegateSel; + Selector DelegateSel, FinalizeSel; public: RetainReleaseDeallocRemover(MigrationPass &pass) : Body(0), Pass(pass) { DelegateSel = Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); + FinalizeSel = + Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); } void transformBody(Stmt *body) { @@ -55,6 +59,8 @@ public: bool VisitObjCMessageExpr(ObjCMessageExpr *E) { switch (E->getMethodFamily()) { default: + if (E->isInstanceMessage() && E->getSelector() == FinalizeSel) + break; return true; case OMF_autorelease: if (isRemovable(E)) { @@ -124,27 +130,105 @@ public: Transaction Trans(Pass.TA); clearDiagnostics(rec->getExprLoc()); - if (E->getMethodFamily() == OMF_release && - isRemovable(E) && isInAtFinally(E)) { + ObjCMessageExpr *Msg = E; + Expr *RecContainer = Msg; + SourceRange RecRange = rec->getSourceRange(); + checkForGCDOrXPC(Msg, RecContainer, rec, RecRange); + + if (Msg->getMethodFamily() == OMF_release && + isRemovable(RecContainer) && isInAtFinally(RecContainer)) { // Change the -release to "receiver = nil" in a finally to avoid a leak // when an exception is thrown. - Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + Pass.TA.replace(RecContainer->getSourceRange(), RecRange); std::string str = " = "; str += getNilString(Pass.Ctx); - Pass.TA.insertAfterToken(rec->getLocEnd(), str); + Pass.TA.insertAfterToken(RecRange.getEnd(), str); return true; } - if (!hasSideEffects(E, Pass.Ctx)) { - if (tryRemoving(E)) + if (!hasSideEffects(rec, Pass.Ctx)) { + if (tryRemoving(RecContainer)) return true; } - Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + Pass.TA.replace(RecContainer->getSourceRange(), RecRange); return true; } private: + /// \brief Check if the retain/release is due to a GCD/XPC macro that are + /// defined as: + /// + /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; }) + /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; }) + /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; }) + /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; }) + /// + /// and return the top container which is the StmtExpr and the macro argument + /// expression. + void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer, + Expr *&Rec, SourceRange &RecRange) { + SourceLocation Loc = Msg->getExprLoc(); + if (!Loc.isMacroID()) + return; + SourceManager &SM = Pass.Ctx.getSourceManager(); + StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, + Pass.Ctx.getLangOpts()); + bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) + .Case("dispatch_retain", true) + .Case("dispatch_release", true) + .Case("xpc_retain", true) + .Case("xpc_release", true) + .Default(false); + if (!isGCDOrXPC) + return; + + StmtExpr *StmtE = 0; + Stmt *S = Msg; + while (S) { + if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) { + StmtE = SE; + break; + } + S = StmtMap->getParent(S); + } + + if (!StmtE) + return; + + Stmt::child_range StmtExprChild = StmtE->children(); + if (!StmtExprChild) + return; + CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild); + if (!CompS) + return; + + Stmt::child_range CompStmtChild = CompS->children(); + if (!CompStmtChild) + return; + DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild); + if (!DeclS) + return; + if (!DeclS->isSingleDecl()) + return; + VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()); + if (!VD) + return; + Expr *Init = VD->getInit(); + if (!Init) + return; + + RecContainer = StmtE; + Rec = Init->IgnoreParenImpCasts(); + if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec)) + Rec = EWC->getSubExpr()->IgnoreParenImpCasts(); + RecRange = Rec->getSourceRange(); + if (SM.isMacroArgExpansion(RecRange.getBegin())) + RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin())); + if (SM.isMacroArgExpansion(RecRange.getEnd())) + RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd())); + } + void clearDiagnostics(SourceLocation loc) const { Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, diag::err_unavailable, @@ -156,12 +240,14 @@ private: if (!E) return false; E = E->IgnoreParenCasts(); + + // Also look through property-getter sugar. + if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) + E = pseudoOp->getResultExpr()->IgnoreImplicit(); + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); - if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) - return propE->getGetterSelector() == DelegateSel; - return false; } @@ -211,7 +297,7 @@ private: } // anonymous namespace -void trans::removeRetainReleaseDealloc(MigrationPass &pass) { +void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { BodyTransform<RetainReleaseDeallocRemover> trans(pass); trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp index 69fb2e8..48437c7 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -38,6 +38,7 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace arcmt; @@ -48,7 +49,7 @@ namespace { class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ MigrationPass &Pass; IdentifierInfo *SelfII; - llvm::OwningPtr<ParentMap> StmtMap; + OwningPtr<ParentMap> StmtMap; public: UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) { @@ -128,6 +129,21 @@ private: if (fname.endswith("Retain") || fname.find("Create") != StringRef::npos || fname.find("Copy") != StringRef::npos) { + // Do not migrate to couple of bridge transfer casts which + // cancel each other out. Leave it unchanged so error gets user + // attention instead. + if (FD->getName() == "CFRetain" && + FD->getNumParams() == 1 && + FD->getParent()->isTranslationUnit() && + FD->getLinkage() == ExternalLinkage) { + Expr *Arg = callE->getArg(0); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { + const Expr *sub = ICE->getSubExpr(); + QualType T = sub->getType(); + if (T->isObjCObjectPointerType()) + return; + } + } castToObjCObject(E, /*retained=*/true); return; } @@ -179,7 +195,7 @@ private: TA.insertAfterToken(CCE->getLParenLoc(), bridge); } else { SourceLocation insertLoc = E->getSubExpr()->getLocStart(); - llvm::SmallString<128> newCast; + SmallString<128> newCast; newCast += '('; newCast += bridge; newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); @@ -236,7 +252,15 @@ private: } } - if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getSubExpr())){ + Expr *subExpr = E->getSubExpr(); + + // Look through pseudo-object expressions. + if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) { + subExpr = pseudo->getResultExpr(); + assert(subExpr && "no result for pseudo-object of non-void type?"); + } + + if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) { if (implCE->getCastKind() == CK_ARCConsumeObject) return rewriteToBridgedCast(E, OBC_BridgeRetained); if (implCE->getCastKind() == CK_ARCReclaimReturnedObject) diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnusedInitDelegate.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnusedInitDelegate.cpp index e2aa6ff..60ed32a 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnusedInitDelegate.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnusedInitDelegate.cpp @@ -54,7 +54,11 @@ public: Transaction Trans(Pass.TA); Pass.TA.clearDiagnostic(diag::err_arc_unused_init_message, ME->getExprLoc()); - Pass.TA.insert(ME->getExprLoc(), "self = "); + SourceRange ExprRange = ME->getSourceRange(); + Pass.TA.insert(ExprRange.getBegin(), "if (!(self = "); + std::string retStr = ")) return "; + retStr += getNilString(Pass.Ctx); + Pass.TA.insertAfterToken(ExprRange.getEnd(), retStr); } return true; } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp index 1dbe811..d1f08aa 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp @@ -31,9 +31,13 @@ class ZeroOutInDeallocRemover : llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties; ImplicitParamDecl *SelfD; ExprSet Removables; + Selector FinalizeSel; public: - ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(0) { } + ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(0) { + FinalizeSel = + Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); + } bool VisitObjCMessageExpr(ObjCMessageExpr *ME) { ASTContext &Ctx = Pass.Ctx; @@ -74,6 +78,15 @@ public: return true; } + bool VisitPseudoObjectExpr(PseudoObjectExpr *POE) { + if (isZeroingPropIvar(POE) && isRemovable(POE)) { + Transaction Trans(Pass.TA); + Pass.TA.removeStmt(POE); + } + + return true; + } + bool VisitBinaryOperator(BinaryOperator *BOE) { if (isZeroingPropIvar(BOE) && isRemovable(BOE)) { Transaction Trans(Pass.TA); @@ -84,7 +97,8 @@ public: } bool TraverseObjCMethodDecl(ObjCMethodDecl *D) { - if (D->getMethodFamily() != OMF_dealloc) + if (D->getMethodFamily() != OMF_dealloc && + !(D->isInstanceMethod() && D->getSelector() == FinalizeSel)) return true; if (!D->hasBody()) return true; @@ -137,17 +151,21 @@ private: } bool isZeroingPropIvar(Expr *E) { - BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E); - if (!BOE) return false; + E = E->IgnoreParens(); + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) + return isZeroingPropIvar(BO); + if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E)) + return isZeroingPropIvar(PO); + return false; + } + bool isZeroingPropIvar(BinaryOperator *BOE) { if (BOE->getOpcode() == BO_Comma) return isZeroingPropIvar(BOE->getLHS()) && isZeroingPropIvar(BOE->getRHS()); if (BOE->getOpcode() != BO_Assign) - return false; - - ASTContext &Ctx = Pass.Ctx; + return false; Expr *LHS = BOE->getLHS(); if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) { @@ -167,31 +185,44 @@ private: if (!IvarBacksPropertySynthesis) return false; } - else if (ObjCPropertyRefExpr *PropRefExp = dyn_cast<ObjCPropertyRefExpr>(LHS)) { - // TODO: Using implicit property decl. - if (PropRefExp->isImplicitProperty()) - return false; - if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { - if (!SynthesizedProperties.count(PDecl)) - return false; - } - } else return false; - Expr *RHS = BOE->getRHS(); - bool RHSIsNull = RHS->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull); - if (RHSIsNull) + return isZero(BOE->getRHS()); + } + + bool isZeroingPropIvar(PseudoObjectExpr *PO) { + BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm()); + if (!BO) return false; + if (BO->getOpcode() != BO_Assign) return false; + + ObjCPropertyRefExpr *PropRefExp = + dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens()); + if (!PropRefExp) return false; + + // TODO: Using implicit property decl. + if (PropRefExp->isImplicitProperty()) + return false; + + if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { + if (!SynthesizedProperties.count(PDecl)) + return false; + } + + return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr()); + } + + bool isZero(Expr *E) { + if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull)) return true; - return isZeroingPropIvar(RHS); + return isZeroingPropIvar(E); } }; } // anonymous namespace -void trans::removeZeroOutPropsInDealloc(MigrationPass &pass) { +void trans::removeZeroOutPropsInDeallocFinalize(MigrationPass &pass) { ZeroOutInDeallocRemover trans(pass); trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransformActions.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransformActions.cpp index ec676e9..0ecfeb5 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransformActions.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransformActions.cpp @@ -122,6 +122,8 @@ public: ASTContext &ctx, Preprocessor &PP) : CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { } + ASTContext &getASTContext() { return Ctx; } + void startTransaction(); bool commitTransaction(); void abortTransaction(); @@ -674,6 +676,12 @@ void TransformActions::reportError(StringRef error, SourceLocation loc, SourceRange range) { assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() && "Errors should be emitted out of a transaction"); + + SourceManager &SM = static_cast<TransformActionsImpl*>(Impl)-> + getASTContext().getSourceManager(); + if (SM.isInSystemHeader(SM.getExpansionLoc(loc))) + return; + // FIXME: Use a custom category name to distinguish rewriter errors. std::string rewriteErr = "[rewriter] "; rewriteErr += error; @@ -684,10 +692,35 @@ void TransformActions::reportError(StringRef error, SourceLocation loc, ReportedErrors = true; } +void TransformActions::reportWarning(StringRef warning, SourceLocation loc, + SourceRange range) { + assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() && + "Warning should be emitted out of a transaction"); + + SourceManager &SM = static_cast<TransformActionsImpl*>(Impl)-> + getASTContext().getSourceManager(); + if (SM.isInSystemHeader(SM.getExpansionLoc(loc))) + return; + + // FIXME: Use a custom category name to distinguish rewriter errors. + std::string rewriterWarn = "[rewriter] "; + rewriterWarn += warning; + unsigned diagID + = Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Warning, + rewriterWarn); + Diags.Report(loc, diagID) << range; +} + void TransformActions::reportNote(StringRef note, SourceLocation loc, SourceRange range) { assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() && "Errors should be emitted out of a transaction"); + + SourceManager &SM = static_cast<TransformActionsImpl*>(Impl)-> + getASTContext().getSourceManager(); + if (SM.isInSystemHeader(SM.getExpansionLoc(loc))) + return; + // FIXME: Use a custom category name to distinguish rewriter errors. std::string rewriteNote = "[rewriter] "; rewriteNote += note; diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp index 4244faf..d342d1a 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp @@ -12,8 +12,6 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" -#include "clang/AST/ParentMap.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/Lex/Lexer.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringSwitch.h" @@ -24,62 +22,35 @@ using namespace clang; using namespace arcmt; using namespace trans; +ASTTraverser::~ASTTraverser() { } + //===----------------------------------------------------------------------===// // Helpers. //===----------------------------------------------------------------------===// -/// \brief True if the class is one that does not support weak. -static bool isClassInWeakBlacklist(ObjCInterfaceDecl *cls) { - if (!cls) +bool trans::canApplyWeak(ASTContext &Ctx, QualType type, + bool AllowOnUnknownClass) { + if (!Ctx.getLangOpts().ObjCRuntimeHasWeak) return false; - bool inList = llvm::StringSwitch<bool>(cls->getName()) - .Case("NSColorSpace", true) - .Case("NSFont", true) - .Case("NSFontPanel", true) - .Case("NSImage", true) - .Case("NSLazyBrowserCell", true) - .Case("NSWindow", true) - .Case("NSWindowController", true) - .Case("NSMenuView", true) - .Case("NSPersistentUIWindowInfo", true) - .Case("NSTableCellView", true) - .Case("NSATSTypeSetter", true) - .Case("NSATSGlyphStorage", true) - .Case("NSLineFragmentRenderingContext", true) - .Case("NSAttributeDictionary", true) - .Case("NSParagraphStyle", true) - .Case("NSTextTab", true) - .Case("NSSimpleHorizontalTypesetter", true) - .Case("_NSCachedAttributedString", true) - .Case("NSStringDrawingTextStorage", true) - .Case("NSTextView", true) - .Case("NSSubTextStorage", true) - .Default(false); - - if (inList) - return true; - - return isClassInWeakBlacklist(cls->getSuperClass()); -} - -bool trans::canApplyWeak(ASTContext &Ctx, QualType type) { - if (!Ctx.getLangOptions().ObjCRuntimeHasWeak) + QualType T = type; + if (T.isNull()) return false; - QualType T = type; + // iOS is always safe to use 'weak'. + if (Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::IOS) + AllowOnUnknownClass = true; + while (const PointerType *ptr = T->getAs<PointerType>()) T = ptr->getPointeeType(); if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) { ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl(); - if (!Class || Class->getName() == "NSObject") + if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject")) return false; // id/NSObject is not safe for weak. - if (Class->isForwardDecl()) + if (!AllowOnUnknownClass && !Class->hasDefinition()) return false; // forward classes are not verifiable, therefore not safe. if (Class->isArcWeakrefUnavailable()) return false; - if (isClassInWeakBlacklist(Class)) - return false; } return true; @@ -105,11 +76,10 @@ SourceLocation trans::findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx) { SourceManager &SM = Ctx.getSourceManager(); if (loc.isMacroID()) { - if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOptions())) + if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc)) return SourceLocation(); - loc = SM.getExpansionRange(loc).second; } - loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOptions()); + loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts()); // Break down the source location. std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); @@ -124,7 +94,7 @@ SourceLocation trans::findSemiAfterLocation(SourceLocation loc, // Lex from the start of the given location. Lexer lexer(SM.getLocForStartOfFile(locInfo.first), - Ctx.getLangOptions(), + Ctx.getLangOpts(), file.begin(), tokenBegin, file.end()); Token tok; lexer.LexFromRawLexer(tok); @@ -189,7 +159,6 @@ class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> { public: ReferenceClear(ExprSet &refs) : Refs(refs) { } bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; } - bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { Refs.erase(E); return true; } }; class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> { @@ -205,12 +174,6 @@ public: Refs.insert(E); return true; } - - bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { - if (E->getDecl() == Dcl) - Refs.insert(E); - return true; - } }; class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> { @@ -290,27 +253,290 @@ void trans::collectRemovables(Stmt *S, ExprSet &exprs) { } //===----------------------------------------------------------------------===// +// MigrationContext +//===----------------------------------------------------------------------===// + +namespace { + +class ASTTransform : public RecursiveASTVisitor<ASTTransform> { + MigrationContext &MigrateCtx; + typedef RecursiveASTVisitor<ASTTransform> base; + +public: + ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { + ObjCImplementationContext ImplCtx(MigrateCtx, D); + for (MigrationContext::traverser_iterator + I = MigrateCtx.traversers_begin(), + E = MigrateCtx.traversers_end(); I != E; ++I) + (*I)->traverseObjCImplementation(ImplCtx); + + return base::TraverseObjCImplementationDecl(D); + } + + bool TraverseStmt(Stmt *rootS) { + if (!rootS) + return true; + + BodyContext BodyCtx(MigrateCtx, rootS); + for (MigrationContext::traverser_iterator + I = MigrateCtx.traversers_begin(), + E = MigrateCtx.traversers_end(); I != E; ++I) + (*I)->traverseBody(BodyCtx); + + return true; + } +}; + +} + +MigrationContext::~MigrationContext() { + for (traverser_iterator + I = traversers_begin(), E = traversers_end(); I != E; ++I) + delete *I; +} + +bool MigrationContext::isGCOwnedNonObjC(QualType T) { + while (!T.isNull()) { + if (const AttributedType *AttrT = T->getAs<AttributedType>()) { + if (AttrT->getAttrKind() == AttributedType::attr_objc_ownership) + return !AttrT->getModifiedType()->isObjCRetainableType(); + } + + if (T->isArrayType()) + T = Pass.Ctx.getBaseElementType(T); + else if (const PointerType *PT = T->getAs<PointerType>()) + T = PT->getPointeeType(); + else if (const ReferenceType *RT = T->getAs<ReferenceType>()) + T = RT->getPointeeType(); + else + break; + } + + return false; +} + +bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr, + StringRef toAttr, + SourceLocation atLoc) { + if (atLoc.isMacroID()) + return false; + + SourceManager &SM = Pass.Ctx.getSourceManager(); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); + + // Try to load the file buffer. + bool invalidTemp = false; + StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Pass.Ctx.getLangOpts(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::at)) return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::raw_identifier)) return false; + if (StringRef(tok.getRawIdentifierData(), tok.getLength()) + != "property") + return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::l_paren)) return false; + + Token BeforeTok = tok; + Token AfterTok; + AfterTok.startToken(); + SourceLocation AttrLoc; + + lexer.LexFromRawLexer(tok); + if (tok.is(tok::r_paren)) + return false; + + while (1) { + if (tok.isNot(tok::raw_identifier)) return false; + StringRef ident(tok.getRawIdentifierData(), tok.getLength()); + if (ident == fromAttr) { + if (!toAttr.empty()) { + Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); + return true; + } + // We want to remove the attribute. + AttrLoc = tok.getLocation(); + } + + do { + lexer.LexFromRawLexer(tok); + if (AttrLoc.isValid() && AfterTok.is(tok::unknown)) + AfterTok = tok; + } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); + if (tok.is(tok::r_paren)) + break; + if (AttrLoc.isInvalid()) + BeforeTok = tok; + lexer.LexFromRawLexer(tok); + } + + if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) { + // We want to remove the attribute. + if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) { + Pass.TA.remove(SourceRange(BeforeTok.getLocation(), + AfterTok.getLocation())); + } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) { + Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation())); + } else { + Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc)); + } + + return true; + } + + return false; +} + +bool MigrationContext::addPropertyAttribute(StringRef attr, + SourceLocation atLoc) { + if (atLoc.isMacroID()) + return false; + + SourceManager &SM = Pass.Ctx.getSourceManager(); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); + + // Try to load the file buffer. + bool invalidTemp = false; + StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Pass.Ctx.getLangOpts(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::at)) return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::raw_identifier)) return false; + if (StringRef(tok.getRawIdentifierData(), tok.getLength()) + != "property") + return false; + lexer.LexFromRawLexer(tok); + + if (tok.isNot(tok::l_paren)) { + Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); + return true; + } + + lexer.LexFromRawLexer(tok); + if (tok.is(tok::r_paren)) { + Pass.TA.insert(tok.getLocation(), attr); + return true; + } + + if (tok.isNot(tok::raw_identifier)) return false; + + Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); + return true; +} + +void MigrationContext::traverse(TranslationUnitDecl *TU) { + for (traverser_iterator + I = traversers_begin(), E = traversers_end(); I != E; ++I) + (*I)->traverseTU(*this); + + ASTTransform(*this).TraverseDecl(TU); +} + +static void GCRewriteFinalize(MigrationPass &pass) { + ASTContext &Ctx = pass.Ctx; + TransformActions &TA = pass.TA; + DeclContext *DC = Ctx.getTranslationUnitDecl(); + Selector FinalizeSel = + Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); + + typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> + impl_iterator; + for (impl_iterator I = impl_iterator(DC->decls_begin()), + E = impl_iterator(DC->decls_end()); I != E; ++I) { + for (ObjCImplementationDecl::instmeth_iterator + MI = (*I)->instmeth_begin(), + ME = (*I)->instmeth_end(); MI != ME; ++MI) { + ObjCMethodDecl *MD = *MI; + if (!MD->hasBody()) + continue; + + if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { + ObjCMethodDecl *FinalizeM = MD; + Transaction Trans(TA); + TA.insert(FinalizeM->getSourceRange().getBegin(), + "#if !__has_feature(objc_arc)\n"); + CharSourceRange::getTokenRange(FinalizeM->getSourceRange()); + const SourceManager &SM = pass.Ctx.getSourceManager(); + const LangOptions &LangOpts = pass.Ctx.getLangOpts(); + bool Invalid; + std::string str = "\n#endif\n"; + str += Lexer::getSourceText( + CharSourceRange::getTokenRange(FinalizeM->getSourceRange()), + SM, LangOpts, &Invalid); + TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str); + + break; + } + } + } +} + +//===----------------------------------------------------------------------===// // getAllTransformations. //===----------------------------------------------------------------------===// +static void traverseAST(MigrationPass &pass) { + MigrationContext MigrateCtx(pass); + + if (pass.isGCMigration()) { + MigrateCtx.addTraverser(new GCCollectableCallsTraverser); + MigrateCtx.addTraverser(new GCAttrsTraverser()); + } + MigrateCtx.addTraverser(new PropertyRewriteTraverser()); + MigrateCtx.addTraverser(new BlockObjCVariableTraverser()); + + MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl()); +} + static void independentTransforms(MigrationPass &pass) { rewriteAutoreleasePool(pass); - rewriteProperties(pass); - removeRetainReleaseDealloc(pass); + removeRetainReleaseDeallocFinalize(pass); rewriteUnusedInitDelegate(pass); - removeZeroOutPropsInDealloc(pass); + removeZeroOutPropsInDeallocFinalize(pass); makeAssignARCSafe(pass); rewriteUnbridgedCasts(pass); - rewriteBlockObjCVariable(pass); checkAPIUses(pass); + traverseAST(pass); } -std::vector<TransformFn> arcmt::getAllTransformations() { +std::vector<TransformFn> arcmt::getAllTransformations( + LangOptions::GCMode OrigGCMode, + bool NoFinalizeRemoval) { std::vector<TransformFn> transforms; + if (OrigGCMode == LangOptions::GCOnly && NoFinalizeRemoval) + transforms.push_back(GCRewriteFinalize); transforms.push_back(independentTransforms); // This depends on previous transformations removing various expressions. - transforms.push_back(removeEmptyStatementsAndDealloc); + transforms.push_back(removeEmptyStatementsAndDeallocFinalize); return transforms; } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h index 5e4db56..445c3e5 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" namespace clang { @@ -25,6 +26,8 @@ namespace arcmt { namespace trans { + class MigrationContext; + //===----------------------------------------------------------------------===// // Transformations. //===----------------------------------------------------------------------===// @@ -32,21 +35,124 @@ namespace trans { void rewriteAutoreleasePool(MigrationPass &pass); void rewriteUnbridgedCasts(MigrationPass &pass); void makeAssignARCSafe(MigrationPass &pass); -void removeRetainReleaseDealloc(MigrationPass &pass); -void removeZeroOutPropsInDealloc(MigrationPass &pass); -void rewriteProperties(MigrationPass &pass); -void rewriteBlockObjCVariable(MigrationPass &pass); +void removeRetainReleaseDeallocFinalize(MigrationPass &pass); +void removeZeroOutPropsInDeallocFinalize(MigrationPass &pass); void rewriteUnusedInitDelegate(MigrationPass &pass); void checkAPIUses(MigrationPass &pass); -void removeEmptyStatementsAndDealloc(MigrationPass &pass); +void removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass); + +class BodyContext { + MigrationContext &MigrateCtx; + ParentMap PMap; + Stmt *TopStmt; + +public: + BodyContext(MigrationContext &MigrateCtx, Stmt *S) + : MigrateCtx(MigrateCtx), PMap(S), TopStmt(S) {} + + MigrationContext &getMigrationContext() { return MigrateCtx; } + ParentMap &getParentMap() { return PMap; } + Stmt *getTopStmt() { return TopStmt; } +}; + +class ObjCImplementationContext { + MigrationContext &MigrateCtx; + ObjCImplementationDecl *ImpD; + +public: + ObjCImplementationContext(MigrationContext &MigrateCtx, + ObjCImplementationDecl *D) + : MigrateCtx(MigrateCtx), ImpD(D) {} + + MigrationContext &getMigrationContext() { return MigrateCtx; } + ObjCImplementationDecl *getImplementationDecl() { return ImpD; } +}; + +class ASTTraverser { +public: + virtual ~ASTTraverser(); + virtual void traverseTU(MigrationContext &MigrateCtx) { } + virtual void traverseBody(BodyContext &BodyCtx) { } + virtual void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) {} +}; + +class MigrationContext { + std::vector<ASTTraverser *> Traversers; + +public: + MigrationPass &Pass; + + struct GCAttrOccurrence { + enum AttrKind { Weak, Strong } Kind; + SourceLocation Loc; + QualType ModifiedType; + Decl *Dcl; + /// \brief true if the attribute is owned, e.g. it is in a body and not just + /// in an interface. + bool FullyMigratable; + }; + std::vector<GCAttrOccurrence> GCAttrs; + llvm::DenseSet<unsigned> AttrSet; + llvm::DenseSet<unsigned> RemovedAttrSet; + + /// \brief Set of raw '@' locations for 'assign' properties group that contain + /// GC __weak. + llvm::DenseSet<unsigned> AtPropsWeak; + + explicit MigrationContext(MigrationPass &pass) : Pass(pass) {} + ~MigrationContext(); + + typedef std::vector<ASTTraverser *>::iterator traverser_iterator; + traverser_iterator traversers_begin() { return Traversers.begin(); } + traverser_iterator traversers_end() { return Traversers.end(); } + + void addTraverser(ASTTraverser *traverser) { + Traversers.push_back(traverser); + } + + bool isGCOwnedNonObjC(QualType T); + bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc) { + return rewritePropertyAttribute(fromAttr, StringRef(), atLoc); + } + bool rewritePropertyAttribute(StringRef fromAttr, StringRef toAttr, + SourceLocation atLoc); + bool addPropertyAttribute(StringRef attr, SourceLocation atLoc); + + void traverse(TranslationUnitDecl *TU); + + void dumpGCAttrs(); +}; + +class PropertyRewriteTraverser : public ASTTraverser { +public: + virtual void traverseObjCImplementation(ObjCImplementationContext &ImplCtx); +}; + +class BlockObjCVariableTraverser : public ASTTraverser { +public: + virtual void traverseBody(BodyContext &BodyCtx); +}; + +// GC transformations + +class GCAttrsTraverser : public ASTTraverser { +public: + virtual void traverseTU(MigrationContext &MigrateCtx); +}; + +class GCCollectableCallsTraverser : public ASTTraverser { +public: + virtual void traverseBody(BodyContext &BodyCtx); +}; //===----------------------------------------------------------------------===// // Helpers. //===----------------------------------------------------------------------===// /// \brief Determine whether we can add weak to the given type. -bool canApplyWeak(ASTContext &Ctx, QualType type); +bool canApplyWeak(ASTContext &Ctx, QualType type, + bool AllowOnUnknownClass = false); /// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. |