Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
//===--- ARCMTActions.cpp - ARC Migrate Tool Frontend Actions ---*- C++ -*-===//
//
// 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/ARCMigrate/ARCMT.h"
#include "clang/Frontend/CompilerInstance.h"
using namespace clang;
using namespace arcmt;
bool CheckAction::BeginInvocation(CompilerInstance &CI) {
if (arcmt::checkForManualIssues(CI.getInvocation(), getCurrentInput(),
CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient()))
return false; // errors, stop the action.
// We only want to see warnings reported from arcmt::checkForManualIssues.
CI.getDiagnostics().setIgnoreAllWarnings(true);
return true;
}
CheckAction::CheckAction(std::unique_ptr<FrontendAction> WrappedAction)
: WrapperFrontendAction(std::move(WrappedAction)) {}
bool ModifyAction::BeginInvocation(CompilerInstance &CI) {
return !arcmt::applyTransformations(CI.getInvocation(), getCurrentInput(),
CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient());
}
ModifyAction::ModifyAction(std::unique_ptr<FrontendAction> WrappedAction)
: WrapperFrontendAction(std::move(WrappedAction)) {}
bool MigrateAction::BeginInvocation(CompilerInstance &CI) {
if (arcmt::migrateWithTemporaryFiles(
CI.getInvocation(), getCurrentInput(), CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient(), MigrateDir, EmitPremigrationARCErros,
PlistOut))
return false; // errors, stop the action.
// We only want to see diagnostics emitted by migrateWithTemporaryFiles.
CI.getDiagnostics().setIgnoreAllWarnings(true);
return true;
}
MigrateAction::MigrateAction(std::unique_ptr<FrontendAction> WrappedAction,
StringRef migrateDir,
StringRef plistOut,
bool emitPremigrationARCErrors)
: WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
PlistOut(plistOut), EmitPremigrationARCErros(emitPremigrationARCErrors) {
if (MigrateDir.empty())
MigrateDir = "."; // user current directory if none is given.
}

View File

@@ -0,0 +1,38 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangARCMigrate
ARCMT.cpp
ARCMTActions.cpp
FileRemapper.cpp
ObjCMT.cpp
PlistReporter.cpp
TransAPIUses.cpp
TransARCAssign.cpp
TransAutoreleasePool.cpp
TransBlockObjCVariable.cpp
TransEmptyStatementsAndDealloc.cpp
TransGCAttrs.cpp
TransGCCalls.cpp
TransProperties.cpp
TransProtectedScope.cpp
TransRetainReleaseDealloc.cpp
TransUnbridgedCasts.cpp
TransUnusedInitDelegate.cpp
TransZeroOutPropsInDealloc.cpp
TransformActions.cpp
Transforms.cpp
LINK_LIBS
clangAST
clangAnalysis
clangBasic
clangEdit
clangFrontend
clangLex
clangRewrite
clangSema
clangSerialization
clangStaticAnalyzerCheckers
)

View File

@@ -0,0 +1,257 @@
//===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/ARCMigrate/FileRemapper.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
using namespace clang;
using namespace arcmt;
FileRemapper::FileRemapper() {
FileMgr.reset(new FileManager(FileSystemOptions()));
}
FileRemapper::~FileRemapper() {
clear();
}
void FileRemapper::clear(StringRef outputDir) {
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
resetTarget(I->second);
FromToMappings.clear();
assert(ToFromMappings.empty());
if (!outputDir.empty()) {
std::string infoFile = getRemapInfoFile(outputDir);
llvm::sys::fs::remove(infoFile);
}
}
std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
assert(!outputDir.empty());
SmallString<128> InfoFile = outputDir;
llvm::sys::path::append(InfoFile, "remap");
return InfoFile.str();
}
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 = filePath;
if (!llvm::sys::fs::exists(infoFile))
return false;
std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
llvm::MemoryBuffer::getFile(infoFile);
if (!fileBuf)
return report("Error opening file: " + infoFile, Diag);
SmallVector<StringRef, 64> lines;
fileBuf.get()->getBuffer().split(lines, "\n");
for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
StringRef fromFilename = lines[idx];
unsigned long long timeModified;
if (lines[idx+1].getAsInteger(10, timeModified))
return report("Invalid file data: '" + lines[idx+1] + "' not a number",
Diag);
StringRef toFilename = lines[idx+2];
const FileEntry *origFE = FileMgr->getFile(fromFilename);
if (!origFE) {
if (ignoreIfFilesChanged)
continue;
return report("File does not exist: " + fromFilename, Diag);
}
const FileEntry *newFE = FileMgr->getFile(toFilename);
if (!newFE) {
if (ignoreIfFilesChanged)
continue;
return report("File does not exist: " + toFilename, Diag);
}
if ((uint64_t)origFE->getModificationTime() != timeModified) {
if (ignoreIfFilesChanged)
continue;
return report("File was modified: " + fromFilename, Diag);
}
pairs.push_back(std::make_pair(origFE, newFE));
}
for (unsigned i = 0, e = pairs.size(); i != e; ++i)
remap(pairs[i].first, pairs[i].second);
return false;
}
bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
using namespace llvm::sys;
if (fs::create_directory(outputDir))
return report("Could not create directory: " + outputDir, Diag);
std::string infoFile = getRemapInfoFile(outputDir);
return flushToFile(infoFile, Diag);
}
bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
using namespace llvm::sys;
std::error_code EC;
std::string infoFile = outputPath;
llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None);
if (EC)
return report(EC.message(), Diag);
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
const FileEntry *origFE = I->first;
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 *>()) {
SmallString<200> newPath = StringRef(FE->getName());
fs::make_absolute(newPath);
infoOut << newPath << '\n';
} else {
SmallString<64> tempPath;
int fd;
if (fs::createTemporaryFile(path::filename(origFE->getName()),
path::extension(origFE->getName()).drop_front(), fd,
tempPath))
return report("Could not create file: " + tempPath.str(), Diag);
llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
newOut.write(mem->getBufferStart(), mem->getBufferSize());
newOut.close();
const FileEntry *newE = FileMgr->getFile(tempPath);
remap(origFE, newE);
infoOut << newE->getName() << '\n';
}
}
infoOut.close();
return false;
}
bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
StringRef outputDir) {
using namespace llvm::sys;
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
const FileEntry *origFE = I->first;
assert(I->second.is<llvm::MemoryBuffer *>());
if (!fs::exists(origFE->getName()))
return report(StringRef("File does not exist: ") + origFE->getName(),
Diag);
std::error_code EC;
llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None);
if (EC)
return report(EC.message(), Diag);
llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
Out.write(mem->getBufferStart(), mem->getBufferSize());
Out.close();
}
clear(outputDir);
return false;
}
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 *>()) {
PPOpts.addRemappedFile(I->first->getName(), FE->getName());
} else {
llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
PPOpts.addRemappedFile(I->first->getName(), mem);
}
}
PPOpts.RetainRemappedFileBuffers = true;
}
void FileRemapper::remap(StringRef filePath,
std::unique_ptr<llvm::MemoryBuffer> memBuf) {
remap(getOriginalFile(filePath), std::move(memBuf));
}
void FileRemapper::remap(const FileEntry *file,
std::unique_ptr<llvm::MemoryBuffer> memBuf) {
assert(file);
Target &targ = FromToMappings[file];
resetTarget(targ);
targ = memBuf.release();
}
void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
assert(file && newfile);
Target &targ = FromToMappings[file];
resetTarget(targ);
targ = newfile;
ToFromMappings[newfile] = file;
}
const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
const FileEntry *file = FileMgr->getFile(filePath);
// If we are updating a file that overriden an original file,
// actually update the original file.
llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
I = ToFromMappings.find(file);
if (I != ToFromMappings.end()) {
file = I->second;
assert(FromToMappings.find(file) != FromToMappings.end() &&
"Original file not in mappings!");
}
return file;
}
void FileRemapper::resetTarget(Target &targ) {
if (!targ)
return;
if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
delete oldmem;
} else {
const FileEntry *toFE = targ.get<const FileEntry *>();
ToFromMappings.erase(toFE);
}
}
bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
<< err.str();
return true;
}

View File

@@ -0,0 +1,181 @@
//===-- Internals.h - Implementation Details---------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
#define LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
#include "clang/ARCMigrate/ARCMT.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include <list>
namespace clang {
class Sema;
class Stmt;
namespace arcmt {
class CapturedDiagList {
typedef std::list<StoredDiagnostic> ListTy;
ListTy List;
public:
void push_back(const StoredDiagnostic &diag) { List.push_back(diag); }
bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
bool hasDiagnostic(ArrayRef<unsigned> IDs, SourceRange range) const;
void reportDiagnostics(DiagnosticsEngine &diags) const;
bool hasErrors() const;
typedef ListTy::const_iterator iterator;
iterator begin() const { return List.begin(); }
iterator end() const { return List.end(); }
};
void writeARCDiagsToPlist(const std::string &outPath,
ArrayRef<StoredDiagnostic> diags,
SourceManager &SM, const LangOptions &LangOpts);
class TransformActions {
DiagnosticsEngine &Diags;
CapturedDiagList &CapturedDiags;
void *Impl; // TransformActionsImpl.
public:
TransformActions(DiagnosticsEngine &diag, CapturedDiagList &capturedDiags,
ASTContext &ctx, Preprocessor &PP);
~TransformActions();
void startTransaction();
bool commitTransaction();
void abortTransaction();
void insert(SourceLocation loc, StringRef text);
void insertAfterToken(SourceLocation loc, StringRef text);
void remove(SourceRange range);
void removeStmt(Stmt *S);
void replace(SourceRange range, StringRef text);
void replace(SourceRange range, SourceRange replacementRange);
void replaceStmt(Stmt *S, StringRef text);
void replaceText(SourceLocation loc, StringRef text,
StringRef replacementText);
void increaseIndentation(SourceRange range,
SourceLocation parentIndent);
bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
bool clearAllDiagnostics(SourceRange range) {
return clearDiagnostic(None, range);
}
bool clearDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
unsigned IDs[] = { ID1, ID2 };
return clearDiagnostic(IDs, range);
}
bool clearDiagnostic(unsigned ID1, unsigned ID2, unsigned ID3,
SourceRange range) {
unsigned IDs[] = { ID1, ID2, ID3 };
return clearDiagnostic(IDs, range);
}
bool hasDiagnostic(unsigned ID, SourceRange range) {
return CapturedDiags.hasDiagnostic(ID, range);
}
bool hasDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
unsigned IDs[] = { ID1, ID2 };
return CapturedDiags.hasDiagnostic(IDs, range);
}
DiagnosticBuilder report(SourceLocation loc, unsigned diagId,
SourceRange range = SourceRange());
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());
bool hasReportedErrors() const {
return Diags.hasUnrecoverableErrorOccurred();
}
class RewriteReceiver {
public:
virtual ~RewriteReceiver();
virtual void insert(SourceLocation loc, StringRef text) = 0;
virtual void remove(CharSourceRange range) = 0;
virtual void increaseIndentation(CharSourceRange range,
SourceLocation parentIndent) = 0;
};
void applyRewrites(RewriteReceiver &receiver);
};
class Transaction {
TransformActions &TA;
bool Aborted;
public:
Transaction(TransformActions &TA) : TA(TA), Aborted(false) {
TA.startTransaction();
}
~Transaction() {
if (!isAborted())
TA.commitTransaction();
}
void abort() {
TA.abortTransaction();
Aborted = true;
}
bool isAborted() const { return Aborted; }
};
class MigrationPass {
public:
ASTContext &Ctx;
LangOptions::GCMode OrigGCMode;
MigratorOptions MigOptions;
Sema &SemaRef;
TransformActions &TA;
const CapturedDiagList &CapturedDiags;
std::vector<SourceLocation> &ARCMTMacroLocs;
Optional<bool> EnableCFBridgeFns;
MigrationPass(ASTContext &Ctx, LangOptions::GCMode OrigGCMode,
Sema &sema, TransformActions &TA,
const CapturedDiagList &capturedDiags,
std::vector<SourceLocation> &ARCMTMacroLocs)
: Ctx(Ctx), OrigGCMode(OrigGCMode), MigOptions(),
SemaRef(sema), TA(TA), CapturedDiags(capturedDiags),
ARCMTMacroLocs(ARCMTMacroLocs) { }
const CapturedDiagList &getDiags() const { return CapturedDiags; }
bool isGCMigration() const { return OrigGCMode != LangOptions::NonGC; }
bool noFinalizeRemoval() const { return MigOptions.NoFinalizeRemoval; }
void setNoFinalizeRemoval(bool val) {MigOptions.NoFinalizeRemoval = val; }
bool CFBridgingFunctionsDefined();
};
static inline StringRef getARCMTMacroName() {
return "__IMPL_ARCMT_REMOVED_EXPR__";
}
} // end namespace arcmt
} // end namespace clang
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
//===--- PlistReporter.cpp - ARC Migrate Tool Plist Reporter ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Internals.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/PlistSupport.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace arcmt;
using namespace markup;
static StringRef getLevelName(DiagnosticsEngine::Level Level) {
switch (Level) {
case DiagnosticsEngine::Ignored:
llvm_unreachable("ignored");
case DiagnosticsEngine::Note:
return "note";
case DiagnosticsEngine::Remark:
case DiagnosticsEngine::Warning:
return "warning";
case DiagnosticsEngine::Fatal:
case DiagnosticsEngine::Error:
return "error";
}
llvm_unreachable("Invalid DiagnosticsEngine level!");
}
void arcmt::writeARCDiagsToPlist(const std::string &outPath,
ArrayRef<StoredDiagnostic> diags,
SourceManager &SM,
const LangOptions &LangOpts) {
DiagnosticIDs DiagIDs;
// Build up a set of FIDs that we use by scanning the locations and
// ranges of the diagnostics.
FIDMap FM;
SmallVector<FileID, 10> Fids;
for (ArrayRef<StoredDiagnostic>::iterator
I = diags.begin(), E = diags.end(); I != E; ++I) {
const StoredDiagnostic &D = *I;
AddFID(FM, Fids, SM, D.getLocation());
for (StoredDiagnostic::range_iterator
RI = D.range_begin(), RE = D.range_end(); RI != RE; ++RI) {
AddFID(FM, Fids, SM, RI->getBegin());
AddFID(FM, Fids, SM, RI->getEnd());
}
}
std::error_code EC;
llvm::raw_fd_ostream o(outPath, EC, llvm::sys::fs::F_Text);
if (EC) {
llvm::errs() << "error: could not create file: " << outPath << '\n';
return;
}
EmitPlistHeader(o);
// Write the root object: a <dict> containing...
// - "files", an <array> mapping from FIDs to file names
// - "diagnostics", an <array> containing the diagnostics
o << "<dict>\n"
" <key>files</key>\n"
" <array>\n";
for (FileID FID : Fids)
EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n';
o << " </array>\n"
" <key>diagnostics</key>\n"
" <array>\n";
for (ArrayRef<StoredDiagnostic>::iterator
DI = diags.begin(), DE = diags.end(); DI != DE; ++DI) {
const StoredDiagnostic &D = *DI;
if (D.getLevel() == DiagnosticsEngine::Ignored)
continue;
o << " <dict>\n";
// Output the diagnostic.
o << " <key>description</key>";
EmitString(o, D.getMessage()) << '\n';
o << " <key>category</key>";
EmitString(o, DiagIDs.getCategoryNameFromID(
DiagIDs.getCategoryNumberForDiag(D.getID()))) << '\n';
o << " <key>type</key>";
EmitString(o, getLevelName(D.getLevel())) << '\n';
// Output the location of the bug.
o << " <key>location</key>\n";
EmitLocation(o, SM, D.getLocation(), FM, 2);
// Output the ranges (if any).
if (!D.getRanges().empty()) {
o << " <key>ranges</key>\n";
o << " <array>\n";
for (auto &R : D.getRanges()) {
CharSourceRange ExpansionRange(SM.getExpansionRange(R.getAsRange()),
R.isTokenRange());
EmitRange(o, SM, Lexer::getAsCharRange(ExpansionRange, SM, LangOpts),
FM, 4);
}
o << " </array>\n";
}
// Close up the entry.
o << " </dict>\n";
}
o << " </array>\n";
// Finish.
o << "</dict>\n</plist>";
}

View File

@@ -0,0 +1,108 @@
//===--- TransAPIUses.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.
//
//===----------------------------------------------------------------------===//
//
// checkAPIUses:
//
// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
//
// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
// with __unsafe_unretained objects.
// - Calling -zone gets replaced with 'nil'.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class APIChecker : public RecursiveASTVisitor<APIChecker> {
MigrationPass &Pass;
Selector getReturnValueSel, setReturnValueSel;
Selector getArgumentSel, setArgumentSel;
Selector zoneSel;
public:
APIChecker(MigrationPass &pass) : Pass(pass) {
SelectorTable &sels = Pass.Ctx.Selectors;
IdentifierTable &ids = Pass.Ctx.Idents;
getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
IdentifierInfo *selIds[2];
selIds[0] = &ids.get("getArgument");
selIds[1] = &ids.get("atIndex");
getArgumentSel = sels.getSelector(2, selIds);
selIds[0] = &ids.get("setArgument");
setArgumentSel = sels.getSelector(2, selIds);
zoneSel = sels.getNullarySelector(&ids.get("zone"));
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
// NSInvocation.
if (E->isInstanceMessage() &&
E->getReceiverInterface() &&
E->getReceiverInterface()->getName() == "NSInvocation") {
StringRef selName;
if (E->getSelector() == getReturnValueSel)
selName = "getReturnValue";
else if (E->getSelector() == setReturnValueSel)
selName = "setReturnValue";
else if (E->getSelector() == getArgumentSel)
selName = "getArgument";
else if (E->getSelector() == setArgumentSel)
selName = "setArgument";
else
return true;
Expr *parm = E->getArg(0)->IgnoreParenCasts();
QualType pointee = parm->getType()->getPointeeType();
if (pointee.isNull())
return true;
if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone)
Pass.TA.report(parm->getLocStart(),
diag::err_arcmt_nsinvocation_ownership,
parm->getSourceRange())
<< selName;
return true;
}
// -zone.
if (E->isInstanceMessage() &&
E->getInstanceReceiver() &&
E->getSelector() == zoneSel &&
Pass.TA.hasDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0))) {
// Calling -zone is meaningless in ARC, change it to nil.
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0));
Pass.TA.replace(E->getSourceRange(), getNilString(Pass));
}
return true;
}
};
} // anonymous namespace
void trans::checkAPIUses(MigrationPass &pass) {
APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,78 @@
//===--- TransARCAssign.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.
//
//===----------------------------------------------------------------------===//
//
// makeAssignARCSafe:
//
// Add '__strong' where appropriate.
//
// for (id x in collection) {
// x = 0;
// }
// ---->
// for (__strong id x in collection) {
// x = 0;
// }
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ARCAssignChecker : public RecursiveASTVisitor<ARCAssignChecker> {
MigrationPass &Pass;
llvm::DenseSet<VarDecl *> ModifiedVars;
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;
DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts());
if (declRef && isa<VarDecl>(declRef->getDecl())) {
ASTContext &Ctx = Pass.Ctx;
Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(Ctx, &Loc);
if (IsLV != Expr::MLV_ConstQualified)
return true;
VarDecl *var = cast<VarDecl>(declRef->getDecl());
if (var->isARCPseudoStrong()) {
Transaction Trans(Pass.TA);
if (Pass.TA.clearDiagnostic(diag::err_typecheck_arr_assign_enumeration,
Exp->getOperatorLoc())) {
if (!ModifiedVars.count(var)) {
TypeLoc TLoc = var->getTypeSourceInfo()->getTypeLoc();
Pass.TA.insert(TLoc.getBeginLoc(), "__strong ");
ModifiedVars.insert(var);
}
}
}
}
return true;
}
};
} // anonymous namespace
void trans::makeAssignARCSafe(MigrationPass &pass) {
ARCAssignChecker assignCheck(pass);
assignCheck.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,435 @@
//===--- TransAutoreleasePool.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.
//
//===----------------------------------------------------------------------===//
//
// rewriteAutoreleasePool:
//
// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
//
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ...
// [pool release];
// ---->
// @autorelease {
// ...
// }
//
// An NSAutoreleasePool will not be touched if:
// - There is not a corresponding -release/-drain in the same scope
// - Not all references of the NSAutoreleasePool variable can be removed
// - There is a variable that is declared inside the intended @autorelease scope
// which is also used outside it.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Sema/SemaDiagnostic.h"
#include <map>
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
Decl *Dcl;
SmallVectorImpl<ObjCMessageExpr *> &Releases;
public:
ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
: Dcl(D), Releases(releases) { }
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
if (!E->isInstanceMessage())
return true;
if (E->getMethodFamily() != OMF_release)
return true;
Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
if (DE->getDecl() == Dcl)
Releases.push_back(E);
}
return true;
}
};
}
namespace {
class AutoreleasePoolRewriter
: public RecursiveASTVisitor<AutoreleasePoolRewriter> {
public:
AutoreleasePoolRewriter(MigrationPass &pass)
: Body(nullptr), Pass(pass) {
PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
DrainSel = pass.Ctx.Selectors.getNullarySelector(
&pass.Ctx.Idents.get("drain"));
}
void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
TraverseStmt(body);
}
~AutoreleasePoolRewriter() {
SmallVector<VarDecl *, 8> VarsToHandle;
for (std::map<VarDecl *, PoolVarInfo>::iterator
I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
VarDecl *var = I->first;
PoolVarInfo &info = I->second;
// Check that we can handle/rewrite all references of the pool.
clearRefsIn(info.Dcl, info.Refs);
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
clearRefsIn(*scope.Begin, info.Refs);
clearRefsIn(*scope.End, info.Refs);
clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
}
// Even if one reference is not handled we will not do anything about that
// pool variable.
if (info.Refs.empty())
VarsToHandle.push_back(var);
}
for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
PoolVarInfo &info = PoolVars[VarsToHandle[i]];
Transaction Trans(Pass.TA);
clearUnavailableDiags(info.Dcl);
Pass.TA.removeStmt(info.Dcl);
// Add "@autoreleasepool { }"
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
clearUnavailableDiags(*scope.Begin);
clearUnavailableDiags(*scope.End);
if (scope.IsFollowedBySimpleReturnStmt) {
// Include the return in the scope.
Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
Pass.TA.removeStmt(*scope.End);
Stmt::child_iterator retI = scope.End;
++retI;
SourceLocation afterSemi = findLocationAfterSemi((*retI)->getLocEnd(),
Pass.Ctx);
assert(afterSemi.isValid() &&
"Didn't we check before setting IsFollowedBySimpleReturnStmt "
"to true?");
Pass.TA.insertAfterToken(afterSemi, "\n}");
Pass.TA.increaseIndentation(
SourceRange(scope.getIndentedRange().getBegin(),
(*retI)->getLocEnd()),
scope.CompoundParent->getLocStart());
} else {
Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
Pass.TA.replaceStmt(*scope.End, "}");
Pass.TA.increaseIndentation(scope.getIndentedRange(),
scope.CompoundParent->getLocStart());
}
}
// Remove rest of pool var references.
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
for (SmallVectorImpl<ObjCMessageExpr *>::iterator
relI = scope.Releases.begin(),
relE = scope.Releases.end(); relI != relE; ++relI) {
clearUnavailableDiags(*relI);
Pass.TA.removeStmt(*relI);
}
}
}
}
bool VisitCompoundStmt(CompoundStmt *S) {
SmallVector<PoolScope, 4> Scopes;
for (Stmt::child_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
Stmt *child = getEssential(*I);
if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
if (DclS->isSingleDecl()) {
if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
if (isNSAutoreleasePool(VD->getType())) {
PoolVarInfo &info = PoolVars[VD];
info.Dcl = DclS;
collectRefs(VD, S, info.Refs);
// Does this statement follow the pattern:
// NSAutoreleasePool * pool = [NSAutoreleasePool new];
if (isPoolCreation(VD->getInit())) {
Scopes.push_back(PoolScope());
Scopes.back().PoolVar = VD;
Scopes.back().CompoundParent = S;
Scopes.back().Begin = I;
}
}
}
}
} else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
// Does this statement follow the pattern:
// pool = [NSAutoreleasePool new];
if (isNSAutoreleasePool(VD->getType()) &&
isPoolCreation(bop->getRHS())) {
Scopes.push_back(PoolScope());
Scopes.back().PoolVar = VD;
Scopes.back().CompoundParent = S;
Scopes.back().Begin = I;
}
}
}
}
if (Scopes.empty())
continue;
if (isPoolDrain(Scopes.back().PoolVar, child)) {
PoolScope &scope = Scopes.back();
scope.End = I;
handlePoolScope(scope, S);
Scopes.pop_back();
}
}
return true;
}
private:
void clearUnavailableDiags(Stmt *S) {
if (S)
Pass.TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
S->getSourceRange());
}
struct PoolScope {
VarDecl *PoolVar;
CompoundStmt *CompoundParent;
Stmt::child_iterator Begin;
Stmt::child_iterator End;
bool IsFollowedBySimpleReturnStmt;
SmallVector<ObjCMessageExpr *, 4> Releases;
PoolScope() : PoolVar(nullptr), CompoundParent(nullptr), Begin(), End(),
IsFollowedBySimpleReturnStmt(false) { }
SourceRange getIndentedRange() const {
Stmt::child_iterator rangeS = Begin;
++rangeS;
if (rangeS == End)
return SourceRange();
Stmt::child_iterator rangeE = Begin;
for (Stmt::child_iterator I = rangeS; I != End; ++I)
++rangeE;
return SourceRange((*rangeS)->getLocStart(), (*rangeE)->getLocEnd());
}
};
class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
ASTContext &Ctx;
SourceRange ScopeRange;
SourceLocation &referenceLoc, &declarationLoc;
public:
NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
SourceLocation &referenceLoc,
SourceLocation &declarationLoc)
: Ctx(ctx), referenceLoc(referenceLoc),
declarationLoc(declarationLoc) {
ScopeRange = SourceRange((*scope.Begin)->getLocStart(),
(*scope.End)->getLocStart());
}
bool VisitDeclRefExpr(DeclRefExpr *E) {
return checkRef(E->getLocation(), E->getDecl()->getLocation());
}
bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
}
bool VisitTagTypeLoc(TagTypeLoc TL) {
return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
}
private:
bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
if (isInScope(declLoc)) {
referenceLoc = refLoc;
declarationLoc = declLoc;
return false;
}
return true;
}
bool isInScope(SourceLocation loc) {
if (loc.isInvalid())
return false;
SourceManager &SM = Ctx.getSourceManager();
if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
return false;
return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
}
};
void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
// Check that all names declared inside the scope are not used
// outside the scope.
{
bool nameUsedOutsideScope = false;
SourceLocation referenceLoc, declarationLoc;
Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
++SI;
// Check if the autoreleasepool scope is followed by a simple return
// statement, in which case we will include the return in the scope.
if (SI != SE)
if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
if ((retS->getRetValue() == nullptr ||
isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
findLocationAfterSemi(retS->getLocEnd(), Pass.Ctx).isValid()) {
scope.IsFollowedBySimpleReturnStmt = true;
++SI; // the return will be included in scope, don't check it.
}
for (; SI != SE; ++SI) {
nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
referenceLoc,
declarationLoc).TraverseStmt(*SI);
if (nameUsedOutsideScope)
break;
}
// If not all references were cleared it means some variables/typenames/etc
// declared inside the pool scope are used outside of it.
// We won't try to rewrite the pool.
if (nameUsedOutsideScope) {
Pass.TA.reportError("a name is referenced outside the "
"NSAutoreleasePool scope that it was declared in", referenceLoc);
Pass.TA.reportNote("name declared here", declarationLoc);
Pass.TA.reportNote("intended @autoreleasepool scope begins here",
(*scope.Begin)->getLocStart());
Pass.TA.reportNote("intended @autoreleasepool scope ends here",
(*scope.End)->getLocStart());
return;
}
}
// Collect all releases of the pool; they will be removed.
{
ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
Stmt::child_iterator I = scope.Begin;
++I;
for (; I != scope.End; ++I)
releaseColl.TraverseStmt(*I);
}
PoolVars[scope.PoolVar].Scopes.push_back(scope);
}
bool isPoolCreation(Expr *E) {
if (!E) return false;
E = getEssential(E);
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
if (!ME) return false;
if (ME->getMethodFamily() == OMF_new &&
ME->getReceiverKind() == ObjCMessageExpr::Class &&
isNSAutoreleasePool(ME->getReceiverInterface()))
return true;
if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
ME->getMethodFamily() == OMF_init) {
Expr *rec = getEssential(ME->getInstanceReceiver());
if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
if (recME->getMethodFamily() == OMF_alloc &&
recME->getReceiverKind() == ObjCMessageExpr::Class &&
isNSAutoreleasePool(recME->getReceiverInterface()))
return true;
}
}
return false;
}
bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
if (!S) return false;
S = getEssential(S);
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
if (!ME) return false;
if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
Expr *rec = getEssential(ME->getInstanceReceiver());
if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
if (dref->getDecl() == poolVar)
return ME->getMethodFamily() == OMF_release ||
ME->getSelector() == DrainSel;
}
return false;
}
bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
return IDecl && IDecl->getIdentifier() == PoolII;
}
bool isNSAutoreleasePool(QualType Ty) {
QualType pointee = Ty->getPointeeType();
if (pointee.isNull())
return false;
if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
return isNSAutoreleasePool(interT->getDecl());
return false;
}
static Expr *getEssential(Expr *E) {
return cast<Expr>(getEssential((Stmt*)E));
}
static Stmt *getEssential(Stmt *S) {
if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S))
S = EWC->getSubExpr();
if (Expr *E = dyn_cast<Expr>(S))
S = E->IgnoreParenCasts();
return S;
}
Stmt *Body;
MigrationPass &Pass;
IdentifierInfo *PoolII;
Selector DrainSel;
struct PoolVarInfo {
DeclStmt *Dcl;
ExprSet Refs;
SmallVector<PoolScope, 2> Scopes;
PoolVarInfo() : Dcl(nullptr) { }
};
std::map<VarDecl *, PoolVarInfo> PoolVars;
};
} // anonymous namespace
void trans::rewriteAutoreleasePool(MigrationPass &pass) {
BodyTransform<AutoreleasePoolRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,147 @@
//===--- TransBlockObjCVariable.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.
//
//===----------------------------------------------------------------------===//
//
// rewriteBlockObjCVariable:
//
// Adding __block to an obj-c variable could be either because the variable
// is used for output storage or the user wanted to break a retain cycle.
// This transformation checks whether a reference of the variable for the block
// is actually needed (it is assigned to or its address is taken) or not.
// If the reference is not needed it will assume __block was added to break a
// cycle so it will remove '__block' and add __weak/__unsafe_unretained.
// e.g
//
// __block Foo *x;
// bar(^ { [x cake]; });
// ---->
// __weak Foo *x;
// bar(^ { [x cake]; });
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class RootBlockObjCVarRewriter :
public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
llvm::DenseSet<VarDecl *> &VarsToChange;
class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
VarDecl *Var;
typedef RecursiveASTVisitor<BlockVarChecker> base;
public:
BlockVarChecker(VarDecl *var) : Var(var) { }
bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) {
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().getLangOpts().CPlusPlus)
return true; // Binding to const C++ reference.
}
}
return base::TraverseImplicitCastExpr(castE);
}
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (E->getDecl() == Var)
return false; // The reference of the variable, and not just its value,
// is needed.
return true;
}
};
public:
RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
: VarsToChange(VarsToChange) { }
bool VisitBlockDecl(BlockDecl *block) {
SmallVector<VarDecl *, 4> BlockVars;
for (const auto &I : block->captures()) {
VarDecl *var = I.getVariable();
if (I.isByRef() &&
var->getType()->isObjCObjectPointerType() &&
isImplicitStrong(var->getType())) {
BlockVars.push_back(var);
}
}
for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) {
VarDecl *var = BlockVars[i];
BlockVarChecker checker(var);
bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody());
if (onlyValueOfVarIsNeeded)
VarsToChange.insert(var);
else
VarsToChange.erase(var);
}
return true;
}
private:
bool isImplicitStrong(QualType ty) {
if (isa<AttributedType>(ty.getTypePtr()))
return false;
return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong;
}
};
class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
llvm::DenseSet<VarDecl *> &VarsToChange;
public:
BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
: VarsToChange(VarsToChange) { }
bool TraverseBlockDecl(BlockDecl *block) {
RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block);
return true;
}
};
} // anonymous namespace
void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) {
MigrationPass &Pass = BodyCtx.getMigrationContext().Pass;
llvm::DenseSet<VarDecl *> VarsToChange;
BlockObjCVarRewriter trans(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");
}
}

View File

@@ -0,0 +1,251 @@
//===--- TransEmptyStatements.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.
//
//===----------------------------------------------------------------------===//
//
// removeEmptyStatementsAndDealloc:
//
// Removes empty statements that are leftovers from previous transformations.
// e.g for
//
// [x retain];
//
// removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements
// will remove.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
static bool isEmptyARCMTMacroStatement(NullStmt *S,
std::vector<SourceLocation> &MacroLocs,
ASTContext &Ctx) {
if (!S->hasLeadingEmptyMacro())
return false;
SourceLocation SemiLoc = S->getSemiLoc();
if (SemiLoc.isInvalid() || SemiLoc.isMacroID())
return false;
if (MacroLocs.empty())
return false;
SourceManager &SM = Ctx.getSourceManager();
std::vector<SourceLocation>::iterator
I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc,
BeforeThanCompare<SourceLocation>(SM));
--I;
SourceLocation
AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
assert(AfterMacroLoc.isFileID());
if (AfterMacroLoc == SemiLoc)
return true;
int RelOffs = 0;
if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs))
return false;
if (RelOffs < 0)
return false;
// We make the reasonable assumption that a semicolon after 100 characters
// means that it is not the next token after our macro. If this assumption
// fails it is not critical, we will just fail to clear out, e.g., an empty
// 'if'.
if (RelOffs - getARCMTMacroName().size() > 100)
return false;
SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx);
return AfterMacroSemiLoc == SemiLoc;
}
namespace {
/// \brief Returns true if the statement became empty due to previous
/// transformations.
class EmptyChecker : public StmtVisitor<EmptyChecker, bool> {
ASTContext &Ctx;
std::vector<SourceLocation> &MacroLocs;
public:
EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> &macroLocs)
: Ctx(ctx), MacroLocs(macroLocs) { }
bool VisitNullStmt(NullStmt *S) {
return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx);
}
bool VisitCompoundStmt(CompoundStmt *S) {
if (S->body_empty())
return false; // was already empty, not because of transformations.
for (auto *I : S->body())
if (!Visit(I))
return false;
return true;
}
bool VisitIfStmt(IfStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getThen() || !Visit(S->getThen()))
return false;
return !S->getElse() || Visit(S->getElse());
}
bool VisitWhileStmt(WhileStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitDoStmt(DoStmt *S) {
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
Expr *Exp = S->getCollection();
if (!Exp)
return false;
if (hasSideEffects(Exp, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
if (!S->getSubStmt())
return false;
return Visit(S->getSubStmt());
}
};
class EmptyStatementsRemover :
public RecursiveASTVisitor<EmptyStatementsRemover> {
MigrationPass &Pass;
public:
EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
bool TraverseStmtExpr(StmtExpr *E) {
CompoundStmt *S = E->getSubStmt();
for (CompoundStmt::body_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
if (I != E - 1)
check(*I);
TraverseStmt(*I);
}
return true;
}
bool VisitCompoundStmt(CompoundStmt *S) {
for (auto *I : S->body())
check(I);
return true;
}
ASTContext &getContext() { return Pass.Ctx; }
private:
void check(Stmt *S) {
if (!S) return;
if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(S);
}
}
};
} // anonymous namespace
static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
std::vector<SourceLocation> &MacroLocs) {
for (auto *I : body->body())
if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
return false;
return true;
}
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 = nullptr;
ObjCMethodDecl *FinalizeM = nullptr;
for (auto *MD : I->instance_methods()) {
if (!MD->hasBody())
continue;
if (MD->getMethodFamily() == OMF_dealloc) {
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::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
cleanupDeallocOrFinalize(pass);
for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
Transaction Trans(pass.TA);
pass.TA.remove(pass.ARCMTMacroLocs[i]);
}
}

View File

@@ -0,0 +1,355 @@
//===--- 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/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/SaveAndRestore.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 (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) {
TL = QL.getUnqualifiedLoc();
} else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) {
if (handleAttr(Attr, D))
break;
TL = Attr.getModifiedLoc();
} else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
TL = Arr.getElementLoc();
} else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
TL = PT.getPointeeLoc();
} else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
TL = RT.getPointeeLoc();
else
break;
}
}
bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
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 (const auto *MI : RD->methods()) {
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() != nullptr;
if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
return CD->getImplementation() != nullptr;
return isa<ObjCImplDecl>(ContD);
}
return false;
}
bool isInMainFile(Decl *D) {
if (!D)
return false;
for (auto I : D->redecls())
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 =
TL.getAs<AttributedTypeLoc>()) {
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";
}

View File

@@ -0,0 +1,77 @@
//===--- TransGCCalls.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/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class GCCollectableCallsChecker :
public RecursiveASTVisitor<GCCollectableCallsChecker> {
MigrationContext &MigrateCtx;
IdentifierInfo *NSMakeCollectableII;
IdentifierInfo *CFMakeCollectableII;
public:
GCCollectableCallsChecker(MigrationContext &ctx)
: MigrateCtx(ctx) {
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())) {
TA.report(E->getLocStart(), diag::warn_arcmt_nsalloc_realloc,
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())
.TraverseStmt(BodyCtx.getTopStmt());
}

View File

@@ -0,0 +1,379 @@
//===--- TransProperties.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.
//
//===----------------------------------------------------------------------===//
//
// rewriteProperties:
//
// - Adds strong/weak/unsafe_unretained ownership specifier to properties that
// are missing one.
// - Migrates properties from (retain) to (strong) and (assign) to
// (unsafe_unretained/weak).
// - If a property is synthesized, adds the ownership specifier in the ivar
// backing the property.
//
// @interface Foo : NSObject {
// NSObject *x;
// }
// @property (assign) id x;
// @end
// ---->
// @interface Foo : NSObject {
// NSObject *__weak x;
// }
// @property (weak) id x;
// @end
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include <map>
using namespace clang;
using namespace arcmt;
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;
ObjCIvarDecl *IvarD;
ObjCPropertyImplDecl *ImplD;
PropData(ObjCPropertyDecl *propD)
: PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
};
typedef SmallVector<PropData, 2> PropsTy;
typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
AtPropDeclsTy AtProps;
llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
public:
explicit PropertiesRewriter(MigrationContext &MigrateCtx)
: MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
AtPropDeclsTy *PrevAtProps = nullptr) {
for (auto *Prop : D->instance_properties()) {
if (Prop->getAtLoc().isInvalid())
continue;
unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
if (PrevAtProps)
if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
continue;
PropsTy &props = AtProps[RawLoc];
props.push_back(Prop);
}
}
void doTransform(ObjCImplementationDecl *D) {
CurImplD = D;
ObjCInterfaceDecl *iface = D->getClassInterface();
if (!iface)
return;
collectProperties(iface, AtProps);
// Look through extensions.
for (auto *Ext : iface->visible_extensions())
collectProperties(Ext, AtProps);
typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
prop_impl_iterator;
for (prop_impl_iterator
I = prop_impl_iterator(D->decls_begin()),
E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
ObjCPropertyImplDecl *implD = *I;
if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
continue;
ObjCPropertyDecl *propD = implD->getPropertyDecl();
if (!propD || propD->isInvalidDecl())
continue;
ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
if (!ivarD || ivarD->isInvalidDecl())
continue;
unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
if (findAtLoc == AtProps.end())
continue;
PropsTy &props = findAtLoc->second;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (I->PropD == propD) {
I->IvarD = ivarD;
I->ImplD = implD;
break;
}
}
}
for (AtPropDeclsTy::iterator
I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
PropsTy &props = I->second;
if (!getPropertyType(props)->isObjCRetainableType())
continue;
if (hasIvarWithExplicitARCOwnership(props))
continue;
Transaction Trans(Pass.TA);
rewriteProperty(props, atLoc);
}
}
private:
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 rewriteProperty(PropsTy &props, SourceLocation atLoc) {
ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
ObjCPropertyDecl::OBJC_PR_strong |
ObjCPropertyDecl::OBJC_PR_weak))
return;
if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
// strong is the default.
return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
}
bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
if (HasIvarAssignedAPlusOneObject)
return doPropAction(PropAction_AssignRemoved, props, atLoc);
return doPropAction(PropAction_AssignRewritten, props, atLoc);
}
if (HasIvarAssignedAPlusOneObject ||
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
return; // 'strong' by default.
return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
}
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 (I->ImplD)
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
}
}
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 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)) {
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_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
}
}
void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
SourceLocation atLoc) const {
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
/*AllowOnUnknownClass=*/Pass.isGCMigration());
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_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
Pass.TA.clearDiagnostic(
diag::err_arc_objc_property_default_assign_on_object,
I->ImplD->getLocation());
}
}
}
bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
}
bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
SourceLocation atLoc) const {
return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
}
bool addAttribute(StringRef attr, SourceLocation atLoc) const {
return MigrateCtx.addPropertyAttribute(attr, atLoc);
}
class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
ObjCIvarDecl *Ivar;
public:
PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
bool VisitBinAssign(BinaryOperator *E) {
Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
if (RE->getDecl() != Ivar)
return true;
if (isPlusOneAssign(E))
return false;
}
return true;
}
};
bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
PlusOneAssign oneAssign(I->IvarD);
bool notFound = oneAssign.TraverseDecl(CurImplD);
if (!notFound)
return true;
}
return false;
}
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()))
return true;
if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
!= Qualifiers::OCL_Strong)
return true;
}
}
return false;
}
// \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().getUnqualifiedType();
#ifndef NDEBUG
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
assert(ty == I->PropD->getType().getUnqualifiedType());
#endif
return ty;
}
ObjCPropertyDecl::PropertyAttributeKind
getPropertyAttrs(PropsTy &props) const {
assert(!props.empty());
ObjCPropertyDecl::PropertyAttributeKind
attrs = props[0].PropD->getPropertyAttributesAsWritten();
#ifndef NDEBUG
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
assert(attrs == I->PropD->getPropertyAttributesAsWritten());
#endif
return attrs;
}
};
} // anonymous namespace
void PropertyRewriteTraverser::traverseObjCImplementation(
ObjCImplementationContext &ImplCtx) {
PropertiesRewriter(ImplCtx.getMigrationContext())
.doTransform(ImplCtx.getImplementationDecl());
}

View File

@@ -0,0 +1,202 @@
//===--- TransProtectedScope.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.
//
//===----------------------------------------------------------------------===//
//
// Adds brackets in case statements that "contain" initialization of retaining
// variable, thus emitting the "switch case is in protected scope" error.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
SmallVectorImpl<DeclRefExpr *> &Refs;
public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
: Refs(refs) { }
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (ValueDecl *D = E->getDecl())
if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
Refs.push_back(E);
return true;
}
};
struct CaseInfo {
SwitchCase *SC;
SourceRange Range;
enum {
St_Unchecked,
St_CannotFix,
St_Fixed
} State;
CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo(SwitchCase *S, SourceRange Range)
: SC(S), Range(Range), State(St_Unchecked) {}
};
class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
ParentMap &PMap;
SmallVectorImpl<CaseInfo> &Cases;
public:
CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
: PMap(PMap), Cases(Cases) { }
bool VisitSwitchStmt(SwitchStmt *S) {
SwitchCase *Curr = S->getSwitchCaseList();
if (!Curr)
return true;
Stmt *Parent = getCaseParent(Curr);
Curr = Curr->getNextSwitchCase();
// Make sure all case statements are in the same scope.
while (Curr) {
if (getCaseParent(Curr) != Parent)
return true;
Curr = Curr->getNextSwitchCase();
}
SourceLocation NextLoc = S->getLocEnd();
Curr = S->getSwitchCaseList();
// We iterate over case statements in reverse source-order.
while (Curr) {
Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
NextLoc = Curr->getLocStart();
Curr = Curr->getNextSwitchCase();
}
return true;
}
Stmt *getCaseParent(SwitchCase *S) {
Stmt *Parent = PMap.getParent(S);
while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
Parent = PMap.getParent(Parent);
return Parent;
}
};
class ProtectedScopeFixer {
MigrationPass &Pass;
SourceManager &SM;
SmallVector<CaseInfo, 16> Cases;
SmallVector<DeclRefExpr *, 16> LocalRefs;
public:
ProtectedScopeFixer(BodyContext &BodyCtx)
: Pass(BodyCtx.getMigrationContext().Pass),
SM(Pass.Ctx.getSourceManager()) {
CaseCollector(BodyCtx.getParentMap(), Cases)
.TraverseStmt(BodyCtx.getTopStmt());
LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
const CapturedDiagList &DiagList = Pass.getDiags();
// Copy the diagnostics so we don't have to worry about invaliding iterators
// from the diagnostic list.
SmallVector<StoredDiagnostic, 16> StoredDiags;
StoredDiags.append(DiagList.begin(), DiagList.end());
SmallVectorImpl<StoredDiagnostic>::iterator
I = StoredDiags.begin(), E = StoredDiags.end();
while (I != E) {
if (I->getID() == diag::err_switch_into_protected_scope &&
isInRange(I->getLocation(), BodyRange)) {
handleProtectedScopeError(I, E);
continue;
}
++I;
}
}
void handleProtectedScopeError(
SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
Transaction Trans(Pass.TA);
assert(DiagI->getID() == diag::err_switch_into_protected_scope);
SourceLocation ErrLoc = DiagI->getLocation();
bool handledAllNotes = true;
++DiagI;
for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
++DiagI) {
if (!handleProtectedNote(*DiagI))
handledAllNotes = false;
}
if (handledAllNotes)
Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
}
bool handleProtectedNote(const StoredDiagnostic &Diag) {
assert(Diag.getLevel() == DiagnosticsEngine::Note);
for (unsigned i = 0; i != Cases.size(); i++) {
CaseInfo &info = Cases[i];
if (isInRange(Diag.getLocation(), info.Range)) {
if (info.State == CaseInfo::St_Unchecked)
tryFixing(info);
assert(info.State != CaseInfo::St_Unchecked);
if (info.State == CaseInfo::St_Fixed) {
Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
return true;
}
return false;
}
}
return false;
}
void tryFixing(CaseInfo &info) {
assert(info.State == CaseInfo::St_Unchecked);
if (hasVarReferencedOutside(info)) {
info.State = CaseInfo::St_CannotFix;
return;
}
Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
Pass.TA.insert(info.Range.getEnd(), "}\n");
info.State = CaseInfo::St_Fixed;
}
bool hasVarReferencedOutside(CaseInfo &info) {
for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
DeclRefExpr *DRE = LocalRefs[i];
if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
!isInRange(DRE->getLocation(), info.Range))
return true;
}
return false;
}
bool isInRange(SourceLocation Loc, SourceRange R) {
if (Loc.isInvalid())
return false;
return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
SM.isBeforeInTranslationUnit(Loc, R.getEnd());
}
};
} // anonymous namespace
void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
ProtectedScopeFixer Fix(BodyCtx);
}

View File

@@ -0,0 +1,456 @@
//===--- TransRetainReleaseDealloc.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.
//
//===----------------------------------------------------------------------===//
//
// removeRetainReleaseDealloc:
//
// Removes retain/release/autorelease/dealloc messages.
//
// return [[foo retain] autorelease];
// ---->
// return foo;
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class RetainReleaseDeallocRemover :
public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
Stmt *Body;
MigrationPass &Pass;
ExprSet Removables;
std::unique_ptr<ParentMap> StmtMap;
Selector DelegateSel, FinalizeSel;
public:
RetainReleaseDeallocRemover(MigrationPass &pass)
: Body(nullptr), 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, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
StmtMap.reset(new ParentMap(body));
TraverseStmt(body);
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
switch (E->getMethodFamily()) {
default:
if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)
break;
return true;
case OMF_autorelease:
if (isRemovable(E)) {
if (!isCommonUnusedAutorelease(E)) {
// An unused autorelease is badness. If we remove it the receiver
// will likely die immediately while previously it was kept alive
// by the autorelease pool. This is bad practice in general, leave it
// and emit an error to force the user to restructure their code.
Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
"message; its receiver may be destroyed immediately",
E->getLocStart(), E->getSourceRange());
return true;
}
}
// Pass through.
LLVM_FALLTHROUGH;
case OMF_retain:
case OMF_release:
if (E->getReceiverKind() == ObjCMessageExpr::Instance)
if (Expr *rec = E->getInstanceReceiver()) {
rec = rec->IgnoreParenImpCasts();
if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
(E->getMethodFamily() != OMF_retain || isRemovable(E))) {
std::string err = "it is not safe to remove '";
err += E->getSelector().getAsString() + "' message on "
"an __unsafe_unretained type";
Pass.TA.reportError(err, rec->getLocStart());
return true;
}
if (isGlobalVar(rec) &&
(E->getMethodFamily() != OMF_retain || isRemovable(E))) {
std::string err = "it is not safe to remove '";
err += E->getSelector().getAsString() + "' message on "
"a global variable";
Pass.TA.reportError(err, rec->getLocStart());
return true;
}
if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
Pass.TA.reportError("it is not safe to remove 'retain' "
"message on the result of a 'delegate' message; "
"the object that was passed to 'setDelegate:' may not be "
"properly retained", rec->getLocStart());
return true;
}
}
case OMF_dealloc:
break;
}
switch (E->getReceiverKind()) {
default:
return true;
case ObjCMessageExpr::SuperInstance: {
Transaction Trans(Pass.TA);
clearDiagnostics(E->getSelectorLoc(0));
if (tryRemoving(E))
return true;
Pass.TA.replace(E->getSourceRange(), "self");
return true;
}
case ObjCMessageExpr::Instance:
break;
}
Expr *rec = E->getInstanceReceiver();
if (!rec) return true;
Transaction Trans(Pass.TA);
clearDiagnostics(E->getSelectorLoc(0));
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(RecContainer->getSourceRange(), RecRange);
std::string str = " = ";
str += getNilString(Pass);
Pass.TA.insertAfterToken(RecRange.getEnd(), str);
return true;
}
if (hasSideEffects(rec, Pass.Ctx) || !tryRemoving(RecContainer))
Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
return true;
}
private:
/// \brief Checks for idioms where an unused -autorelease is common.
///
/// Returns true for this idiom which is common in property
/// setters:
///
/// [backingValue autorelease];
/// backingValue = [newValue retain]; // in general a +1 assign
///
/// For these as well:
///
/// [[var retain] autorelease];
/// return var;
///
bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
return isPlusOneAssignBeforeOrAfterAutorelease(E) ||
isReturnedAfterAutorelease(E);
}
bool isReturnedAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *nextStmt = getNextStmt(E);
if (!nextStmt)
return false;
// Check for "return <variable>;".
if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(nextStmt))
return RefD == getReferencedDecl(RetS->getRetValue());
return false;
}
bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *prevStmt, *nextStmt;
std::tie(prevStmt, nextStmt) = getPreviousAndNextStmt(E);
return isPlusOneAssignToVar(prevStmt, RefD) ||
isPlusOneAssignToVar(nextStmt, RefD);
}
bool isPlusOneAssignToVar(Stmt *S, Decl *RefD) {
if (!S)
return false;
// Check for "RefD = [+1 retained object];".
if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) {
return (RefD == getReferencedDecl(Bop->getLHS())) && isPlusOneAssign(Bop);
}
if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) {
if (VarDecl *VD = dyn_cast<VarDecl>(RefD))
return isPlusOne(VD->getInit());
}
return false;
}
return false;
}
Stmt *getNextStmt(Expr *E) {
return getPreviousAndNextStmt(E).second;
}
std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) {
Stmt *prevStmt = nullptr, *nextStmt = nullptr;
if (!E)
return std::make_pair(prevStmt, nextStmt);
Stmt *OuterS = E, *InnerS;
do {
InnerS = OuterS;
OuterS = StmtMap->getParent(InnerS);
}
while (OuterS && (isa<ParenExpr>(OuterS) ||
isa<CastExpr>(OuterS) ||
isa<ExprWithCleanups>(OuterS)));
if (!OuterS)
return std::make_pair(prevStmt, nextStmt);
Stmt::child_iterator currChildS = OuterS->child_begin();
Stmt::child_iterator childE = OuterS->child_end();
Stmt::child_iterator prevChildS = childE;
for (; currChildS != childE; ++currChildS) {
if (*currChildS == InnerS)
break;
prevChildS = currChildS;
}
if (prevChildS != childE) {
prevStmt = *prevChildS;
if (prevStmt)
prevStmt = prevStmt->IgnoreImplicit();
}
if (currChildS == childE)
return std::make_pair(prevStmt, nextStmt);
++currChildS;
if (currChildS == childE)
return std::make_pair(prevStmt, nextStmt);
nextStmt = *currChildS;
if (nextStmt)
nextStmt = nextStmt->IgnoreImplicit();
return std::make_pair(prevStmt, nextStmt);
}
Decl *getReferencedDecl(Expr *E) {
if (!E)
return nullptr;
E = E->IgnoreParenCasts();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
switch (ME->getMethodFamily()) {
case OMF_copy:
case OMF_autorelease:
case OMF_release:
case OMF_retain:
return getReferencedDecl(ME->getInstanceReceiver());
default:
return nullptr;
}
}
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl();
if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
return ME->getMemberDecl();
if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
return IRE->getDecl();
return nullptr;
}
/// \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 = nullptr;
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.begin() == StmtExprChild.end())
return;
auto *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild.begin());
if (!CompS)
return;
Stmt::child_range CompStmtChild = CompS->children();
if (CompStmtChild.begin() == CompStmtChild.end())
return;
auto *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild.begin());
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,
diag::err_unavailable_message,
loc);
}
bool isDelegateMessage(Expr *E) const {
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);
return false;
}
bool isInAtFinally(Expr *E) const {
assert(E);
Stmt *S = E;
while (S) {
if (isa<ObjCAtFinallyStmt>(S))
return true;
S = StmtMap->getParent(S);
}
return false;
}
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
bool tryRemoving(Expr *E) const {
if (isRemovable(E)) {
Pass.TA.removeStmt(E);
return true;
}
Stmt *parent = StmtMap->getParent(E);
if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
return tryRemoving(castE);
if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
return tryRemoving(parenE);
if (BinaryOperator *
bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
isRemovable(bopE)) {
Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
return true;
}
}
return false;
}
};
} // anonymous namespace
void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
BodyTransform<RetainReleaseDeallocRemover> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,469 @@
//===--- TransUnbridgedCasts.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.
//
//===----------------------------------------------------------------------===//
//
// rewriteUnbridgedCasts:
//
// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer
// is from a file-level variable, __bridge cast is used to convert it.
// For the result of a function call that we know is +1/+0,
// __bridge/CFBridgingRelease is used.
//
// NSString *str = (NSString *)kUTTypePlainText;
// str = b ? kUTTypeRTF : kUTTypePlainText;
// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault,
// _uuid);
// ---->
// NSString *str = (__bridge NSString *)kUTTypePlainText;
// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
// NSString *_uuidString = (NSString *)
// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
//
// For a C pointer to ObjC, for casting 'self', __bridge is used.
//
// CFStringRef str = (CFStringRef)self;
// ---->
// CFStringRef str = (__bridge CFStringRef)self;
//
// Uses of Block_copy/Block_release macros are rewritten:
//
// c = Block_copy(b);
// Block_release(c);
// ---->
// c = [b copy];
// <removed>
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/ParentMap.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
MigrationPass &Pass;
IdentifierInfo *SelfII;
std::unique_ptr<ParentMap> StmtMap;
Decl *ParentD;
Stmt *Body;
mutable std::unique_ptr<ExprSet> Removables;
public:
UnbridgedCastRewriter(MigrationPass &pass)
: Pass(pass), ParentD(nullptr), Body(nullptr) {
SelfII = &Pass.Ctx.Idents.get("self");
}
void transformBody(Stmt *body, Decl *ParentD) {
this->ParentD = ParentD;
Body = body;
StmtMap.reset(new ParentMap(body));
TraverseStmt(body);
}
bool TraverseBlockDecl(BlockDecl *D) {
// ParentMap does not enter into a BlockDecl to record its stmts, so use a
// new UnbridgedCastRewriter to handle the block.
UnbridgedCastRewriter(Pass).transformBody(D->getBody(), D);
return true;
}
bool VisitCastExpr(CastExpr *E) {
if (E->getCastKind() != CK_CPointerToObjCPointerCast &&
E->getCastKind() != CK_BitCast &&
E->getCastKind() != CK_AnyPointerToBlockPointerCast)
return true;
QualType castType = E->getType();
Expr *castExpr = E->getSubExpr();
QualType castExprType = castExpr->getType();
if (castType->isObjCRetainableType() == castExprType->isObjCRetainableType())
return true;
bool exprRetainable = castExprType->isObjCIndirectLifetimeType();
bool castRetainable = castType->isObjCIndirectLifetimeType();
if (exprRetainable == castRetainable) return true;
if (castExpr->isNullPointerConstant(Pass.Ctx,
Expr::NPC_ValueDependentIsNull))
return true;
SourceLocation loc = castExpr->getExprLoc();
if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc))
return true;
if (castType->isObjCRetainableType())
transformNonObjCToObjCCast(E);
else
transformObjCToNonObjCCast(E);
return true;
}
private:
void transformNonObjCToObjCCast(CastExpr *E) {
if (!E) return;
// Global vars are assumed that are cast as unretained.
if (isGlobalVar(E))
if (E->getSubExpr()->getType()->isPointerType()) {
castToObjCObject(E, /*retained=*/false);
return;
}
// If the cast is directly over the result of a Core Foundation function
// try to figure out whether it should be cast as retained or unretained.
Expr *inner = E->IgnoreParenCasts();
if (CallExpr *callE = dyn_cast<CallExpr>(inner)) {
if (FunctionDecl *FD = callE->getDirectCallee()) {
if (FD->hasAttr<CFReturnsRetainedAttr>()) {
castToObjCObject(E, /*retained=*/true);
return;
}
if (FD->hasAttr<CFReturnsNotRetainedAttr>()) {
castToObjCObject(E, /*retained=*/false);
return;
}
if (FD->isGlobal() &&
FD->getIdentifier() &&
ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF",
FD->getIdentifier()->getName())) {
StringRef fname = FD->getIdentifier()->getName();
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->isExternallyVisible()) {
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;
}
if (fname.find("Get") != StringRef::npos) {
castToObjCObject(E, /*retained=*/false);
return;
}
}
}
}
// If returning an ivar or a member of an ivar from a +0 method, use
// a __bridge cast.
Expr *base = inner->IgnoreParenImpCasts();
while (isa<MemberExpr>(base))
base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts();
if (isa<ObjCIvarRefExpr>(base) &&
isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) {
if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) {
if (!method->hasAttr<NSReturnsRetainedAttr>()) {
castToObjCObject(E, /*retained=*/false);
return;
}
}
}
}
void castToObjCObject(CastExpr *E, bool retained) {
rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge);
}
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) {
Transaction Trans(Pass.TA);
rewriteToBridgedCast(E, Kind, Trans);
}
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind,
Transaction &Trans) {
TransformActions &TA = Pass.TA;
// We will remove the compiler diagnostic.
if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
E->getLocStart())) {
Trans.abort();
return;
}
StringRef bridge;
switch(Kind) {
case OBC_Bridge:
bridge = "__bridge "; break;
case OBC_BridgeTransfer:
bridge = "__bridge_transfer "; break;
case OBC_BridgeRetained:
bridge = "__bridge_retained "; break;
}
TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
E->getLocStart());
if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) {
if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {
TA.insertAfterToken(CCE->getLParenLoc(), bridge);
} else {
SourceLocation insertLoc = E->getSubExpr()->getLocStart();
SmallString<128> newCast;
newCast += '(';
newCast += bridge;
newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
newCast += ')';
if (isa<ParenExpr>(E->getSubExpr())) {
TA.insert(insertLoc, newCast.str());
} else {
newCast += '(';
TA.insert(insertLoc, newCast.str());
TA.insertAfterToken(E->getLocEnd(), ")");
}
}
} else {
assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained);
SmallString<32> BridgeCall;
Expr *WrapE = E->getSubExpr();
SourceLocation InsertLoc = WrapE->getLocStart();
SourceManager &SM = Pass.Ctx.getSourceManager();
char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1));
if (Lexer::isIdentifierBodyChar(PrevChar, Pass.Ctx.getLangOpts()))
BridgeCall += ' ';
if (Kind == OBC_BridgeTransfer)
BridgeCall += "CFBridgingRelease";
else
BridgeCall += "CFBridgingRetain";
if (isa<ParenExpr>(WrapE)) {
TA.insert(InsertLoc, BridgeCall);
} else {
BridgeCall += '(';
TA.insert(InsertLoc, BridgeCall);
TA.insertAfterToken(WrapE->getLocEnd(), ")");
}
}
}
void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) {
Transaction Trans(Pass.TA);
Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange());
rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans);
}
void getBlockMacroRanges(CastExpr *E, SourceRange &Outer, SourceRange &Inner) {
SourceManager &SM = Pass.Ctx.getSourceManager();
SourceLocation Loc = E->getExprLoc();
assert(Loc.isMacroID());
SourceLocation MacroBegin, MacroEnd;
std::tie(MacroBegin, MacroEnd) = SM.getImmediateExpansionRange(Loc);
SourceRange SubRange = E->getSubExpr()->IgnoreParenImpCasts()->getSourceRange();
SourceLocation InnerBegin = SM.getImmediateMacroCallerLoc(SubRange.getBegin());
SourceLocation InnerEnd = SM.getImmediateMacroCallerLoc(SubRange.getEnd());
Outer = SourceRange(MacroBegin, MacroEnd);
Inner = SourceRange(InnerBegin, InnerEnd);
}
void rewriteBlockCopyMacro(CastExpr *E) {
SourceRange OuterRange, InnerRange;
getBlockMacroRanges(E, OuterRange, InnerRange);
Transaction Trans(Pass.TA);
Pass.TA.replace(OuterRange, InnerRange);
Pass.TA.insert(InnerRange.getBegin(), "[");
Pass.TA.insertAfterToken(InnerRange.getEnd(), " copy]");
Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
OuterRange);
}
void removeBlockReleaseMacro(CastExpr *E) {
SourceRange OuterRange, InnerRange;
getBlockMacroRanges(E, OuterRange, InnerRange);
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
OuterRange);
if (!hasSideEffects(E, Pass.Ctx)) {
if (tryRemoving(cast<Expr>(StmtMap->getParentIgnoreParenCasts(E))))
return;
}
Pass.TA.replace(OuterRange, InnerRange);
}
bool tryRemoving(Expr *E) const {
if (!Removables) {
Removables.reset(new ExprSet);
collectRemovables(Body, *Removables);
}
if (Removables->count(E)) {
Pass.TA.removeStmt(E);
return true;
}
return false;
}
void transformObjCToNonObjCCast(CastExpr *E) {
SourceLocation CastLoc = E->getExprLoc();
if (CastLoc.isMacroID()) {
StringRef MacroName = Lexer::getImmediateMacroName(CastLoc,
Pass.Ctx.getSourceManager(),
Pass.Ctx.getLangOpts());
if (MacroName == "Block_copy") {
rewriteBlockCopyMacro(E);
return;
}
if (MacroName == "Block_release") {
removeBlockReleaseMacro(E);
return;
}
}
if (isSelf(E->getSubExpr()))
return rewriteToBridgedCast(E, OBC_Bridge);
CallExpr *callE;
if (isPassedToCFRetain(E, callE))
return rewriteCastForCFRetain(E, callE);
ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr());
if (family == OMF_retain)
return rewriteToBridgedCast(E, OBC_BridgeRetained);
if (family == OMF_autorelease || family == OMF_release) {
std::string err = "it is not safe to cast to '";
err += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
err += "' the result of '";
err += family == OMF_autorelease ? "autorelease" : "release";
err += "' message; a __bridge cast may result in a pointer to a "
"destroyed object and a __bridge_retained may leak the object";
Pass.TA.reportError(err, E->getLocStart(),
E->getSubExpr()->getSourceRange());
Stmt *parent = E;
do {
parent = StmtMap->getParentIgnoreParenImpCasts(parent);
} while (parent && isa<ExprWithCleanups>(parent));
if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) {
std::string note = "remove the cast and change return type of function "
"to '";
note += E->getSubExpr()->getType().getAsString(Pass.Ctx.getPrintingPolicy());
note += "' to have the object automatically autoreleased";
Pass.TA.reportNote(note, retS->getLocStart());
}
}
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)
return rewriteToBridgedCast(E, OBC_Bridge);
}
bool isConsumed = false;
if (isPassedToCParamWithKnownOwnership(E, isConsumed))
return rewriteToBridgedCast(E, isConsumed ? OBC_BridgeRetained
: OBC_Bridge);
}
static ObjCMethodFamily getFamilyOfMessage(Expr *E) {
E = E->IgnoreParenCasts();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
return ME->getMethodFamily();
return OMF_None;
}
bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const {
if ((callE = dyn_cast_or_null<CallExpr>(
StmtMap->getParentIgnoreParenImpCasts(E))))
if (FunctionDecl *
FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl()))
if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 &&
FD->getParent()->isTranslationUnit() &&
FD->isExternallyVisible())
return true;
return false;
}
bool isPassedToCParamWithKnownOwnership(Expr *E, bool &isConsumed) const {
if (CallExpr *callE = dyn_cast_or_null<CallExpr>(
StmtMap->getParentIgnoreParenImpCasts(E)))
if (FunctionDecl *
FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) {
unsigned i = 0;
for (unsigned e = callE->getNumArgs(); i != e; ++i) {
Expr *arg = callE->getArg(i);
if (arg == E || arg->IgnoreParenImpCasts() == E)
break;
}
if (i < callE->getNumArgs() && i < FD->getNumParams()) {
ParmVarDecl *PD = FD->getParamDecl(i);
if (PD->hasAttr<CFConsumedAttr>()) {
isConsumed = true;
return true;
}
}
}
return false;
}
bool isSelf(Expr *E) const {
E = E->IgnoreParenLValueCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
if (IPD->getIdentifier() == SelfII)
return true;
return false;
}
};
} // end anonymous namespace
void trans::rewriteUnbridgedCasts(MigrationPass &pass) {
BodyTransform<UnbridgedCastRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,78 @@
//===--- TransUnusedInitDelegate.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.
//
//===----------------------------------------------------------------------===//
// Transformations:
//===----------------------------------------------------------------------===//
//
// rewriteUnusedInitDelegate:
//
// Rewrites an unused result of calling a delegate initialization, to assigning
// the result to self.
// e.g
// [self init];
// ---->
// self = [self init];
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class UnusedInitRewriter : public RecursiveASTVisitor<UnusedInitRewriter> {
Stmt *Body;
MigrationPass &Pass;
ExprSet Removables;
public:
UnusedInitRewriter(MigrationPass &pass)
: Body(nullptr), Pass(pass) { }
void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
TraverseStmt(body);
}
bool VisitObjCMessageExpr(ObjCMessageExpr *ME) {
if (ME->isDelegateInitCall() &&
isRemovable(ME) &&
Pass.TA.hasDiagnostic(diag::err_arc_unused_init_message,
ME->getExprLoc())) {
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_arc_unused_init_message,
ME->getExprLoc());
SourceRange ExprRange = ME->getSourceRange();
Pass.TA.insert(ExprRange.getBegin(), "if (!(self = ");
std::string retStr = ")) return ";
retStr += getNilString(Pass);
Pass.TA.insertAfterToken(ExprRange.getEnd(), retStr);
}
return true;
}
private:
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
};
} // anonymous namespace
void trans::rewriteUnusedInitDelegate(MigrationPass &pass) {
BodyTransform<UnusedInitRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

View File

@@ -0,0 +1,227 @@
//===--- TransZeroOutPropsInDealloc.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.
//
//===----------------------------------------------------------------------===//
//
// removeZeroOutPropsInDealloc:
//
// Removes zero'ing out "strong" @synthesized properties in a -dealloc method.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ZeroOutInDeallocRemover :
public RecursiveASTVisitor<ZeroOutInDeallocRemover> {
typedef RecursiveASTVisitor<ZeroOutInDeallocRemover> base;
MigrationPass &Pass;
llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties;
ImplicitParamDecl *SelfD;
ExprSet Removables;
Selector FinalizeSel;
public:
ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(nullptr) {
FinalizeSel =
Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
}
bool VisitObjCMessageExpr(ObjCMessageExpr *ME) {
ASTContext &Ctx = Pass.Ctx;
TransformActions &TA = Pass.TA;
if (ME->getReceiverKind() != ObjCMessageExpr::Instance)
return true;
Expr *receiver = ME->getInstanceReceiver();
if (!receiver)
return true;
DeclRefExpr *refE = dyn_cast<DeclRefExpr>(receiver->IgnoreParenCasts());
if (!refE || refE->getDecl() != SelfD)
return true;
bool BackedBySynthesizeSetter = false;
for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
P = SynthesizedProperties.begin(),
E = SynthesizedProperties.end(); P != E; ++P) {
ObjCPropertyDecl *PropDecl = P->first;
if (PropDecl->getSetterName() == ME->getSelector()) {
BackedBySynthesizeSetter = true;
break;
}
}
if (!BackedBySynthesizeSetter)
return true;
// Remove the setter message if RHS is null
Transaction Trans(TA);
Expr *RHS = ME->getArg(0);
bool RHSIsNull =
RHS->isNullPointerConstant(Ctx,
Expr::NPC_ValueDependentIsNull);
if (RHSIsNull && isRemovable(ME))
TA.removeStmt(ME);
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);
Pass.TA.removeStmt(BOE);
}
return true;
}
bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
if (D->getMethodFamily() != OMF_dealloc &&
!(D->isInstanceMethod() && D->getSelector() == FinalizeSel))
return true;
if (!D->hasBody())
return true;
ObjCImplDecl *IMD = dyn_cast<ObjCImplDecl>(D->getDeclContext());
if (!IMD)
return true;
SelfD = D->getSelfDecl();
collectRemovables(D->getBody(), Removables);
// For a 'dealloc' method use, find all property implementations in
// this class implementation.
for (auto *PID : IMD->property_impls()) {
if (PID->getPropertyImplementation() ==
ObjCPropertyImplDecl::Synthesize) {
ObjCPropertyDecl *PD = PID->getPropertyDecl();
ObjCMethodDecl *setterM = PD->getSetterMethodDecl();
if (!(setterM && setterM->isDefined())) {
ObjCPropertyDecl::PropertyAttributeKind AttrKind =
PD->getPropertyAttributes();
if (AttrKind &
(ObjCPropertyDecl::OBJC_PR_retain |
ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_strong))
SynthesizedProperties[PD] = PID;
}
}
}
// Now, remove all zeroing of ivars etc.
base::TraverseObjCMethodDecl(D);
// clear out for next method.
SynthesizedProperties.clear();
SelfD = nullptr;
Removables.clear();
return true;
}
bool TraverseFunctionDecl(FunctionDecl *D) { return true; }
bool TraverseBlockDecl(BlockDecl *block) { return true; }
bool TraverseBlockExpr(BlockExpr *block) { return true; }
private:
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
bool isZeroingPropIvar(Expr *E) {
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;
Expr *LHS = BOE->getLHS();
if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
ObjCIvarDecl *IVDecl = IV->getDecl();
if (!IVDecl->getType()->isObjCObjectPointerType())
return false;
bool IvarBacksPropertySynthesis = false;
for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
P = SynthesizedProperties.begin(),
E = SynthesizedProperties.end(); P != E; ++P) {
ObjCPropertyImplDecl *PropImpDecl = P->second;
if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) {
IvarBacksPropertySynthesis = true;
break;
}
}
if (!IvarBacksPropertySynthesis)
return false;
}
else
return false;
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(E);
}
};
} // anonymous namespace
void trans::removeZeroOutPropsInDeallocFinalize(MigrationPass &pass) {
ZeroOutInDeallocRemover trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

Some files were not shown because too many files have changed in this diff Show More