mirror of
https://github.com/intel/llvm.git
synced 2026-01-25 01:07:04 +08:00
The ARC Migration Tool. All the credit goes to Argyrios and Fariborz
for this. llvm-svn: 133104
This commit is contained in:
@@ -373,6 +373,12 @@
|
||||
90FD6D90103C3D80005F5B73 /* TypeXML.def */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TypeXML.def; path = clang/Frontend/TypeXML.def; sourceTree = "<group>"; };
|
||||
90FD6D91103C3D80005F5B73 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = clang/Frontend/Utils.h; sourceTree = "<group>"; };
|
||||
90FD6DB5103D977E005F5B73 /* index-test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "index-test.cpp"; path = "tools/index-test/index-test.cpp"; sourceTree = "<group>"; };
|
||||
BB20603B131EDDBF003C3343 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
|
||||
BB20603C131EDDBF003C3343 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
|
||||
BB206041131EDDDA003C3343 /* ARRMT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARRMT.h; sourceTree = "<group>"; };
|
||||
BB206043131EDE03003C3343 /* arrmt-test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "arrmt-test.cpp"; sourceTree = "<group>"; };
|
||||
BB206044131EDE03003C3343 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
|
||||
BB206045131EDE03003C3343 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
|
||||
BB5C372812A5057500259F53 /* DumpXML.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DumpXML.cpp; path = /Volumes/Data/llvm/tools/clang/lib/AST/DumpXML.cpp; sourceTree = "<absolute>"; };
|
||||
BBA5AB141309C2FA000B38F1 /* AdjustedReturnValueChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AdjustedReturnValueChecker.cpp; sourceTree = "<group>"; };
|
||||
BBA5AB151309C2FA000B38F1 /* AnalyzerStatsChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnalyzerStatsChecker.cpp; sourceTree = "<group>"; };
|
||||
@@ -777,6 +783,7 @@
|
||||
08FB7795FE84155DC02AAC07 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB20603A131EDDBF003C3343 /* ARRMigrate */,
|
||||
BBA5AB121309C2FA000B38F1 /* StaticAnalyzer */,
|
||||
57EB5660121B034300ECA335 /* Serialization */,
|
||||
BFE2F67911DA95590007EDC0 /* Rewrite */,
|
||||
@@ -1105,6 +1112,38 @@
|
||||
name = "index-test";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB20603A131EDDBF003C3343 /* ARRMigrate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB20603B131EDDBF003C3343 /* CMakeLists.txt */,
|
||||
BB20603C131EDDBF003C3343 /* Makefile */,
|
||||
BDDF60E91337BF40009F1764 /* Transforms.cpp */,
|
||||
);
|
||||
name = ARRMigrate;
|
||||
path = lib/ARRMigrate;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB206040131EDDDA003C3343 /* ARRMigrate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB206041131EDDDA003C3343 /* ARRMT.h */,
|
||||
);
|
||||
name = ARRMigrate;
|
||||
path = clang/ARRMigrate;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB206042131EDE03003C3343 /* arrmt-test */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BD8A47E7133D32660066FE40 /* ARRMT.cpp */,
|
||||
BB206043131EDE03003C3343 /* arrmt-test.cpp */,
|
||||
BB206044131EDE03003C3343 /* CMakeLists.txt */,
|
||||
BB206045131EDE03003C3343 /* Makefile */,
|
||||
);
|
||||
name = "arrmt-test";
|
||||
path = "tools/arrmt-test";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BBA5AB121309C2FA000B38F1 /* StaticAnalyzer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1575,6 +1614,7 @@
|
||||
DED7D72E0A524295003AD0FB /* include */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB206040131EDDDA003C3343 /* ARRMigrate */,
|
||||
DED7D7300A524295003AD0FB /* Basic */,
|
||||
DED7D7390A524295003AD0FB /* Lex */,
|
||||
DE1F21F20A7D84E800FBF588 /* Parse */,
|
||||
@@ -1724,6 +1764,7 @@
|
||||
DEDFE61F0F7B3AE10035BD10 /* Tools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB206042131EDE03003C3343 /* arrmt-test */,
|
||||
90F9EFA8104ABDC400D09A15 /* c-index-test */,
|
||||
9012911E104812DA0083456D /* CIndex */,
|
||||
90FD6DB4103D9763005F5B73 /* index-test */,
|
||||
|
||||
83
clang/include/clang/ARCMigrate/ARCMT.h
Normal file
83
clang/include/clang/ARCMigrate/ARCMT.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===-- ARCMT.h - ARC Migration Rewriter ------------------------*- 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_ARCMIGRATE_ARCMT_H
|
||||
#define LLVM_CLANG_ARCMIGRATE_ARCMT_H
|
||||
|
||||
#include "clang/ARCMigrate/FileRemapper.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
class DiagnosticClient;
|
||||
|
||||
namespace arcmt {
|
||||
class MigrationPass;
|
||||
|
||||
/// \brief Creates an AST with the provided CompilerInvocation but with these
|
||||
/// changes:
|
||||
/// -if a PCH/PTH is set, the original header is used instead
|
||||
/// -Automatic Reference Counting mode is enabled
|
||||
///
|
||||
/// It then checks the AST and produces errors/warning for ARC migration issues
|
||||
/// that the user needs to handle manually.
|
||||
///
|
||||
/// \returns false if no error is produced, true otherwise.
|
||||
bool checkForManualIssues(CompilerInvocation &CI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient);
|
||||
|
||||
/// \brief Works similar to checkForManualIssues but instead of checking, it
|
||||
/// applies automatic modifications to source files to conform to ARC.
|
||||
///
|
||||
/// \returns false if no error is produced, true otherwise.
|
||||
bool applyTransformations(CompilerInvocation &origCI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient);
|
||||
|
||||
/// \brief Like applyTransformations but no source file is modified, compilation
|
||||
/// happens using in-memory buffers.
|
||||
bool applyTransformationsInMemory(CompilerInvocation &origCI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient);
|
||||
|
||||
typedef void (*TransformFn)(MigrationPass &pass);
|
||||
|
||||
std::vector<TransformFn> getAllTransformations();
|
||||
|
||||
class MigrationProcess {
|
||||
CompilerInvocation OrigCI;
|
||||
DiagnosticClient *DiagClient;
|
||||
FileRemapper Remapper;
|
||||
|
||||
public:
|
||||
MigrationProcess(const CompilerInvocation &CI, DiagnosticClient *diagClient)
|
||||
: OrigCI(CI), DiagClient(diagClient) { }
|
||||
|
||||
class RewriteListener {
|
||||
public:
|
||||
virtual ~RewriteListener();
|
||||
|
||||
virtual void start(ASTContext &Ctx) { }
|
||||
virtual void finish() { }
|
||||
|
||||
virtual void insert(SourceLocation loc, llvm::StringRef text) { }
|
||||
virtual void remove(CharSourceRange range) { }
|
||||
};
|
||||
|
||||
bool applyTransform(TransformFn trans, RewriteListener *listener = 0);
|
||||
|
||||
FileRemapper &getRemapper() { return Remapper; }
|
||||
};
|
||||
|
||||
} // end namespace arcmt
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
||||
76
clang/include/clang/ARCMigrate/FileRemapper.h
Normal file
76
clang/include/clang/ARCMigrate/FileRemapper.h
Normal file
@@ -0,0 +1,76 @@
|
||||
//===-- FileRemapper.h - File Remapping Helper ------------------*- 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_ARCMIGRATE_FILEREMAPPER_H
|
||||
#define LLVM_CLANG_ARCMIGRATE_FILEREMAPPER_H
|
||||
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace llvm {
|
||||
class MemoryBuffer;
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
class FileManager;
|
||||
class FileEntry;
|
||||
class Diagnostic;
|
||||
class CompilerInvocation;
|
||||
|
||||
namespace arcmt {
|
||||
|
||||
class FileRemapper {
|
||||
// FIXME: Reuse the same FileManager for multiple ASTContexts.
|
||||
llvm::OwningPtr<FileManager> FileMgr;
|
||||
|
||||
typedef llvm::PointerUnion<const FileEntry *, llvm::MemoryBuffer *> Target;
|
||||
typedef llvm::DenseMap<const FileEntry *, Target> MappingsTy;
|
||||
MappingsTy FromToMappings;
|
||||
|
||||
llvm::DenseMap<const FileEntry *, const FileEntry *> ToFromMappings;
|
||||
|
||||
public:
|
||||
FileRemapper();
|
||||
~FileRemapper();
|
||||
|
||||
bool initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag,
|
||||
bool ignoreIfFilesChanged);
|
||||
bool flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag);
|
||||
|
||||
bool overwriteOriginal(Diagnostic &Diag,
|
||||
llvm::StringRef outputDir = llvm::StringRef());
|
||||
|
||||
void remap(llvm::StringRef filePath, llvm::MemoryBuffer *memBuf);
|
||||
void remap(llvm::StringRef filePath, llvm::StringRef newPath);
|
||||
|
||||
void applyMappings(CompilerInvocation &CI) const;
|
||||
|
||||
void transferMappingsAndClear(CompilerInvocation &CI);
|
||||
|
||||
void clear(llvm::StringRef outputDir = llvm::StringRef());
|
||||
|
||||
private:
|
||||
void remap(const FileEntry *file, llvm::MemoryBuffer *memBuf);
|
||||
void remap(const FileEntry *file, const FileEntry *newfile);
|
||||
|
||||
const FileEntry *getOriginalFile(llvm::StringRef filePath);
|
||||
void resetTarget(Target &targ);
|
||||
|
||||
bool report(const std::string &err, Diagnostic &Diag);
|
||||
|
||||
std::string getRemapInfoFile(llvm::StringRef outputDir);
|
||||
};
|
||||
|
||||
} // end namespace arcmt
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
||||
484
clang/lib/ARCMigrate/ARCMT.cpp
Normal file
484
clang/lib/ARCMigrate/ARCMT.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
//===--- ARCMT.cpp - Migration 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 "Internals.h"
|
||||
#include "clang/Frontend/ASTUnit.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/Rewrite/Rewriter.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
#include "clang/Basic/DiagnosticCategories.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace arcmt;
|
||||
using llvm::StringRef;
|
||||
|
||||
bool CapturedDiagList::clearDiagnostic(llvm::ArrayRef<unsigned> IDs,
|
||||
SourceRange range) {
|
||||
if (range.isInvalid())
|
||||
return false;
|
||||
|
||||
bool cleared = false;
|
||||
ListTy::iterator I = List.begin();
|
||||
while (I != List.end()) {
|
||||
FullSourceLoc diagLoc = I->getLocation();
|
||||
if ((IDs.empty() || // empty means clear all diagnostics in the range.
|
||||
std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
|
||||
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
|
||||
(diagLoc == range.getEnd() ||
|
||||
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
|
||||
cleared = true;
|
||||
ListTy::iterator eraseS = I++;
|
||||
while (I != List.end() && I->getLevel() == Diagnostic::Note)
|
||||
++I;
|
||||
// Clear the diagnostic and any notes following it.
|
||||
List.erase(eraseS, I);
|
||||
continue;
|
||||
}
|
||||
|
||||
++I;
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
bool CapturedDiagList::hasDiagnostic(llvm::ArrayRef<unsigned> IDs,
|
||||
SourceRange range) {
|
||||
if (range.isInvalid())
|
||||
return false;
|
||||
|
||||
ListTy::iterator I = List.begin();
|
||||
while (I != List.end()) {
|
||||
FullSourceLoc diagLoc = I->getLocation();
|
||||
if ((IDs.empty() || // empty means any diagnostic in the range.
|
||||
std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
|
||||
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
|
||||
(diagLoc == range.getEnd() ||
|
||||
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
++I;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CapturedDiagList::reportDiagnostics(Diagnostic &Diags) {
|
||||
for (ListTy::iterator I = List.begin(), E = List.end(); I != E; ++I)
|
||||
Diags.Report(*I);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CaptureDiagnosticClient : public DiagnosticClient {
|
||||
Diagnostic &Diags;
|
||||
CapturedDiagList &CapturedDiags;
|
||||
public:
|
||||
CaptureDiagnosticClient(Diagnostic &diags,
|
||||
CapturedDiagList &capturedDiags)
|
||||
: Diags(diags), CapturedDiags(capturedDiags) { }
|
||||
|
||||
virtual void HandleDiagnostic(Diagnostic::Level level,
|
||||
const DiagnosticInfo &Info) {
|
||||
if (arcmt::isARCDiagnostic(Info.getID(), Diags) ||
|
||||
level >= Diagnostic::Error || level == Diagnostic::Note) {
|
||||
CapturedDiags.push_back(StoredDiagnostic(level, Info));
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-ARC warnings are ignored.
|
||||
Diags.setLastDiagnosticIgnored();
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
CompilerInvocation *createInvocationForMigration(CompilerInvocation &origCI) {
|
||||
llvm::OwningPtr<CompilerInvocation> CInvok;
|
||||
CInvok.reset(new CompilerInvocation(origCI));
|
||||
CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string();
|
||||
CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string();
|
||||
std::string define = getARCMTMacroName();
|
||||
define += '=';
|
||||
CInvok->getPreprocessorOpts().addMacroDef(define);
|
||||
CInvok->getLangOpts().ObjCAutoRefCount = true;
|
||||
CInvok->getDiagnosticOpts().ErrorLimit = 0;
|
||||
|
||||
// FIXME: Hackety hack! Try to find out if there is an ARC runtime.
|
||||
bool hasARCRuntime = false;
|
||||
llvm::SmallVector<std::string, 16> args;
|
||||
args.push_back("-x");
|
||||
args.push_back("objective-c");
|
||||
args.push_back("-fobjc-arc");
|
||||
|
||||
llvm::Triple triple(CInvok->getTargetOpts().Triple);
|
||||
if (triple.getOS() == llvm::Triple::IOS ||
|
||||
triple.getOS() == llvm::Triple::MacOSX) {
|
||||
unsigned Major, Minor, Micro;
|
||||
triple.getOSVersion(Major, Minor, Micro);
|
||||
llvm::SmallString<100> flag;
|
||||
if (triple.getOS() == llvm::Triple::IOS)
|
||||
flag += "-miphoneos-version-min=";
|
||||
else
|
||||
flag += "-mmacosx-version-min=";
|
||||
llvm::raw_svector_ostream(flag) << Major << '.' << Minor << '.' << Micro;
|
||||
args.push_back(flag.str());
|
||||
}
|
||||
|
||||
args.push_back(origCI.getFrontendOpts().Inputs[0].second.c_str());
|
||||
// Also push all defines to deal with the iOS simulator hack.
|
||||
for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size();
|
||||
i != e; ++i) {
|
||||
std::string &def = origCI.getPreprocessorOpts().Macros[i].first;
|
||||
bool isUndef = origCI.getPreprocessorOpts().Macros[i].second;
|
||||
if (!isUndef) {
|
||||
std::string newdef = "-D";
|
||||
newdef += def;
|
||||
args.push_back(newdef);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::SmallVector<const char *, 16> cargs;
|
||||
for (unsigned i = 0, e = args.size(); i != e; ++i)
|
||||
cargs.push_back(args[i].c_str());
|
||||
|
||||
llvm::OwningPtr<CompilerInvocation> checkCI;
|
||||
checkCI.reset(clang::createInvocationFromCommandLine(cargs));
|
||||
if (checkCI)
|
||||
hasARCRuntime = !checkCI->getLangOpts().ObjCNoAutoRefCountRuntime;
|
||||
|
||||
CInvok->getLangOpts().ObjCNoAutoRefCountRuntime = !hasARCRuntime;
|
||||
|
||||
return CInvok.take();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// checkForManualIssues.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient) {
|
||||
if (!origCI.getLangOpts().ObjC1)
|
||||
return false;
|
||||
|
||||
std::vector<TransformFn> transforms = arcmt::getAllTransformations();
|
||||
assert(!transforms.empty());
|
||||
|
||||
llvm::OwningPtr<CompilerInvocation> CInvok;
|
||||
CInvok.reset(createInvocationForMigration(origCI));
|
||||
CInvok->getFrontendOpts().Inputs.clear();
|
||||
CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
|
||||
|
||||
CapturedDiagList capturedDiags;
|
||||
|
||||
assert(DiagClient);
|
||||
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
|
||||
new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
|
||||
|
||||
// Filter of all diagnostics.
|
||||
CaptureDiagnosticClient errRec(*Diags, capturedDiags);
|
||||
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
|
||||
|
||||
llvm::OwningPtr<ASTUnit> Unit(
|
||||
ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
|
||||
if (!Unit)
|
||||
return true;
|
||||
|
||||
// Don't filter diagnostics anymore.
|
||||
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
|
||||
|
||||
ASTContext &Ctx = Unit->getASTContext();
|
||||
|
||||
if (Diags->hasFatalErrorOccurred()) {
|
||||
Diags->Reset();
|
||||
DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
|
||||
capturedDiags.reportDiagnostics(*Diags);
|
||||
DiagClient->EndSourceFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
// After parsing of source files ended, we want to reuse the
|
||||
// diagnostics objects to emit further diagnostics.
|
||||
// We call BeginSourceFile because DiagnosticClient requires that
|
||||
// diagnostics with source range information are emitted only in between
|
||||
// BeginSourceFile() and EndSourceFile().
|
||||
DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
|
||||
|
||||
// No macros will be added since we are just checking and we won't modify
|
||||
// source code.
|
||||
std::vector<SourceLocation> ARCMTMacroLocs;
|
||||
|
||||
TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
|
||||
MigrationPass pass(Ctx, Unit->getSema(), testAct, ARCMTMacroLocs);
|
||||
|
||||
for (unsigned i=0, e = transforms.size(); i != e; ++i)
|
||||
transforms[i](pass);
|
||||
|
||||
capturedDiags.reportDiagnostics(*Diags);
|
||||
|
||||
DiagClient->EndSourceFile();
|
||||
|
||||
return Diags->getClient()->getNumErrors() > 0;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// applyTransformations.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool arcmt::applyTransformations(CompilerInvocation &origCI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient) {
|
||||
if (!origCI.getLangOpts().ObjC1)
|
||||
return false;
|
||||
|
||||
// Make sure checking is successful first.
|
||||
CompilerInvocation CInvokForCheck(origCI);
|
||||
if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient))
|
||||
return true;
|
||||
|
||||
CompilerInvocation CInvok(origCI);
|
||||
CInvok.getFrontendOpts().Inputs.clear();
|
||||
CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
|
||||
|
||||
MigrationProcess migration(CInvok, DiagClient);
|
||||
|
||||
std::vector<TransformFn> transforms = arcmt::getAllTransformations();
|
||||
assert(!transforms.empty());
|
||||
|
||||
for (unsigned i=0, e = transforms.size(); i != e; ++i) {
|
||||
bool err = migration.applyTransform(transforms[i]);
|
||||
if (err) return true;
|
||||
}
|
||||
|
||||
origCI.getLangOpts().ObjCAutoRefCount = true;
|
||||
|
||||
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
|
||||
new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
|
||||
return migration.getRemapper().overwriteOriginal(*Diags);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// applyTransformationsInMemory.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool arcmt::applyTransformationsInMemory(CompilerInvocation &origCI,
|
||||
llvm::StringRef Filename, InputKind Kind,
|
||||
DiagnosticClient *DiagClient) {
|
||||
if (!origCI.getLangOpts().ObjC1)
|
||||
return false;
|
||||
|
||||
// Make sure checking is successful first.
|
||||
CompilerInvocation CInvokForCheck(origCI);
|
||||
if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient))
|
||||
return true;
|
||||
|
||||
CompilerInvocation CInvok(origCI);
|
||||
CInvok.getFrontendOpts().Inputs.clear();
|
||||
CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
|
||||
|
||||
MigrationProcess migration(CInvok, DiagClient);
|
||||
|
||||
std::vector<TransformFn> transforms = arcmt::getAllTransformations();
|
||||
assert(!transforms.empty());
|
||||
|
||||
for (unsigned i=0, e = transforms.size(); i != e; ++i) {
|
||||
bool err = migration.applyTransform(transforms[i]);
|
||||
if (err) return true;
|
||||
}
|
||||
|
||||
origCI.getLangOpts().ObjCAutoRefCount = true;
|
||||
migration.getRemapper().transferMappingsAndClear(origCI);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CollectTransformActions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
|
||||
std::vector<SourceLocation> &ARCMTMacroLocs;
|
||||
|
||||
public:
|
||||
ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
|
||||
: ARCMTMacroLocs(ARCMTMacroLocs) { }
|
||||
|
||||
virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI) {
|
||||
if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
|
||||
ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
|
||||
}
|
||||
};
|
||||
|
||||
class ARCMTMacroTrackerAction : public ASTFrontendAction {
|
||||
std::vector<SourceLocation> &ARCMTMacroLocs;
|
||||
|
||||
public:
|
||||
ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
|
||||
: ARCMTMacroLocs(ARCMTMacroLocs) { }
|
||||
|
||||
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
|
||||
llvm::StringRef InFile) {
|
||||
CI.getPreprocessor().addPPCallbacks(
|
||||
new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
|
||||
return new ASTConsumer();
|
||||
}
|
||||
};
|
||||
|
||||
class RewritesApplicator : public TransformActions::RewriteReceiver {
|
||||
Rewriter &rewriter;
|
||||
ASTContext &Ctx;
|
||||
MigrationProcess::RewriteListener *Listener;
|
||||
|
||||
public:
|
||||
RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
|
||||
MigrationProcess::RewriteListener *listener)
|
||||
: rewriter(rewriter), Ctx(ctx), Listener(listener) {
|
||||
if (Listener)
|
||||
Listener->start(ctx);
|
||||
}
|
||||
~RewritesApplicator() {
|
||||
if (Listener)
|
||||
Listener->finish();
|
||||
}
|
||||
|
||||
virtual void insert(SourceLocation loc, llvm::StringRef text) {
|
||||
bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
|
||||
/*indentNewLines=*/true);
|
||||
if (!err && Listener)
|
||||
Listener->insert(loc, text);
|
||||
}
|
||||
|
||||
virtual void remove(CharSourceRange range) {
|
||||
Rewriter::RewriteOptions removeOpts;
|
||||
removeOpts.IncludeInsertsAtBeginOfRange = false;
|
||||
removeOpts.IncludeInsertsAtEndOfRange = false;
|
||||
removeOpts.RemoveLineIfEmpty = true;
|
||||
|
||||
bool err = rewriter.RemoveText(range, removeOpts);
|
||||
if (!err && Listener)
|
||||
Listener->remove(range);
|
||||
}
|
||||
|
||||
virtual void increaseIndentation(CharSourceRange range,
|
||||
SourceLocation parentIndent) {
|
||||
rewriter.IncreaseIndentation(range, parentIndent);
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace.
|
||||
|
||||
/// \brief Anchor for VTable.
|
||||
MigrationProcess::RewriteListener::~RewriteListener() { }
|
||||
|
||||
bool MigrationProcess::applyTransform(TransformFn trans,
|
||||
RewriteListener *listener) {
|
||||
llvm::OwningPtr<CompilerInvocation> CInvok;
|
||||
CInvok.reset(createInvocationForMigration(OrigCI));
|
||||
CInvok->getDiagnosticOpts().IgnoreWarnings = true;
|
||||
|
||||
Remapper.applyMappings(*CInvok);
|
||||
|
||||
CapturedDiagList capturedDiags;
|
||||
std::vector<SourceLocation> ARCMTMacroLocs;
|
||||
|
||||
assert(DiagClient);
|
||||
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
|
||||
new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
|
||||
|
||||
// Filter of all diagnostics.
|
||||
CaptureDiagnosticClient errRec(*Diags, capturedDiags);
|
||||
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
|
||||
|
||||
llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction;
|
||||
ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
|
||||
|
||||
llvm::OwningPtr<ASTUnit> Unit(
|
||||
ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
|
||||
ASTAction.get()));
|
||||
if (!Unit)
|
||||
return true;
|
||||
Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
|
||||
|
||||
// Don't filter diagnostics anymore.
|
||||
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
|
||||
|
||||
ASTContext &Ctx = Unit->getASTContext();
|
||||
|
||||
if (Diags->hasFatalErrorOccurred()) {
|
||||
Diags->Reset();
|
||||
DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
|
||||
capturedDiags.reportDiagnostics(*Diags);
|
||||
DiagClient->EndSourceFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
// After parsing of source files ended, we want to reuse the
|
||||
// diagnostics objects to emit further diagnostics.
|
||||
// We call BeginSourceFile because DiagnosticClient requires that
|
||||
// diagnostics with source range information are emitted only in between
|
||||
// BeginSourceFile() and EndSourceFile().
|
||||
DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
|
||||
|
||||
Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions());
|
||||
TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
|
||||
MigrationPass pass(Ctx, Unit->getSema(), TA, ARCMTMacroLocs);
|
||||
|
||||
trans(pass);
|
||||
|
||||
{
|
||||
RewritesApplicator applicator(rewriter, Ctx, listener);
|
||||
TA.applyRewrites(applicator);
|
||||
}
|
||||
|
||||
DiagClient->EndSourceFile();
|
||||
|
||||
if (DiagClient->getNumErrors())
|
||||
return true;
|
||||
|
||||
for (Rewriter::buffer_iterator
|
||||
I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
|
||||
FileID FID = I->first;
|
||||
RewriteBuffer &buf = I->second;
|
||||
const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
|
||||
assert(file);
|
||||
std::string newFname = file->getName();
|
||||
newFname += "-trans";
|
||||
llvm::SmallString<512> newText;
|
||||
llvm::raw_svector_ostream vecOS(newText);
|
||||
buf.write(vecOS);
|
||||
vecOS.flush();
|
||||
llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
|
||||
llvm::StringRef(newText.data(), newText.size()), newFname);
|
||||
llvm::SmallString<64> filePath(file->getName());
|
||||
Unit->getFileManager().FixupRelativePath(filePath);
|
||||
Remapper.remap(filePath.str(), memBuf);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// isARCDiagnostic.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool arcmt::isARCDiagnostic(unsigned diagID, Diagnostic &Diag) {
|
||||
return Diag.getDiagnosticIDs()->getCategoryNumberForDiag(diagID) ==
|
||||
diag::DiagCat_Automatic_Reference_Counting_Issue;
|
||||
}
|
||||
14
clang/lib/ARCMigrate/CMakeLists.txt
Normal file
14
clang/lib/ARCMigrate/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend clangRewrite)
|
||||
|
||||
add_clang_library(clangARCMigrate
|
||||
ARCMT.cpp
|
||||
FileRemapper.cpp
|
||||
TransformActions.cpp
|
||||
Transforms.cpp
|
||||
)
|
||||
|
||||
add_dependencies(clangARCMigrate
|
||||
ClangAttrClasses
|
||||
ClangAttrList
|
||||
ClangDeclNodes
|
||||
ClangStmtNodes)
|
||||
290
clang/lib/ARCMigrate/FileRemapper.cpp
Normal file
290
clang/lib/ARCMigrate/FileRemapper.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
//===--- 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/Frontend/CompilerInvocation.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/FileSystem.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(llvm::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);
|
||||
bool existed;
|
||||
llvm::sys::fs::remove(infoFile, existed);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FileRemapper::getRemapInfoFile(llvm::StringRef outputDir) {
|
||||
assert(!outputDir.empty());
|
||||
llvm::sys::Path dir(outputDir);
|
||||
llvm::sys::Path infoFile = dir;
|
||||
infoFile.appendComponent("remap");
|
||||
return infoFile.str();
|
||||
}
|
||||
|
||||
bool FileRemapper::initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag,
|
||||
bool ignoreIfFilesChanged) {
|
||||
assert(FromToMappings.empty() &&
|
||||
"initFromDisk should be called before any remap calls");
|
||||
std::string infoFile = getRemapInfoFile(outputDir);
|
||||
bool fileExists = false;
|
||||
llvm::sys::fs::exists(infoFile, fileExists);
|
||||
if (!fileExists)
|
||||
return false;
|
||||
|
||||
std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
|
||||
|
||||
std::ifstream fin(infoFile.c_str());
|
||||
if (!fin.good())
|
||||
return report(std::string("Error opening file: ") + infoFile, Diag);
|
||||
|
||||
while (true) {
|
||||
std::string fromFilename, toFilename;
|
||||
uint64_t timeModified;
|
||||
|
||||
fin >> fromFilename >> timeModified >> toFilename;
|
||||
if (fin.eof())
|
||||
break;
|
||||
if (!fin.good()) {
|
||||
if (ignoreIfFilesChanged)
|
||||
return false;
|
||||
return report(std::string("Error in format of file: ") + infoFile, Diag);
|
||||
}
|
||||
|
||||
const FileEntry *origFE = FileMgr->getFile(fromFilename);
|
||||
if (!origFE) {
|
||||
if (ignoreIfFilesChanged)
|
||||
continue;
|
||||
return report(std::string("File does not exist: ") + fromFilename, Diag);
|
||||
}
|
||||
const FileEntry *newFE = FileMgr->getFile(toFilename);
|
||||
if (!newFE) {
|
||||
if (ignoreIfFilesChanged)
|
||||
continue;
|
||||
return report(std::string("File does not exist: ") + toFilename, Diag);
|
||||
}
|
||||
|
||||
if ((uint64_t)origFE->getModificationTime() != timeModified) {
|
||||
if (ignoreIfFilesChanged)
|
||||
continue;
|
||||
return report(std::string("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(llvm::StringRef outputDir, Diagnostic &Diag) {
|
||||
using namespace llvm::sys;
|
||||
|
||||
bool existed;
|
||||
if (fs::create_directory(outputDir, existed) != llvm::errc::success)
|
||||
return report(std::string("Could not create directory: ") + outputDir.str(),
|
||||
Diag);
|
||||
|
||||
std::string errMsg;
|
||||
std::string infoFile = getRemapInfoFile(outputDir);
|
||||
llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
|
||||
llvm::raw_fd_ostream::F_Binary);
|
||||
if (!errMsg.empty() || infoOut.has_error())
|
||||
return report(errMsg, Diag);
|
||||
|
||||
for (MappingsTy::iterator
|
||||
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
|
||||
|
||||
const FileEntry *origFE = I->first;
|
||||
infoOut << origFE->getName() << '\n';
|
||||
infoOut << (uint64_t)origFE->getModificationTime() << '\n';
|
||||
|
||||
if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
|
||||
infoOut << FE->getName() << '\n';
|
||||
} else {
|
||||
|
||||
llvm::SmallString<64> tempPath;
|
||||
tempPath = path::filename(origFE->getName());
|
||||
tempPath += "-%%%%%%%%";
|
||||
tempPath += path::extension(origFE->getName());
|
||||
int fd;
|
||||
if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success)
|
||||
return report(std::string("Could not create file: ") + tempPath.c_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(Diagnostic &Diag,
|
||||
llvm::StringRef outputDir) {
|
||||
using namespace llvm::sys;
|
||||
|
||||
for (MappingsTy::iterator
|
||||
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
|
||||
const FileEntry *origFE = I->first;
|
||||
if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) {
|
||||
if (fs::copy_file(newFE->getName(), origFE->getName(),
|
||||
fs::copy_option::overwrite_if_exists) != llvm::errc::success) {
|
||||
std::string err = "Could not copy file '";
|
||||
llvm::raw_string_ostream os(err);
|
||||
os << "Could not copy file '" << newFE->getName() << "' to file '"
|
||||
<< origFE->getName() << "'";
|
||||
os.flush();
|
||||
return report(err, Diag);
|
||||
}
|
||||
} else {
|
||||
|
||||
bool fileExists = false;
|
||||
fs::exists(origFE->getName(), fileExists);
|
||||
if (!fileExists)
|
||||
return report(std::string("File does not exist: ") + origFE->getName(),
|
||||
Diag);
|
||||
|
||||
std::string errMsg;
|
||||
llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
|
||||
llvm::raw_fd_ostream::F_Binary);
|
||||
if (!errMsg.empty() || Out.has_error())
|
||||
return report(errMsg, Diag);
|
||||
|
||||
llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
|
||||
Out.write(mem->getBufferStart(), mem->getBufferSize());
|
||||
Out.close();
|
||||
}
|
||||
}
|
||||
|
||||
clear(outputDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileRemapper::applyMappings(CompilerInvocation &CI) const {
|
||||
PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
|
||||
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::transferMappingsAndClear(CompilerInvocation &CI) {
|
||||
PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
|
||||
for (MappingsTy::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);
|
||||
}
|
||||
I->second = Target();
|
||||
}
|
||||
|
||||
PPOpts.RetainRemappedFileBuffers = false;
|
||||
clear();
|
||||
}
|
||||
|
||||
void FileRemapper::remap(llvm::StringRef filePath, llvm::MemoryBuffer *memBuf) {
|
||||
remap(getOriginalFile(filePath), memBuf);
|
||||
}
|
||||
|
||||
void FileRemapper::remap(llvm::StringRef filePath, llvm::StringRef newPath) {
|
||||
const FileEntry *file = getOriginalFile(filePath);
|
||||
const FileEntry *newfile = FileMgr->getFile(newPath);
|
||||
remap(file, newfile);
|
||||
}
|
||||
|
||||
void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
|
||||
assert(file);
|
||||
Target &targ = FromToMappings[file];
|
||||
resetTarget(targ);
|
||||
targ = memBuf;
|
||||
}
|
||||
|
||||
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(llvm::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 *>();
|
||||
llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
|
||||
I = ToFromMappings.find(toFE);
|
||||
if (I != ToFromMappings.end())
|
||||
ToFromMappings.erase(I);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRemapper::report(const std::string &err, Diagnostic &Diag) {
|
||||
unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
|
||||
err);
|
||||
Diag.Report(ID);
|
||||
return true;
|
||||
}
|
||||
146
clang/lib/ARCMigrate/Internals.h
Normal file
146
clang/lib/ARCMigrate/Internals.h
Normal file
@@ -0,0 +1,146 @@
|
||||
//===-- 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 "llvm/ADT/ArrayRef.h"
|
||||
|
||||
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(llvm::ArrayRef<unsigned> IDs, SourceRange range);
|
||||
bool hasDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
|
||||
|
||||
void reportDiagnostics(Diagnostic &diags);
|
||||
};
|
||||
|
||||
class TransformActions {
|
||||
Diagnostic &Diags;
|
||||
CapturedDiagList &CapturedDiags;
|
||||
void *Impl; // TransformActionsImpl.
|
||||
|
||||
public:
|
||||
TransformActions(Diagnostic &diag, CapturedDiagList &capturedDiags,
|
||||
ASTContext &ctx, Preprocessor &PP);
|
||||
~TransformActions();
|
||||
|
||||
void startTransaction();
|
||||
bool commitTransaction();
|
||||
void abortTransaction();
|
||||
|
||||
void insert(SourceLocation loc, llvm::StringRef text);
|
||||
void insertAfterToken(SourceLocation loc, llvm::StringRef text);
|
||||
void remove(SourceRange range);
|
||||
void removeStmt(Stmt *S);
|
||||
void replace(SourceRange range, llvm::StringRef text);
|
||||
void replace(SourceRange range, SourceRange replacementRange);
|
||||
void replaceStmt(Stmt *S, llvm::StringRef text);
|
||||
void replaceText(SourceLocation loc, llvm::StringRef text,
|
||||
llvm::StringRef replacementText);
|
||||
void increaseIndentation(SourceRange range,
|
||||
SourceLocation parentIndent);
|
||||
|
||||
bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
|
||||
bool clearAllDiagnostics(SourceRange range) {
|
||||
return clearDiagnostic(llvm::ArrayRef<unsigned>(), 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);
|
||||
}
|
||||
|
||||
void reportError(llvm::StringRef error, SourceLocation loc,
|
||||
SourceRange range = SourceRange());
|
||||
void reportNote(llvm::StringRef note, SourceLocation loc,
|
||||
SourceRange range = SourceRange());
|
||||
|
||||
class RewriteReceiver {
|
||||
public:
|
||||
virtual ~RewriteReceiver();
|
||||
|
||||
virtual void insert(SourceLocation loc, llvm::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;
|
||||
Sema &SemaRef;
|
||||
TransformActions &TA;
|
||||
std::vector<SourceLocation> &ARCMTMacroLocs;
|
||||
|
||||
MigrationPass(ASTContext &Ctx, Sema &sema, TransformActions &TA,
|
||||
std::vector<SourceLocation> &ARCMTMacroLocs)
|
||||
: Ctx(Ctx), SemaRef(sema), TA(TA), ARCMTMacroLocs(ARCMTMacroLocs) { }
|
||||
};
|
||||
|
||||
bool isARCDiagnostic(unsigned diagID, Diagnostic &Diag);
|
||||
|
||||
static inline llvm::StringRef getARCMTMacroName() {
|
||||
return "__IMPL_ARCMT_REMOVED_EXPR__";
|
||||
}
|
||||
|
||||
} // end namespace arcmt
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
||||
18
clang/lib/ARCMigrate/Makefile
Normal file
18
clang/lib/ARCMigrate/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
##===- clang/lib/ARCMigrate/Makefile --------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
#
|
||||
# This implements code transformation to ARC mode.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ../..
|
||||
LIBRARYNAME := clangARCMigrate
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
699
clang/lib/ARCMigrate/TransformActions.cpp
Normal file
699
clang/lib/ARCMigrate/TransformActions.cpp
Normal file
@@ -0,0 +1,699 @@
|
||||
//===--- ARCMT.cpp - Migration 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 "Internals.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include <map>
|
||||
|
||||
using namespace clang;
|
||||
using namespace arcmt;
|
||||
using llvm::StringRef;
|
||||
|
||||
namespace {
|
||||
|
||||
/// \brief Collects transformations and merges them before applying them with
|
||||
/// with applyRewrites(). E.g. if the same source range
|
||||
/// is requested to be removed twice, only one rewriter remove will be invoked.
|
||||
/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
|
||||
/// be done (e.g. it resides in a macro) all rewrites in the transaction are
|
||||
/// aborted.
|
||||
/// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
|
||||
class TransformActionsImpl {
|
||||
CapturedDiagList &CapturedDiags;
|
||||
ASTContext &Ctx;
|
||||
Preprocessor &PP;
|
||||
|
||||
bool IsInTransaction;
|
||||
|
||||
enum ActionKind {
|
||||
Act_Insert, Act_InsertAfterToken,
|
||||
Act_Remove, Act_RemoveStmt,
|
||||
Act_Replace, Act_ReplaceText,
|
||||
Act_IncreaseIndentation,
|
||||
Act_ClearDiagnostic
|
||||
};
|
||||
|
||||
struct ActionData {
|
||||
ActionKind Kind;
|
||||
SourceLocation Loc;
|
||||
SourceRange R1, R2;
|
||||
llvm::StringRef Text1, Text2;
|
||||
Stmt *S;
|
||||
llvm::SmallVector<unsigned, 2> DiagIDs;
|
||||
};
|
||||
|
||||
std::vector<ActionData> CachedActions;
|
||||
|
||||
enum RangeComparison {
|
||||
Range_Before,
|
||||
Range_After,
|
||||
Range_Contains,
|
||||
Range_Contained,
|
||||
Range_ExtendsBegin,
|
||||
Range_ExtendsEnd
|
||||
};
|
||||
|
||||
/// \brief A range to remove. It is a character range.
|
||||
struct CharRange {
|
||||
FullSourceLoc Begin, End;
|
||||
|
||||
CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {
|
||||
SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();
|
||||
assert(beginLoc.isValid() && endLoc.isValid());
|
||||
if (range.isTokenRange()) {
|
||||
Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr);
|
||||
End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);
|
||||
} else {
|
||||
Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr);
|
||||
End = FullSourceLoc(srcMgr.getInstantiationLoc(endLoc), srcMgr);
|
||||
}
|
||||
assert(Begin.isValid() && End.isValid());
|
||||
}
|
||||
|
||||
RangeComparison compareWith(const CharRange &RHS) const {
|
||||
if (End.isBeforeInTranslationUnitThan(RHS.Begin))
|
||||
return Range_Before;
|
||||
if (RHS.End.isBeforeInTranslationUnitThan(Begin))
|
||||
return Range_After;
|
||||
if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
|
||||
!RHS.End.isBeforeInTranslationUnitThan(End))
|
||||
return Range_Contained;
|
||||
if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
|
||||
RHS.End.isBeforeInTranslationUnitThan(End))
|
||||
return Range_Contains;
|
||||
if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))
|
||||
return Range_ExtendsBegin;
|
||||
else
|
||||
return Range_ExtendsEnd;
|
||||
}
|
||||
|
||||
static RangeComparison compare(SourceRange LHS, SourceRange RHS,
|
||||
SourceManager &SrcMgr, Preprocessor &PP) {
|
||||
return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)
|
||||
.compareWith(CharRange(CharSourceRange::getTokenRange(RHS),
|
||||
SrcMgr, PP));
|
||||
}
|
||||
};
|
||||
|
||||
typedef llvm::SmallVector<StringRef, 2> TextsVec;
|
||||
typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>
|
||||
InsertsMap;
|
||||
InsertsMap Inserts;
|
||||
/// \brief A list of ranges to remove. They are always sorted and they never
|
||||
/// intersect with each other.
|
||||
std::list<CharRange> Removals;
|
||||
|
||||
llvm::DenseSet<Stmt *> StmtRemovals;
|
||||
|
||||
std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;
|
||||
|
||||
/// \brief Keeps text passed to transformation methods.
|
||||
llvm::StringMap<bool> UniqueText;
|
||||
|
||||
public:
|
||||
TransformActionsImpl(CapturedDiagList &capturedDiags,
|
||||
ASTContext &ctx, Preprocessor &PP)
|
||||
: CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }
|
||||
|
||||
void startTransaction();
|
||||
bool commitTransaction();
|
||||
void abortTransaction();
|
||||
|
||||
bool isInTransaction() const { return IsInTransaction; }
|
||||
|
||||
void insert(SourceLocation loc, llvm::StringRef text);
|
||||
void insertAfterToken(SourceLocation loc, llvm::StringRef text);
|
||||
void remove(SourceRange range);
|
||||
void removeStmt(Stmt *S);
|
||||
void replace(SourceRange range, llvm::StringRef text);
|
||||
void replace(SourceRange range, SourceRange replacementRange);
|
||||
void replaceStmt(Stmt *S, llvm::StringRef text);
|
||||
void replaceText(SourceLocation loc, llvm::StringRef text,
|
||||
llvm::StringRef replacementText);
|
||||
void increaseIndentation(SourceRange range,
|
||||
SourceLocation parentIndent);
|
||||
|
||||
bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
|
||||
|
||||
void applyRewrites(TransformActions::RewriteReceiver &receiver);
|
||||
|
||||
private:
|
||||
bool canInsert(SourceLocation loc);
|
||||
bool canInsertAfterToken(SourceLocation loc);
|
||||
bool canRemoveRange(SourceRange range);
|
||||
bool canReplaceRange(SourceRange range, SourceRange replacementRange);
|
||||
bool canReplaceText(SourceLocation loc, llvm::StringRef text);
|
||||
|
||||
void commitInsert(SourceLocation loc, StringRef text);
|
||||
void commitInsertAfterToken(SourceLocation loc, StringRef text);
|
||||
void commitRemove(SourceRange range);
|
||||
void commitRemoveStmt(Stmt *S);
|
||||
void commitReplace(SourceRange range, SourceRange replacementRange);
|
||||
void commitReplaceText(SourceLocation loc, llvm::StringRef text,
|
||||
llvm::StringRef replacementText);
|
||||
void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);
|
||||
void commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
|
||||
|
||||
void addRemoval(CharSourceRange range);
|
||||
void addInsertion(SourceLocation loc, StringRef text);
|
||||
|
||||
/// \brief Stores text passed to the transformation methods to keep the string
|
||||
/// "alive". Since the vast majority of text will be the same, we also unique
|
||||
/// the strings using a StringMap.
|
||||
StringRef getUniqueText(StringRef text);
|
||||
|
||||
/// \brief Computes the source location just past the end of the token at
|
||||
/// the given source location. If the location points at a macro, the whole
|
||||
/// macro instantiation is skipped.
|
||||
static SourceLocation getLocForEndOfToken(SourceLocation loc,
|
||||
SourceManager &SM,Preprocessor &PP);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void TransformActionsImpl::startTransaction() {
|
||||
assert(!IsInTransaction &&
|
||||
"Cannot start a transaction in the middle of another one");
|
||||
IsInTransaction = true;
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::commitTransaction() {
|
||||
assert(IsInTransaction && "No transaction started");
|
||||
|
||||
if (CachedActions.empty()) {
|
||||
IsInTransaction = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that all actions are possible otherwise abort the whole transaction.
|
||||
bool AllActionsPossible = true;
|
||||
for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
|
||||
ActionData &act = CachedActions[i];
|
||||
switch (act.Kind) {
|
||||
case Act_Insert:
|
||||
if (!canInsert(act.Loc))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_InsertAfterToken:
|
||||
if (!canInsertAfterToken(act.Loc))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_Remove:
|
||||
if (!canRemoveRange(act.R1))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_RemoveStmt:
|
||||
assert(act.S);
|
||||
if (!canRemoveRange(act.S->getSourceRange()))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_Replace:
|
||||
if (!canReplaceRange(act.R1, act.R2))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_ReplaceText:
|
||||
if (!canReplaceText(act.Loc, act.Text1))
|
||||
AllActionsPossible = false;
|
||||
break;
|
||||
case Act_IncreaseIndentation:
|
||||
// This is not important, we don't care if it will fail.
|
||||
break;
|
||||
case Act_ClearDiagnostic:
|
||||
// We are just checking source rewrites.
|
||||
break;
|
||||
}
|
||||
if (!AllActionsPossible)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!AllActionsPossible) {
|
||||
abortTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
|
||||
ActionData &act = CachedActions[i];
|
||||
switch (act.Kind) {
|
||||
case Act_Insert:
|
||||
commitInsert(act.Loc, act.Text1);
|
||||
break;
|
||||
case Act_InsertAfterToken:
|
||||
commitInsertAfterToken(act.Loc, act.Text1);
|
||||
break;
|
||||
case Act_Remove:
|
||||
commitRemove(act.R1);
|
||||
break;
|
||||
case Act_RemoveStmt:
|
||||
commitRemoveStmt(act.S);
|
||||
break;
|
||||
case Act_Replace:
|
||||
commitReplace(act.R1, act.R2);
|
||||
break;
|
||||
case Act_ReplaceText:
|
||||
commitReplaceText(act.Loc, act.Text1, act.Text2);
|
||||
break;
|
||||
case Act_IncreaseIndentation:
|
||||
commitIncreaseIndentation(act.R1, act.Loc);
|
||||
break;
|
||||
case Act_ClearDiagnostic:
|
||||
commitClearDiagnostic(act.DiagIDs, act.R1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CachedActions.clear();
|
||||
IsInTransaction = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransformActionsImpl::abortTransaction() {
|
||||
assert(IsInTransaction && "No transaction started");
|
||||
CachedActions.clear();
|
||||
IsInTransaction = false;
|
||||
}
|
||||
|
||||
void TransformActionsImpl::insert(SourceLocation loc, StringRef text) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
text = getUniqueText(text);
|
||||
ActionData data;
|
||||
data.Kind = Act_Insert;
|
||||
data.Loc = loc;
|
||||
data.Text1 = text;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
text = getUniqueText(text);
|
||||
ActionData data;
|
||||
data.Kind = Act_InsertAfterToken;
|
||||
data.Loc = loc;
|
||||
data.Text1 = text;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::remove(SourceRange range) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
ActionData data;
|
||||
data.Kind = Act_Remove;
|
||||
data.R1 = range;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::removeStmt(Stmt *S) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
ActionData data;
|
||||
data.Kind = Act_RemoveStmt;
|
||||
data.S = S;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::replace(SourceRange range, StringRef text) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
text = getUniqueText(text);
|
||||
remove(range);
|
||||
insert(range.getBegin(), text);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::replace(SourceRange range,
|
||||
SourceRange replacementRange) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
ActionData data;
|
||||
data.Kind = Act_Replace;
|
||||
data.R1 = range;
|
||||
data.R2 = replacementRange;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,
|
||||
StringRef replacementText) {
|
||||
text = getUniqueText(text);
|
||||
replacementText = getUniqueText(replacementText);
|
||||
ActionData data;
|
||||
data.Kind = Act_ReplaceText;
|
||||
data.Loc = loc;
|
||||
data.Text1 = text;
|
||||
data.Text2 = replacementText;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
text = getUniqueText(text);
|
||||
insert(S->getLocStart(), text);
|
||||
removeStmt(S);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::increaseIndentation(SourceRange range,
|
||||
SourceLocation parentIndent) {
|
||||
if (range.isInvalid()) return;
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
ActionData data;
|
||||
data.Kind = Act_IncreaseIndentation;
|
||||
data.R1 = range;
|
||||
data.Loc = parentIndent;
|
||||
CachedActions.push_back(data);
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::clearDiagnostic(llvm::ArrayRef<unsigned> IDs,
|
||||
SourceRange range) {
|
||||
assert(IsInTransaction && "Actions only allowed during a transaction");
|
||||
if (!CapturedDiags.hasDiagnostic(IDs, range))
|
||||
return false;
|
||||
|
||||
ActionData data;
|
||||
data.Kind = Act_ClearDiagnostic;
|
||||
data.R1 = range;
|
||||
data.DiagIDs.append(IDs.begin(), IDs.end());
|
||||
CachedActions.push_back(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::canInsert(SourceLocation loc) {
|
||||
if (loc.isInvalid())
|
||||
return false;
|
||||
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
if (SM.isInSystemHeader(SM.getInstantiationLoc(loc)))
|
||||
return false;
|
||||
|
||||
if (loc.isFileID())
|
||||
return true;
|
||||
return SM.isAtStartOfMacroInstantiation(loc);
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
|
||||
if (loc.isInvalid())
|
||||
return false;
|
||||
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
if (SM.isInSystemHeader(SM.getInstantiationLoc(loc)))
|
||||
return false;
|
||||
|
||||
if (loc.isFileID())
|
||||
return true;
|
||||
return SM.isAtEndOfMacroInstantiation(loc);
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::canRemoveRange(SourceRange range) {
|
||||
return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::canReplaceRange(SourceRange range,
|
||||
SourceRange replacementRange) {
|
||||
return canRemoveRange(range) && canRemoveRange(replacementRange);
|
||||
}
|
||||
|
||||
bool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {
|
||||
if (!canInsert(loc))
|
||||
return false;
|
||||
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
loc = SM.getInstantiationLoc(loc);
|
||||
|
||||
// Break down the source location.
|
||||
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
|
||||
|
||||
// Try to load the file buffer.
|
||||
bool invalidTemp = false;
|
||||
llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
|
||||
if (invalidTemp)
|
||||
return false;
|
||||
|
||||
return file.substr(locInfo.second).startswith(text);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {
|
||||
addInsertion(loc, text);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,
|
||||
StringRef text) {
|
||||
addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitRemove(SourceRange range) {
|
||||
addRemoval(CharSourceRange::getTokenRange(range));
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitRemoveStmt(Stmt *S) {
|
||||
assert(S);
|
||||
if (StmtRemovals.count(S))
|
||||
return; // already removed.
|
||||
|
||||
if (Expr *E = dyn_cast<Expr>(S)) {
|
||||
commitRemove(E->getSourceRange());
|
||||
commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());
|
||||
} else
|
||||
commitRemove(S->getSourceRange());
|
||||
|
||||
StmtRemovals.insert(S);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitReplace(SourceRange range,
|
||||
SourceRange replacementRange) {
|
||||
RangeComparison comp = CharRange::compare(replacementRange, range,
|
||||
Ctx.getSourceManager(), PP);
|
||||
assert(comp == Range_Contained);
|
||||
if (comp != Range_Contained)
|
||||
return; // Although we asserted, be extra safe for release build.
|
||||
if (range.getBegin() != replacementRange.getBegin())
|
||||
addRemoval(CharSourceRange::getCharRange(range.getBegin(),
|
||||
replacementRange.getBegin()));
|
||||
if (replacementRange.getEnd() != range.getEnd())
|
||||
addRemoval(CharSourceRange::getTokenRange(
|
||||
getLocForEndOfToken(replacementRange.getEnd(),
|
||||
Ctx.getSourceManager(), PP),
|
||||
range.getEnd()));
|
||||
}
|
||||
void TransformActionsImpl::commitReplaceText(SourceLocation loc,
|
||||
StringRef text,
|
||||
StringRef replacementText) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
loc = SM.getInstantiationLoc(loc);
|
||||
// canReplaceText already checked if loc points at text.
|
||||
SourceLocation afterText = loc.getFileLocWithOffset(text.size());
|
||||
|
||||
addRemoval(CharSourceRange::getCharRange(loc, afterText));
|
||||
commitInsert(loc, replacementText);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitIncreaseIndentation(SourceRange range,
|
||||
SourceLocation parentIndent) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
IndentationRanges.push_back(
|
||||
std::make_pair(CharRange(CharSourceRange::getTokenRange(range),
|
||||
SM, PP),
|
||||
SM.getInstantiationLoc(parentIndent)));
|
||||
}
|
||||
|
||||
void TransformActionsImpl::commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs,
|
||||
SourceRange range) {
|
||||
CapturedDiags.clearDiagnostic(IDs, range);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
loc = SM.getInstantiationLoc(loc);
|
||||
for (std::list<CharRange>::reverse_iterator
|
||||
I = Removals.rbegin(), E = Removals.rend(); I != E; ++I) {
|
||||
if (!SM.isBeforeInTranslationUnit(loc, I->End))
|
||||
break;
|
||||
if (I->Begin.isBeforeInTranslationUnitThan(loc))
|
||||
return;
|
||||
}
|
||||
|
||||
Inserts[FullSourceLoc(loc, SM)].push_back(text);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::addRemoval(CharSourceRange range) {
|
||||
CharRange newRange(range, Ctx.getSourceManager(), PP);
|
||||
if (newRange.Begin == newRange.End)
|
||||
return;
|
||||
|
||||
Inserts.erase(Inserts.upper_bound(newRange.Begin),
|
||||
Inserts.lower_bound(newRange.End));
|
||||
|
||||
std::list<CharRange>::iterator I = Removals.end();
|
||||
while (I != Removals.begin()) {
|
||||
std::list<CharRange>::iterator RI = I;
|
||||
--RI;
|
||||
RangeComparison comp = newRange.compareWith(*RI);
|
||||
switch (comp) {
|
||||
case Range_Before:
|
||||
--I;
|
||||
break;
|
||||
case Range_After:
|
||||
Removals.insert(I, newRange);
|
||||
return;
|
||||
case Range_Contained:
|
||||
return;
|
||||
case Range_Contains:
|
||||
RI->End = newRange.End;
|
||||
case Range_ExtendsBegin:
|
||||
newRange.End = RI->End;
|
||||
Removals.erase(RI);
|
||||
break;
|
||||
case Range_ExtendsEnd:
|
||||
RI->End = newRange.End;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Removals.insert(Removals.begin(), newRange);
|
||||
}
|
||||
|
||||
void TransformActionsImpl::applyRewrites(
|
||||
TransformActions::RewriteReceiver &receiver) {
|
||||
for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {
|
||||
SourceLocation loc = I->first;
|
||||
for (TextsVec::iterator
|
||||
TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {
|
||||
receiver.insert(loc, *TI);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<std::pair<CharRange, SourceLocation> >::iterator
|
||||
I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {
|
||||
CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,
|
||||
I->first.End);
|
||||
receiver.increaseIndentation(range, I->second);
|
||||
}
|
||||
|
||||
for (std::list<CharRange>::iterator
|
||||
I = Removals.begin(), E = Removals.end(); I != E; ++I) {
|
||||
CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);
|
||||
receiver.remove(range);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Stores text passed to the transformation methods to keep the string
|
||||
/// "alive". Since the vast majority of text will be the same, we also unique
|
||||
/// the strings using a StringMap.
|
||||
StringRef TransformActionsImpl::getUniqueText(StringRef text) {
|
||||
llvm::StringMapEntry<bool> &entry = UniqueText.GetOrCreateValue(text);
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
/// \brief Computes the source location just past the end of the token at
|
||||
/// the given source location. If the location points at a macro, the whole
|
||||
/// macro instantiation is skipped.
|
||||
SourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,
|
||||
SourceManager &SM,
|
||||
Preprocessor &PP) {
|
||||
if (loc.isMacroID())
|
||||
loc = SM.getInstantiationRange(loc).second;
|
||||
return PP.getLocForEndOfToken(loc);
|
||||
}
|
||||
|
||||
TransformActions::RewriteReceiver::~RewriteReceiver() { }
|
||||
|
||||
TransformActions::TransformActions(Diagnostic &diag,
|
||||
CapturedDiagList &capturedDiags,
|
||||
ASTContext &ctx, Preprocessor &PP)
|
||||
: Diags(diag), CapturedDiags(capturedDiags) {
|
||||
Impl = new TransformActionsImpl(capturedDiags, ctx, PP);
|
||||
}
|
||||
|
||||
TransformActions::~TransformActions() {
|
||||
delete static_cast<TransformActionsImpl*>(Impl);
|
||||
}
|
||||
|
||||
void TransformActions::startTransaction() {
|
||||
static_cast<TransformActionsImpl*>(Impl)->startTransaction();
|
||||
}
|
||||
|
||||
bool TransformActions::commitTransaction() {
|
||||
return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();
|
||||
}
|
||||
|
||||
void TransformActions::abortTransaction() {
|
||||
static_cast<TransformActionsImpl*>(Impl)->abortTransaction();
|
||||
}
|
||||
|
||||
|
||||
void TransformActions::insert(SourceLocation loc, llvm::StringRef text) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);
|
||||
}
|
||||
|
||||
void TransformActions::insertAfterToken(SourceLocation loc,
|
||||
llvm::StringRef text) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);
|
||||
}
|
||||
|
||||
void TransformActions::remove(SourceRange range) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->remove(range);
|
||||
}
|
||||
|
||||
void TransformActions::removeStmt(Stmt *S) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);
|
||||
}
|
||||
|
||||
void TransformActions::replace(SourceRange range, llvm::StringRef text) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->replace(range, text);
|
||||
}
|
||||
|
||||
void TransformActions::replace(SourceRange range,
|
||||
SourceRange replacementRange) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);
|
||||
}
|
||||
|
||||
void TransformActions::replaceStmt(Stmt *S, llvm::StringRef text) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);
|
||||
}
|
||||
|
||||
void TransformActions::replaceText(SourceLocation loc, llvm::StringRef text,
|
||||
llvm::StringRef replacementText) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,
|
||||
replacementText);
|
||||
}
|
||||
|
||||
void TransformActions::increaseIndentation(SourceRange range,
|
||||
SourceLocation parentIndent) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,
|
||||
parentIndent);
|
||||
}
|
||||
|
||||
bool TransformActions::clearDiagnostic(llvm::ArrayRef<unsigned> IDs,
|
||||
SourceRange range) {
|
||||
return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);
|
||||
}
|
||||
|
||||
void TransformActions::applyRewrites(RewriteReceiver &receiver) {
|
||||
static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);
|
||||
}
|
||||
|
||||
void TransformActions::reportError(llvm::StringRef error, SourceLocation loc,
|
||||
SourceRange range) {
|
||||
assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() &&
|
||||
"Errors should be emitted out of a transaction");
|
||||
// FIXME: Use a custom category name to distinguish rewriter errors.
|
||||
std::string rewriteErr = "[rewriter] ";
|
||||
rewriteErr += error;
|
||||
unsigned diagID
|
||||
= Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
|
||||
rewriteErr);
|
||||
Diags.Report(loc, diagID) << range;
|
||||
}
|
||||
|
||||
void TransformActions::reportNote(llvm::StringRef note, SourceLocation loc,
|
||||
SourceRange range) {
|
||||
assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() &&
|
||||
"Errors should be emitted out of a transaction");
|
||||
// FIXME: Use a custom category name to distinguish rewriter errors.
|
||||
std::string rewriteNote = "[rewriter] ";
|
||||
rewriteNote += note;
|
||||
unsigned diagID
|
||||
= Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note,
|
||||
rewriteNote);
|
||||
Diags.Report(loc, diagID) << range;
|
||||
}
|
||||
2094
clang/lib/ARCMigrate/Transforms.cpp
Normal file
2094
clang/lib/ARCMigrate/Transforms.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ add_subdirectory(Sema)
|
||||
add_subdirectory(CodeGen)
|
||||
add_subdirectory(Analysis)
|
||||
add_subdirectory(Rewrite)
|
||||
add_subdirectory(ARCMigrate)
|
||||
add_subdirectory(Driver)
|
||||
add_subdirectory(Serialization)
|
||||
add_subdirectory(Frontend)
|
||||
|
||||
@@ -1389,6 +1389,26 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
||||
|
||||
Args.AddLastArg(CmdArgs, options::OPT_working_directory);
|
||||
|
||||
if (!Args.hasArg(options::OPT_fno_objc_arc)) {
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_ccc_arrmt_check,
|
||||
options::OPT_ccc_arrmt_modify,
|
||||
options::OPT_ccc_arrmt_modify_in_memory)) {
|
||||
switch (A->getOption().getID()) {
|
||||
default:
|
||||
llvm_unreachable("missed a case");
|
||||
case options::OPT_ccc_arrmt_check:
|
||||
CmdArgs.push_back("-arcmt-check");
|
||||
break;
|
||||
case options::OPT_ccc_arrmt_modify:
|
||||
CmdArgs.push_back("-arcmt-modify");
|
||||
break;
|
||||
case options::OPT_ccc_arrmt_modify_in_memory:
|
||||
CmdArgs.push_back("-arcmt-modify-in-memory");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add preprocessing options like -I, -D, etc. if we are using the
|
||||
// preprocessor.
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set( LLVM_USED_LIBS
|
||||
clangARCMigrate
|
||||
clangAST
|
||||
clangBasic
|
||||
clangDriver
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/VerifyDiagnosticsClient.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/ARCMigrate/ARCMT.h"
|
||||
#include "clang/Serialization/ASTReader.h"
|
||||
#include "clang/Sema/CodeCompleteConsumer.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
@@ -596,6 +597,34 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
|
||||
if (hasSourceManager())
|
||||
getSourceManager().clearIDTables();
|
||||
|
||||
switch (getFrontendOpts().ARCMTAction) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case FrontendOptions::ARCMT_Check:
|
||||
if (arcmt::checkForManualIssues(getInvocation(), InFile,
|
||||
getFrontendOpts().Inputs[i].first,
|
||||
getDiagnostics().getClient()))
|
||||
continue;
|
||||
// We only want to see warnings reported from arcmt::checkForManualIssues.
|
||||
getDiagnostics().setIgnoreAllWarnings(true);
|
||||
break;
|
||||
|
||||
case FrontendOptions::ARCMT_Modify:
|
||||
if (arcmt::applyTransformations(getInvocation(), InFile,
|
||||
getFrontendOpts().Inputs[i].first,
|
||||
getDiagnostics().getClient()))
|
||||
continue;
|
||||
break;
|
||||
|
||||
case FrontendOptions::ARCMT_ModifyInMemory:
|
||||
if (arcmt::applyTransformationsInMemory(getInvocation(), InFile,
|
||||
getFrontendOpts().Inputs[i].first,
|
||||
getDiagnostics().getClient()))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Act.BeginSourceFile(*this, InFile, getFrontendOpts().Inputs[i].first)) {
|
||||
Act.Execute();
|
||||
Act.EndSourceFile();
|
||||
|
||||
@@ -415,6 +415,19 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts,
|
||||
Res.push_back("-version");
|
||||
if (Opts.FixWhatYouCan)
|
||||
Res.push_back("-fix-what-you-can");
|
||||
switch (Opts.ARCMTAction) {
|
||||
case FrontendOptions::ARCMT_None:
|
||||
break;
|
||||
case FrontendOptions::ARCMT_Check:
|
||||
Res.push_back("-arcmt-check");
|
||||
break;
|
||||
case FrontendOptions::ARCMT_Modify:
|
||||
Res.push_back("-arcmt-modify");
|
||||
break;
|
||||
case FrontendOptions::ARCMT_ModifyInMemory:
|
||||
Res.push_back("-arcmt-modify-in-memory");
|
||||
break;
|
||||
}
|
||||
|
||||
bool NeedLang = false;
|
||||
for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i)
|
||||
@@ -1227,6 +1240,25 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
||||
Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can);
|
||||
Opts.Modules = Args.getAllArgValues(OPT_import_module);
|
||||
|
||||
Opts.ARCMTAction = FrontendOptions::ARCMT_None;
|
||||
if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
|
||||
OPT_arcmt_modify,
|
||||
OPT_arcmt_modify_in_memory)) {
|
||||
switch (A->getOption().getID()) {
|
||||
default:
|
||||
llvm_unreachable("missed a case");
|
||||
case OPT_arcmt_check:
|
||||
Opts.ARCMTAction = FrontendOptions::ARCMT_Check;
|
||||
break;
|
||||
case OPT_arcmt_modify:
|
||||
Opts.ARCMTAction = FrontendOptions::ARCMT_Modify;
|
||||
break;
|
||||
case OPT_arcmt_modify_in_memory:
|
||||
Opts.ARCMTAction = FrontendOptions::ARCMT_ModifyInMemory;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InputKind DashX = IK_None;
|
||||
if (const Arg *A = Args.getLastArg(OPT_x)) {
|
||||
DashX = llvm::StringSwitch<InputKind>(A->getValue(Args))
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
CLANG_LEVEL := ..
|
||||
|
||||
PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \
|
||||
StaticAnalyzer Rewrite Serialization Frontend FrontendTool \
|
||||
Index Driver
|
||||
StaticAnalyzer Rewrite ARCMigrate Serialization Frontend \
|
||||
FrontendTool Index Driver
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
|
||||
72
clang/test/ARCMT/Common.h
Normal file
72
clang/test/ARCMT/Common.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#if __has_feature(objc_arr)
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
|
||||
#else
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
#endif
|
||||
|
||||
typedef struct _NSZone NSZone;
|
||||
typedef int BOOL;
|
||||
typedef unsigned NSUInteger;
|
||||
typedef int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef int32_t UChar32;
|
||||
typedef unsigned char UChar;
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
|
||||
- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
@end
|
||||
|
||||
@protocol NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@protocol NSMutableCopying
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
- (id)init;
|
||||
|
||||
+ (id)new;
|
||||
+ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
+ (id)alloc;
|
||||
- (void)dealloc;
|
||||
|
||||
- (void)finalize;
|
||||
|
||||
- (id)copy;
|
||||
- (id)mutableCopy;
|
||||
|
||||
+ (id)copyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
+ (id)mutableCopyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
@end
|
||||
|
||||
extern void NSRecycleZone(NSZone *zone);
|
||||
|
||||
NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
@interface NSAutoreleasePool : NSObject {
|
||||
@private
|
||||
void *_token;
|
||||
void *_reserved3;
|
||||
void *_reserved2;
|
||||
void *_reserved;
|
||||
}
|
||||
|
||||
+ (void)addObject:(id)anObject;
|
||||
|
||||
- (void)addObject:(id)anObject;
|
||||
|
||||
- (void)drain;
|
||||
|
||||
@end
|
||||
|
||||
typedef const void* objc_objectptr_t;
|
||||
extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer);
|
||||
extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer);
|
||||
extern objc_objectptr_t objc_unretainedPointer(id object);
|
||||
80
clang/test/ARCMT/alloc-with-zone-check.m
Normal file
80
clang/test/ARCMT/alloc-with-zone-check.m
Normal file
@@ -0,0 +1,80 @@
|
||||
// RUN: arcmt-test -check-only -verify --args %s
|
||||
|
||||
#if __has_feature(objc_arr)
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
|
||||
#else
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
#endif
|
||||
|
||||
typedef struct _NSZone NSZone;
|
||||
typedef int BOOL;
|
||||
typedef unsigned NSUInteger;
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
|
||||
- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; // expected-note {{marked unavailable here}}
|
||||
@end
|
||||
|
||||
@protocol NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@protocol NSMutableCopying
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
- (id)init;
|
||||
|
||||
+ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; // expected-note 2 {{marked unavailable here}}
|
||||
+ (id)alloc;
|
||||
- (void)dealloc;
|
||||
|
||||
- (void)finalize;
|
||||
|
||||
- (id)copy;
|
||||
- (id)mutableCopy;
|
||||
|
||||
+ (id)copyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
+ (id)mutableCopyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
@end
|
||||
|
||||
extern void NSRecycleZone(NSZone *zone);
|
||||
|
||||
id IhaveSideEffect();
|
||||
|
||||
@interface Foo : NSObject <NSCopying, NSMutableCopying> {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(id)test:(id)obj {
|
||||
Foo *foo1 = [[Foo allocWithZone:[self zone]] init];
|
||||
Foo *foo2 = [[Foo allocWithZone:[super zone]] init];
|
||||
Foo *foo3 = [[Foo allocWithZone:[IhaveSideEffect() zone]] init]; // expected-error {{not available}}
|
||||
NSRecycleZone([self zone]); // expected-error {{not available}}
|
||||
|
||||
foo1 = [foo1 copyWithZone:[self zone]];
|
||||
foo2 = [foo1 copyWithZone:[super zone]];
|
||||
foo3 = [foo1 copyWithZone:[IhaveSideEffect() zone]];
|
||||
foo1 = [foo1 mutableCopyWithZone:[self zone]];
|
||||
|
||||
return foo1;
|
||||
}
|
||||
|
||||
+(id)allocWithZone:(NSZone *)zone {
|
||||
return [super allocWithZone:zone]; // expected-error {{not available in automatic reference counting mode}}
|
||||
}
|
||||
|
||||
@end
|
||||
42
clang/test/ARCMT/alloc-with-zone.m
Normal file
42
clang/test/ARCMT/alloc-with-zone.m
Normal file
@@ -0,0 +1,42 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject <NSCopying, NSMutableCopying> {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(NSZone *)z;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
+(id)class_test:(NSZone *)z {
|
||||
return [self allocWithZone:z];
|
||||
}
|
||||
|
||||
-(id)test:(NSZone *)z {
|
||||
NSZone *z2 = [self zone], *z3 = z2;
|
||||
NSZone *z4 = z3;
|
||||
|
||||
Foo *foo1 = [[Foo allocWithZone:[self zone]] init];
|
||||
Foo *foo2 = [[Foo allocWithZone:[super zone]] init];
|
||||
Foo *foo3 = [[Foo allocWithZone:z] init];
|
||||
|
||||
Foo *foo4 = [[Foo allocWithZone:z2] init];
|
||||
Foo *foo5 = [[Foo allocWithZone:z3] init];
|
||||
Foo *foo6 = [[Foo allocWithZone:z4] init];
|
||||
|
||||
foo1 = [foo1 copyWithZone:[self zone]];
|
||||
foo2 = [foo1 copyWithZone:[super zone]];
|
||||
foo3 = [foo1 copyWithZone:z];
|
||||
foo1 = [foo1 mutableCopyWithZone:[self zone]];
|
||||
|
||||
return foo1;
|
||||
}
|
||||
|
||||
@end
|
||||
40
clang/test/ARCMT/alloc-with-zone.m.result
Normal file
40
clang/test/ARCMT/alloc-with-zone.m.result
Normal file
@@ -0,0 +1,40 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject <NSCopying, NSMutableCopying> {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(NSZone *)z;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
+(id)class_test:(NSZone *)z {
|
||||
return [self alloc];
|
||||
}
|
||||
|
||||
-(id)test:(NSZone *)z {
|
||||
|
||||
Foo *foo1 = [[Foo alloc] init];
|
||||
Foo *foo2 = [[Foo alloc] init];
|
||||
Foo *foo3 = [[Foo alloc] init];
|
||||
|
||||
Foo *foo4 = [[Foo alloc] init];
|
||||
Foo *foo5 = [[Foo alloc] init];
|
||||
Foo *foo6 = [[Foo alloc] init];
|
||||
|
||||
foo1 = [foo1 copy];
|
||||
foo2 = [foo1 copy];
|
||||
foo3 = [foo1 copy];
|
||||
foo1 = [foo1 mutableCopy];
|
||||
|
||||
return foo1;
|
||||
}
|
||||
|
||||
@end
|
||||
19
clang/test/ARCMT/assign-prop-no-arc-runtime.m
Normal file
19
clang/test/ARCMT/assign-prop-no-arc-runtime.m
Normal file
@@ -0,0 +1,19 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-no-arc-runtime -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=40300 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -miphoneos-version-min=4.3 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -mmacosx-version-min=10.6 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
NSObject *x;
|
||||
}
|
||||
@property (readonly,assign) id x;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@end
|
||||
19
clang/test/ARCMT/assign-prop-no-arc-runtime.m.result
Normal file
19
clang/test/ARCMT/assign-prop-no-arc-runtime.m.result
Normal file
@@ -0,0 +1,19 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-no-arc-runtime -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=40300 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -miphoneos-version-min=4.3 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -mmacosx-version-min=10.6 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
NSObject *__unsafe_unretained x;
|
||||
}
|
||||
@property (readonly,assign) id x;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@end
|
||||
23
clang/test/ARCMT/assign-prop-with-arc-runtime.m
Normal file
23
clang/test/ARCMT/assign-prop-with-arc-runtime.m
Normal file
@@ -0,0 +1,23 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -miphoneos-version-min=5.0 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -mmacosx-version-min=10.7 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
NSObject *x, *w, *q1, *q2;
|
||||
NSObject *z1, *__unsafe_unretained z2;
|
||||
}
|
||||
@property (readonly,assign) id x;
|
||||
@property (assign) id w;
|
||||
@property (assign) id q1, q2;
|
||||
@property (assign) id z1, z2;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x,w,q1,q2,z1,z2;
|
||||
@end
|
||||
23
clang/test/ARCMT/assign-prop-with-arc-runtime.m.result
Normal file
23
clang/test/ARCMT/assign-prop-with-arc-runtime.m.result
Normal file
@@ -0,0 +1,23 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -miphoneos-version-min=5.0 > %t
|
||||
// RUN: diff %t %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -mmacosx-version-min=10.7 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
NSObject *__weak x, *__weak w, *__weak q1, *__weak q2;
|
||||
NSObject *__unsafe_unretained z1, *__unsafe_unretained z2;
|
||||
}
|
||||
@property (readonly,weak) id x;
|
||||
@property (weak) id w;
|
||||
@property (weak) id q1, q2;
|
||||
@property (assign) id z1, z2;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x,w,q1,q2,z1,z2;
|
||||
@end
|
||||
29
clang/test/ARCMT/atautorelease-2.m
Normal file
29
clang/test/ARCMT/atautorelease-2.m
Normal file
@@ -0,0 +1,29 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSAutoreleasePool
|
||||
- drain;
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
-release;
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
while (argc) {
|
||||
[chunkPool release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
[chunkPool drain];
|
||||
[pool drain];
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
clang/test/ARCMT/atautorelease-2.m.result
Normal file
28
clang/test/ARCMT/atautorelease-2.m.result
Normal file
@@ -0,0 +1,28 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSAutoreleasePool
|
||||
- drain;
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
-release;
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
@autoreleasepool {
|
||||
|
||||
while (argc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
40
clang/test/ARCMT/atautorelease-3.m
Normal file
40
clang/test/ARCMT/atautorelease-3.m
Normal file
@@ -0,0 +1,40 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSAutoreleasePool
|
||||
- drain;
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
- release;
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
void test1(int x) {
|
||||
// All this stuff get removed since nothing is happening inside.
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
|
||||
while (x) {
|
||||
chunkPool = [[NSAutoreleasePool alloc] init];
|
||||
[chunkPool release];
|
||||
}
|
||||
|
||||
[chunkPool drain];
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
void test2(int x) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
|
||||
while (x) {
|
||||
chunkPool = [[NSAutoreleasePool alloc] init];
|
||||
++x;
|
||||
[chunkPool release];
|
||||
}
|
||||
|
||||
[chunkPool drain];
|
||||
[pool drain];
|
||||
}
|
||||
31
clang/test/ARCMT/atautorelease-3.m.result
Normal file
31
clang/test/ARCMT/atautorelease-3.m.result
Normal file
@@ -0,0 +1,31 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSAutoreleasePool
|
||||
- drain;
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
- release;
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
void test1(int x) {
|
||||
// All this stuff get removed since nothing is happening inside.
|
||||
}
|
||||
|
||||
void test2(int x) {
|
||||
@autoreleasepool {
|
||||
@autoreleasepool {
|
||||
while (x) {
|
||||
@autoreleasepool {
|
||||
++x;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
144
clang/test/ARCMT/atautorelease-check.m
Normal file
144
clang/test/ARCMT/atautorelease-check.m
Normal file
@@ -0,0 +1,144 @@
|
||||
// RUN: arcmt-test -check-only -verify --args %s
|
||||
|
||||
#if __has_feature(objc_arr)
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
|
||||
#else
|
||||
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
#endif
|
||||
|
||||
typedef struct _NSZone NSZone;
|
||||
typedef int BOOL;
|
||||
typedef unsigned NSUInteger;
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
|
||||
- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
@end
|
||||
|
||||
@protocol NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@protocol NSMutableCopying
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
- (id)init;
|
||||
|
||||
+ (id)new;
|
||||
+ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
+ (id)alloc;
|
||||
- (void)dealloc;
|
||||
|
||||
- (void)finalize;
|
||||
|
||||
- (id)copy;
|
||||
- (id)mutableCopy;
|
||||
|
||||
+ (id)copyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
+ (id)mutableCopyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
|
||||
@end
|
||||
|
||||
extern void NSRecycleZone(NSZone *zone);
|
||||
|
||||
NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
@interface NSAutoreleasePool : NSObject { // expected-note 13 {{marked unavailable here}}
|
||||
@private
|
||||
void *_token;
|
||||
void *_reserved3;
|
||||
void *_reserved2;
|
||||
void *_reserved;
|
||||
}
|
||||
|
||||
+ (void)addObject:(id)anObject;
|
||||
|
||||
- (void)addObject:(id)anObject;
|
||||
|
||||
- (void)drain;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}}
|
||||
|
||||
while (argc) {
|
||||
[chunkPool release];
|
||||
// the following pool was not released in this scope, don't touch it.
|
||||
chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}}
|
||||
}
|
||||
|
||||
[chunkPool drain];
|
||||
[pool drain];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f(void) {
|
||||
NSAutoreleasePool * pool; // expected-error {{'NSAutoreleasePool' is unavailable}}
|
||||
|
||||
for (int i=0; i != 10; ++i) {
|
||||
id x = pool; // We won't touch a NSAutoreleasePool if we can't safely
|
||||
// remove all the references to it.
|
||||
}
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}}
|
||||
NSLog(@"%s", "YES");
|
||||
[pool release];
|
||||
}
|
||||
|
||||
void f2(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
|
||||
// expected-note {{scope begins here}}
|
||||
|
||||
// 'x' is declared inside the "pool scope" but used outside it, if we create
|
||||
// a @autorelease scope it will be undefined outside it so don't touch the pool.
|
||||
int x = 0; // expected-note {{declared here}}
|
||||
|
||||
[pool release]; // expected-note {{scope ends here}}
|
||||
|
||||
++x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
|
||||
}
|
||||
|
||||
void f3(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
|
||||
// expected-note {{scope begins here}}
|
||||
|
||||
struct S { int x; }; // expected-note {{declared here}}
|
||||
|
||||
[pool release]; // expected-note {{scope ends here}}
|
||||
|
||||
struct S *var; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
|
||||
var->x = 0;
|
||||
}
|
||||
|
||||
void f4(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
|
||||
// expected-note {{scope begins here}}
|
||||
|
||||
enum { Bar }; // expected-note {{declared here}}
|
||||
|
||||
[pool release]; // expected-note {{scope ends here}}
|
||||
|
||||
int x = Bar; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
|
||||
}
|
||||
|
||||
void f5(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
|
||||
// expected-note {{scope begins here}}
|
||||
|
||||
typedef int Bar; // expected-note {{declared here}}
|
||||
|
||||
[pool release]; // expected-note {{scope ends here}}
|
||||
|
||||
Bar x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
|
||||
}
|
||||
47
clang/test/ARCMT/atautorelease.m
Normal file
47
clang/test/ARCMT/atautorelease.m
Normal file
@@ -0,0 +1,47 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
if (argc) {
|
||||
NSAutoreleasePool * pool = [NSAutoreleasePool new];
|
||||
NSLog(@"%s", "YES");
|
||||
[pool drain];
|
||||
}
|
||||
[pool drain];
|
||||
|
||||
NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];
|
||||
NSLog(@"%s", "YES");
|
||||
[pool1 release];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f(void) {
|
||||
NSAutoreleasePool *pool1;
|
||||
|
||||
pool1 = [NSAutoreleasePool new];
|
||||
int x = 4;
|
||||
|
||||
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
|
||||
++x;
|
||||
[pool2 drain];
|
||||
|
||||
[pool1 release];
|
||||
}
|
||||
|
||||
int UIApplicationMain(int argc, char *argv[]);
|
||||
|
||||
int main2(int argc, char *argv[]) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
int result = UIApplicationMain(argc, argv);
|
||||
[pool release];
|
||||
return result;
|
||||
}
|
||||
46
clang/test/ARCMT/atautorelease.m.result
Normal file
46
clang/test/ARCMT/atautorelease.m.result
Normal file
@@ -0,0 +1,46 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
if (argc) {
|
||||
@autoreleasepool {
|
||||
NSLog(@"%s", "YES");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
NSLog(@"%s", "YES");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f(void) {
|
||||
|
||||
@autoreleasepool {
|
||||
int x = 4;
|
||||
|
||||
@autoreleasepool {
|
||||
++x;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int UIApplicationMain(int argc, char *argv[]);
|
||||
|
||||
int main2(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
int result = UIApplicationMain(argc, argv);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
48
clang/test/ARCMT/autoreleases.m
Normal file
48
clang/test/ARCMT/autoreleases.m
Normal file
@@ -0,0 +1,48 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef unsigned char BOOL;
|
||||
|
||||
@interface NSObject {
|
||||
id isa;
|
||||
}
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
@end
|
||||
|
||||
@interface NSAutoreleasePool : NSObject
|
||||
- drain;
|
||||
@end
|
||||
|
||||
@interface A : NSObject {
|
||||
@package
|
||||
id object;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface B : NSObject
|
||||
- (BOOL)containsSelf:(A*)a;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
@end
|
||||
|
||||
@implementation B
|
||||
- (BOOL)containsSelf:(A*)a {
|
||||
return a->object == self;
|
||||
}
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
A *a = [[A new] autorelease];
|
||||
B *b = [[B new] autorelease];
|
||||
NSLog(@"%s", [b containsSelf:a] ? "YES" : "NO");
|
||||
[pool drain];
|
||||
return 0;
|
||||
}
|
||||
48
clang/test/ARCMT/autoreleases.m.result
Normal file
48
clang/test/ARCMT/autoreleases.m.result
Normal file
@@ -0,0 +1,48 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef unsigned char BOOL;
|
||||
|
||||
@interface NSObject {
|
||||
id isa;
|
||||
}
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
@end
|
||||
|
||||
@interface NSAutoreleasePool : NSObject
|
||||
- drain;
|
||||
@end
|
||||
|
||||
@interface A : NSObject {
|
||||
@package
|
||||
id object;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface B : NSObject
|
||||
- (BOOL)containsSelf:(A*)a;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
@end
|
||||
|
||||
@implementation B
|
||||
- (BOOL)containsSelf:(A*)a {
|
||||
return a->object == self;
|
||||
}
|
||||
@end
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
A *a = [A new];
|
||||
B *b = [B new];
|
||||
NSLog(@"%s", [b containsSelf:a] ? "YES" : "NO");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
255
clang/test/ARCMT/checking.m
Normal file
255
clang/test/ARCMT/checking.m
Normal file
@@ -0,0 +1,255 @@
|
||||
// RUN: arcmt-test -check-only -verify --args %s
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
typedef const struct __CFString * CFStringRef;
|
||||
extern const CFStringRef kUTTypePlainText;
|
||||
extern const CFStringRef kUTTypeRTF;
|
||||
@class NSString;
|
||||
@class A;
|
||||
|
||||
struct UnsafeS {
|
||||
A *__unsafe_unretained unsafeObj;
|
||||
};
|
||||
|
||||
@interface A : NSObject
|
||||
- (id)retain;
|
||||
- (id)retainCount;
|
||||
- (id)autorelease;
|
||||
- (id)init;
|
||||
- (oneway void)release;
|
||||
- (void)dealloc;
|
||||
-(void)test;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
-(void)test {
|
||||
[super dealloc];
|
||||
}
|
||||
-(void)dealloc {
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id)retain { return self; } // expected-error {{ARC forbids implementation}}
|
||||
- (id)retainCount { return self; } // expected-error {{ARC forbids implementation}}
|
||||
- (id)autorelease { return self; } // expected-error {{ARC forbids implementation}}
|
||||
- (oneway void)release { } // expected-error {{ARC forbids implementation}}
|
||||
@end
|
||||
|
||||
void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
|
||||
[unsafeS->unsafeObj retain]; // expected-error {{It is not safe to remove 'retain' message on an __unsafe_unretained type}} \
|
||||
// expected-error {{ARC forbids explicit message send}}
|
||||
[a dealloc];
|
||||
[a retain];
|
||||
[a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}}
|
||||
[a release];
|
||||
[a autorelease];
|
||||
|
||||
CFStringRef cfstr;
|
||||
NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
|
||||
// expected-note{{use __bridge to convert directly (no change in ownership)}} \
|
||||
// expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
|
||||
str = (NSString *)kUTTypePlainText;
|
||||
str = b ? kUTTypeRTF : kUTTypePlainText;
|
||||
str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
|
||||
str = (NSString *)a; // no change.
|
||||
|
||||
SEL s = @selector(retain); // expected-error {{ARC forbids use of 'retain' in a @selector}}
|
||||
s = @selector(release); // expected-error {{ARC forbids use of 'release' in a @selector}}
|
||||
s = @selector(autorelease); // expected-error {{ARC forbids use of 'autorelease' in a @selector}}
|
||||
s = @selector(dealloc); // expected-error {{ARC forbids use of 'dealloc' in a @selector}}
|
||||
|
||||
static id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing lifetime}}
|
||||
}
|
||||
|
||||
struct S {
|
||||
A* a; // expected-error {{ARC forbids Objective-C objects in structs or unions}}
|
||||
};
|
||||
|
||||
@interface B
|
||||
-(id)alloc;
|
||||
- (id)initWithInt: (int) i;
|
||||
@end
|
||||
|
||||
void rdar8861761() {
|
||||
B *o1 = [[B alloc] initWithInt:0];
|
||||
B *o2 = [B alloc];
|
||||
[o2 initWithInt:0];
|
||||
}
|
||||
|
||||
@interface Test13
|
||||
- (id) init0;
|
||||
- (void) noninit;
|
||||
@end
|
||||
@implementation Test13
|
||||
- (id) init0 {
|
||||
self = 0;
|
||||
}
|
||||
- (void) noninit {
|
||||
self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}}
|
||||
|
||||
for (id x in collection) { // expected-error {{use of undeclared identifier 'collection'}}
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
void * cvt(id arg)
|
||||
{
|
||||
void* voidp_val;
|
||||
(void)(int*)arg; // expected-error {{disallowed}}
|
||||
(void)(id)arg;
|
||||
(void)(__autoreleasing id*)arg; // expected-error {{disallowed}}
|
||||
(void)(id*)arg; // expected-error {{pointer to non-const type 'id' with no explicit lifetime}} expected-error {{disallowed}}
|
||||
|
||||
(void)(__autoreleasing id**)voidp_val;
|
||||
(void)(void*)voidp_val;
|
||||
(void)(void**)arg; // expected-error {{disallowed}}
|
||||
cvt((void*)arg); // expected-error {{requires a bridged cast}} expected-error {{disallowed}} \
|
||||
// expected-note {{use __bridge}} expected-note {{use __bridge_retained}}
|
||||
cvt(0);
|
||||
(void)(__strong id**)(0);
|
||||
return arg; // expected-error {{disallowed}}
|
||||
}
|
||||
|
||||
|
||||
void test12(id collection) {
|
||||
for (id x in collection) {
|
||||
x = 0;
|
||||
}
|
||||
|
||||
for (__strong id x in collection) {
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void test6(unsigned cond) {
|
||||
// FIXME: Fix this automatically ?
|
||||
switch (cond) {
|
||||
case 0:
|
||||
;
|
||||
id x; // expected-note {{jump bypasses initialization of retaining variable}}
|
||||
|
||||
case 1: // expected-error {{switch case is in protected scope}}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@class Test8_incomplete;
|
||||
@interface Test8_complete @end;
|
||||
@interface Test8_super @end;
|
||||
@interface Test8 : Test8_super
|
||||
- (id) init00;
|
||||
- (id) init01; // expected-note {{declaration in interface}}
|
||||
- (id) init02;
|
||||
- (id) init03; // covariance
|
||||
- (id) init04; // covariance
|
||||
- (id) init05;
|
||||
|
||||
- (void) init10; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
|
||||
- (void) init11;
|
||||
- (void) init12;
|
||||
- (void) init13; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
|
||||
- (void) init14; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
|
||||
- (void) init15;
|
||||
|
||||
// These should be invalid to actually call.
|
||||
- (Test8_incomplete*) init20;
|
||||
- (Test8_incomplete*) init21; // expected-note {{declaration in interface}}
|
||||
- (Test8_incomplete*) init22;
|
||||
- (Test8_incomplete*) init23;
|
||||
- (Test8_incomplete*) init24;
|
||||
- (Test8_incomplete*) init25;
|
||||
|
||||
- (Test8_super*) init30; // id exception to covariance
|
||||
- (Test8_super*) init31; // expected-note {{declaration in interface}}
|
||||
- (Test8_super*) init32;
|
||||
- (Test8_super*) init33;
|
||||
- (Test8_super*) init34; // covariance
|
||||
- (Test8_super*) init35;
|
||||
|
||||
- (Test8*) init40; // id exception to covariance
|
||||
- (Test8*) init41; // expected-note {{declaration in interface}}
|
||||
- (Test8*) init42;
|
||||
- (Test8*) init43; // this should be a warning, but that's a general language thing, not an ARC thing
|
||||
- (Test8*) init44;
|
||||
- (Test8*) init45;
|
||||
|
||||
- (Test8_complete*) init50; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init51; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init52; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init53; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init54; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init55; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
@end
|
||||
@implementation Test8
|
||||
- (id) init00 { return 0; }
|
||||
- (id) init10 { return 0; } // expected-error {{method implementation does not match its declaration}}
|
||||
- (id) init20 { return 0; }
|
||||
- (id) init30 { return 0; }
|
||||
- (id) init40 { return 0; }
|
||||
- (id) init50 { return 0; }
|
||||
|
||||
- (void) init01 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
|
||||
- (void) init11 {}
|
||||
- (void) init21 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
|
||||
- (void) init31 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
|
||||
- (void) init41 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
|
||||
- (void) init51 {}
|
||||
|
||||
- (Test8_incomplete*) init02 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_incomplete*) init12 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_incomplete*) init22 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_incomplete*) init32 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_incomplete*) init42 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_incomplete*) init52 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
|
||||
- (Test8_super*) init03 { return 0; }
|
||||
- (Test8_super*) init13 { return 0; } // expected-error {{method implementation does not match its declaration}}
|
||||
- (Test8_super*) init23 { return 0; }
|
||||
- (Test8_super*) init33 { return 0; }
|
||||
- (Test8_super*) init43 { return 0; }
|
||||
- (Test8_super*) init53 { return 0; }
|
||||
|
||||
- (Test8*) init04 { return 0; }
|
||||
- (Test8*) init14 { return 0; } // expected-error {{method implementation does not match its declaration}}
|
||||
- (Test8*) init24 { return 0; }
|
||||
- (Test8*) init34 { return 0; }
|
||||
- (Test8*) init44 { return 0; }
|
||||
- (Test8*) init54 { return 0; }
|
||||
|
||||
- (Test8_complete*) init05 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init15 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init25 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init35 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init45 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test8_complete*) init55 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
|
||||
@end
|
||||
|
||||
@class Test9_incomplete;
|
||||
@interface Test9
|
||||
- (Test9_incomplete*) init1; // expected-error {{init methods must return a type related to the receiver type}}
|
||||
- (Test9_incomplete*) init2;
|
||||
@end
|
||||
id test9(Test9 *v) {
|
||||
return [v init1];
|
||||
}
|
||||
|
||||
// rdar://9491791
|
||||
void rdar9491791(int p) {
|
||||
switch (p) {
|
||||
case 3:;
|
||||
NSObject *o = [[NSObject alloc] init]; // expected-note {{jump bypasses initialization of retaining variable}}
|
||||
[o release];
|
||||
break;
|
||||
default: // expected-error {{switch case is in protected scope}}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define RELEASE_MACRO(x) do { [x release]; } while(1)
|
||||
|
||||
// rdar://9504750
|
||||
void rdar9504750(id p) {
|
||||
RELEASE_MACRO(p); // expected-error {{ARC forbids explicit message send of 'release'}}
|
||||
}
|
||||
105
clang/test/ARCMT/cxx-checking.mm
Normal file
105
clang/test/ARCMT/cxx-checking.mm
Normal file
@@ -0,0 +1,105 @@
|
||||
// RUN: arcmt-test -check-only -verify --args -Warc-abi %s
|
||||
|
||||
// Classes that have an Objective-C object pointer.
|
||||
struct HasObjectMember0 { // expected-warning{{'HasObjectMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}}
|
||||
id x;
|
||||
};
|
||||
|
||||
struct HasObjectMember1 { // expected-warning{{'HasObjectMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}}
|
||||
id x[3];
|
||||
};
|
||||
|
||||
struct HasObjectMember2 { // expected-warning{{'HasObjectMember2' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}}
|
||||
id x[3][2];
|
||||
};
|
||||
|
||||
// Don't complain if the type has non-external linkage
|
||||
namespace {
|
||||
struct HasObjectMember3 {
|
||||
id x[3][2];
|
||||
};
|
||||
}
|
||||
|
||||
// Don't complain if the Objective-C pointer type was explicitly given
|
||||
// no lifetime.
|
||||
struct HasObjectMember3 {
|
||||
__unsafe_unretained id x[3][2];
|
||||
};
|
||||
|
||||
struct HasBlockPointerMember0 { // expected-warning{{'HasBlockPointerMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}}
|
||||
int (^bp)(int);
|
||||
};
|
||||
|
||||
struct HasBlockPointerMember1 { // expected-warning{{'HasBlockPointerMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}}
|
||||
int (^bp[2][3])(int);
|
||||
};
|
||||
|
||||
struct NonPOD {
|
||||
NonPOD(const NonPOD&);
|
||||
};
|
||||
|
||||
struct HasObjectMemberAndNonPOD0 { // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \
|
||||
// expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}}
|
||||
id x;
|
||||
NonPOD np;
|
||||
};
|
||||
|
||||
struct HasObjectMemberAndNonPOD1 { // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \
|
||||
// expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}}
|
||||
NonPOD np;
|
||||
id x[3];
|
||||
};
|
||||
|
||||
struct HasObjectMemberAndNonPOD2 { // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \
|
||||
// expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}}
|
||||
NonPOD np;
|
||||
id x[3][2];
|
||||
};
|
||||
|
||||
struct HasObjectMemberAndNonPOD3 {
|
||||
HasObjectMemberAndNonPOD3 &operator=(const HasObjectMemberAndNonPOD3&);
|
||||
~HasObjectMemberAndNonPOD3();
|
||||
NonPOD np;
|
||||
id x[3][2];
|
||||
};
|
||||
|
||||
struct HasBlockPointerMemberAndNonPOD0 { // expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \
|
||||
// expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}}
|
||||
NonPOD np;
|
||||
int (^bp)(int);
|
||||
};
|
||||
|
||||
struct HasBlockPointerMemberAndNonPOD1 { // expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \
|
||||
// expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}}
|
||||
NonPOD np;
|
||||
int (^bp[2][3])(int);
|
||||
};
|
||||
|
||||
int check_non_pod_objc_pointer0[__is_pod(id)? -1 : 1];
|
||||
int check_non_pod_objc_pointer1[__is_pod(__strong id)? -1 : 1];
|
||||
int check_non_pod_objc_pointer2[__is_pod(__unsafe_unretained id)? 1 : -1];
|
||||
int check_non_pod_objc_pointer3[__is_pod(id[2][3])? -1 : 1];
|
||||
int check_non_pod_objc_pointer4[__is_pod(__unsafe_unretained id[2][3])? 1 : -1];
|
||||
int check_non_pod_block0[__is_pod(int (^)(int))? -1 : 1];
|
||||
int check_non_pod_block1[__is_pod(int (^ __unsafe_unretained)(int))? 1 : -1];
|
||||
|
||||
struct FlexibleArrayMember0 {
|
||||
int length;
|
||||
id array[]; // expected-error{{flexible array member 'array' of non-POD element type 'id __strong[]'}}
|
||||
};
|
||||
|
||||
struct FlexibleArrayMember1 {
|
||||
int length;
|
||||
__unsafe_unretained id array[];
|
||||
};
|
||||
|
||||
// It's okay to pass a retainable type through an ellipsis.
|
||||
void variadic(...);
|
||||
void test_variadic() {
|
||||
variadic(1, 17, @"Foo");
|
||||
}
|
||||
|
||||
// It's okay to create a VLA of retainable types.
|
||||
void vla(int n) {
|
||||
id vla[n];
|
||||
}
|
||||
24
clang/test/ARCMT/dealloc.m
Normal file
24
clang/test/ARCMT/dealloc.m
Normal file
@@ -0,0 +1,24 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface A
|
||||
- (id)retain;
|
||||
- (id)autorelease;
|
||||
- (oneway void)release;
|
||||
- (void)dealloc;
|
||||
@end
|
||||
|
||||
void test1(A *a) {
|
||||
[a dealloc];
|
||||
}
|
||||
|
||||
@interface Test2 : A
|
||||
- (void) dealloc;
|
||||
@end
|
||||
|
||||
@implementation Test2
|
||||
- (void) dealloc {
|
||||
[super dealloc];
|
||||
}
|
||||
@end
|
||||
20
clang/test/ARCMT/dealloc.m.result
Normal file
20
clang/test/ARCMT/dealloc.m.result
Normal file
@@ -0,0 +1,20 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface A
|
||||
- (id)retain;
|
||||
- (id)autorelease;
|
||||
- (oneway void)release;
|
||||
- (void)dealloc;
|
||||
@end
|
||||
|
||||
void test1(A *a) {
|
||||
}
|
||||
|
||||
@interface Test2 : A
|
||||
- (void) dealloc;
|
||||
@end
|
||||
|
||||
@implementation Test2
|
||||
@end
|
||||
37
clang/test/ARCMT/init.m
Normal file
37
clang/test/ARCMT/init.m
Normal file
@@ -0,0 +1,37 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSObject
|
||||
-init;
|
||||
@end
|
||||
|
||||
@interface A : NSObject
|
||||
-init;
|
||||
-init2;
|
||||
-foo;
|
||||
+alloc;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
-(id) init {
|
||||
[self init];
|
||||
id a;
|
||||
[a init];
|
||||
a = [[A alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id) init2 {
|
||||
[super init];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id) foo {
|
||||
[self init];
|
||||
[super init];
|
||||
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
37
clang/test/ARCMT/init.m.result
Normal file
37
clang/test/ARCMT/init.m.result
Normal file
@@ -0,0 +1,37 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface NSObject
|
||||
-init;
|
||||
@end
|
||||
|
||||
@interface A : NSObject
|
||||
-init;
|
||||
-init2;
|
||||
-foo;
|
||||
+alloc;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
-(id) init {
|
||||
self = [self init];
|
||||
id a;
|
||||
[a init];
|
||||
a = [[A alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id) init2 {
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id) foo {
|
||||
[self init];
|
||||
[super init];
|
||||
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
14
clang/test/ARCMT/nonobjc-to-objc-cast-2.m
Normal file
14
clang/test/ARCMT/nonobjc-to-objc-cast-2.m
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: arcmt-test -check-only -verify --args %s
|
||||
|
||||
typedef int BOOL;
|
||||
typedef const struct __CFString * CFStringRef;
|
||||
|
||||
@class NSString;
|
||||
|
||||
void f(BOOL b) {
|
||||
CFStringRef cfstr;
|
||||
NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
|
||||
// expected-note{{use __bridge to convert directly (no change in ownership)}} \
|
||||
// expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
|
||||
void *vp = str; // expected-error {{disallowed}}
|
||||
}
|
||||
32
clang/test/ARCMT/nonobjc-to-objc-cast.m
Normal file
32
clang/test/ARCMT/nonobjc-to-objc-cast.m
Normal file
@@ -0,0 +1,32 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface NSString : NSObject
|
||||
@end
|
||||
|
||||
typedef const struct __CFString * CFStringRef;
|
||||
extern const CFStringRef kUTTypePlainText;
|
||||
extern const CFStringRef kUTTypeRTF;
|
||||
|
||||
typedef const struct __CFAllocator * CFAllocatorRef;
|
||||
typedef const struct __CFUUID * CFUUIDRef;
|
||||
|
||||
extern const CFAllocatorRef kCFAllocatorDefault;
|
||||
|
||||
extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid);
|
||||
|
||||
void f(BOOL b, id p) {
|
||||
NSString *str = (NSString *)kUTTypePlainText;
|
||||
str = b ? kUTTypeRTF : kUTTypePlainText;
|
||||
str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
|
||||
str = (NSString *)p; // no change.
|
||||
|
||||
// FIXME: Add objc -> c examples that we can handle.
|
||||
|
||||
CFUUIDRef _uuid;
|
||||
NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
|
||||
_uuidString = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid) autorelease];
|
||||
}
|
||||
32
clang/test/ARCMT/nonobjc-to-objc-cast.m.result
Normal file
32
clang/test/ARCMT/nonobjc-to-objc-cast.m.result
Normal file
@@ -0,0 +1,32 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface NSString : NSObject
|
||||
@end
|
||||
|
||||
typedef const struct __CFString * CFStringRef;
|
||||
extern const CFStringRef kUTTypePlainText;
|
||||
extern const CFStringRef kUTTypeRTF;
|
||||
|
||||
typedef const struct __CFAllocator * CFAllocatorRef;
|
||||
typedef const struct __CFUUID * CFUUIDRef;
|
||||
|
||||
extern const CFAllocatorRef kCFAllocatorDefault;
|
||||
|
||||
extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid);
|
||||
|
||||
void f(BOOL b, id p) {
|
||||
NSString *str = (__bridge NSString *)kUTTypePlainText;
|
||||
str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
|
||||
str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
|
||||
str = (NSString *)p; // no change.
|
||||
|
||||
// FIXME: Add objc -> c examples that we can handle.
|
||||
|
||||
CFUUIDRef _uuid;
|
||||
NSString *_uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
|
||||
_uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
|
||||
}
|
||||
80
clang/test/ARCMT/releases.m
Normal file
80
clang/test/ARCMT/releases.m
Normal file
@@ -0,0 +1,80 @@
|
||||
// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef int BOOL;
|
||||
|
||||
id IhaveSideEffect();
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain;
|
||||
- (oneway void)release;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
@end
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(void)test:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(void)test:(id)obj {
|
||||
id x = self.bar;
|
||||
[x retain];
|
||||
self.bar = obj;
|
||||
// do stuff with x;
|
||||
[x release];
|
||||
|
||||
[IhaveSideEffect() release];
|
||||
|
||||
[x release], x = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void func(Foo *p) {
|
||||
[p release];
|
||||
(([p release]));
|
||||
}
|
||||
|
||||
@interface Baz {
|
||||
id <NSObject> _foo;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Baz
|
||||
- dealloc {
|
||||
[_foo release];
|
||||
return 0;
|
||||
}
|
||||
@end
|
||||
|
||||
void block_test(Foo *p) {
|
||||
id (^B)() = ^() {
|
||||
if (p) {
|
||||
id (^IB)() = ^() {
|
||||
id bar = [p retain];
|
||||
[p release];
|
||||
return bar;
|
||||
};
|
||||
IB();
|
||||
}
|
||||
return [p retain];
|
||||
};
|
||||
}
|
||||
|
||||
#define RELEASE_MACRO(x) [x release]
|
||||
#define RELEASE_MACRO2(x) RELEASE_MACRO(x)
|
||||
|
||||
void test2(id p) {
|
||||
RELEASE_MACRO(p);
|
||||
RELEASE_MACRO2(p);
|
||||
}
|
||||
72
clang/test/ARCMT/releases.m.result
Normal file
72
clang/test/ARCMT/releases.m.result
Normal file
@@ -0,0 +1,72 @@
|
||||
// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef int BOOL;
|
||||
|
||||
id IhaveSideEffect();
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain;
|
||||
- (oneway void)release;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
@end
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(void)test:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(void)test:(id)obj {
|
||||
id x = self.bar;
|
||||
self.bar = obj;
|
||||
// do stuff with x;
|
||||
|
||||
IhaveSideEffect();
|
||||
|
||||
x = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void func(Foo *p) {
|
||||
}
|
||||
|
||||
@interface Baz {
|
||||
id <NSObject> _foo;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Baz
|
||||
- dealloc {
|
||||
return 0;
|
||||
}
|
||||
@end
|
||||
|
||||
void block_test(Foo *p) {
|
||||
id (^B)() = ^() {
|
||||
if (p) {
|
||||
id (^IB)() = ^() {
|
||||
id bar = p;
|
||||
return bar;
|
||||
};
|
||||
IB();
|
||||
}
|
||||
return p;
|
||||
};
|
||||
}
|
||||
|
||||
#define RELEASE_MACRO(x) [x release]
|
||||
#define RELEASE_MACRO2(x) RELEASE_MACRO(x)
|
||||
|
||||
void test2(id p) {
|
||||
}
|
||||
26
clang/test/ARCMT/remove-dealloc-method.m
Normal file
26
clang/test/ARCMT/remove-dealloc-method.m
Normal file
@@ -0,0 +1,26 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#define nil ((void*) 0)
|
||||
|
||||
@interface Foo
|
||||
@property (retain) id x;
|
||||
@property (retain) id y;
|
||||
@property (retain) id w;
|
||||
@property (retain) id z;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@synthesize y;
|
||||
@synthesize w;
|
||||
@synthesize z;
|
||||
|
||||
- (void) dealloc {
|
||||
self.x = 0;
|
||||
[self setY:nil];
|
||||
w = nil;
|
||||
self.z = nil;
|
||||
}
|
||||
@end
|
||||
20
clang/test/ARCMT/remove-dealloc-method.m.result
Normal file
20
clang/test/ARCMT/remove-dealloc-method.m.result
Normal file
@@ -0,0 +1,20 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#define nil ((void*) 0)
|
||||
|
||||
@interface Foo
|
||||
@property (retain) id x;
|
||||
@property (retain) id y;
|
||||
@property (retain) id w;
|
||||
@property (retain) id z;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@synthesize y;
|
||||
@synthesize w;
|
||||
@synthesize z;
|
||||
|
||||
@end
|
||||
44
clang/test/ARCMT/remove-dealloc-zerouts.m
Normal file
44
clang/test/ARCMT/remove-dealloc-zerouts.m
Normal file
@@ -0,0 +1,44 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface Foo
|
||||
@property (retain) id x;
|
||||
@property (retain) id y;
|
||||
@property (retain) id w;
|
||||
@property (retain) id z;
|
||||
@property (strong) id q;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@synthesize y;
|
||||
@synthesize w;
|
||||
@synthesize q;
|
||||
@dynamic z;
|
||||
|
||||
- (void) dealloc {
|
||||
self.x = self.y = self.w = 0;
|
||||
self.x = 0, w = 0, y = 0;
|
||||
[self setY:0];
|
||||
w = 0;
|
||||
q = 0;
|
||||
self.z = 0;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface Bar
|
||||
@property (retain) Foo *a;
|
||||
- (void) setA:(Foo*) val;
|
||||
- (id) a;
|
||||
@end
|
||||
|
||||
@implementation Bar
|
||||
- (void) dealloc {
|
||||
[self setA:0]; // This is user-defined setter overriding synthesize, don't touch it.
|
||||
self.a.x = 0; // every dealloc must zero out its own ivar. This patter is not recognized.
|
||||
}
|
||||
@synthesize a;
|
||||
- (void) setA:(Foo*) val { }
|
||||
- (id) a {return 0;}
|
||||
@end
|
||||
39
clang/test/ARCMT/remove-dealloc-zerouts.m.result
Normal file
39
clang/test/ARCMT/remove-dealloc-zerouts.m.result
Normal file
@@ -0,0 +1,39 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
@interface Foo
|
||||
@property (retain) id x;
|
||||
@property (retain) id y;
|
||||
@property (retain) id w;
|
||||
@property (retain) id z;
|
||||
@property (strong) id q;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
@synthesize x;
|
||||
@synthesize y;
|
||||
@synthesize w;
|
||||
@synthesize q;
|
||||
@dynamic z;
|
||||
|
||||
- (void) dealloc {
|
||||
self.z = 0;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface Bar
|
||||
@property (retain) Foo *a;
|
||||
- (void) setA:(Foo*) val;
|
||||
- (id) a;
|
||||
@end
|
||||
|
||||
@implementation Bar
|
||||
- (void) dealloc {
|
||||
[self setA:0]; // This is user-defined setter overriding synthesize, don't touch it.
|
||||
self.a.x = 0; // every dealloc must zero out its own ivar. This patter is not recognized.
|
||||
}
|
||||
@synthesize a;
|
||||
- (void) setA:(Foo*) val { }
|
||||
- (id) a {return 0;}
|
||||
@end
|
||||
45
clang/test/ARCMT/remove-statements.m
Normal file
45
clang/test/ARCMT/remove-statements.m
Normal file
@@ -0,0 +1,45 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface myController : NSObject
|
||||
-(id)test:(id)x;
|
||||
@end
|
||||
|
||||
#define MY_MACRO1(x)
|
||||
#define MY_MACRO2(x) (void)x
|
||||
|
||||
@implementation myController
|
||||
-(id) test:(id) x {
|
||||
[[x retain] autorelease];
|
||||
return [[x retain] autorelease];
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
id array, array_already_empty;
|
||||
for (id element in array_already_empty) {
|
||||
}
|
||||
|
||||
[array release];
|
||||
;
|
||||
|
||||
int b, b_array_already_empty;
|
||||
if (b)
|
||||
[array release];
|
||||
if (b_array_already_empty) ;
|
||||
|
||||
if (b) {
|
||||
[array release];
|
||||
}
|
||||
if (b_array_already_empty) {
|
||||
}
|
||||
|
||||
if (b)
|
||||
MY_MACRO1(array);
|
||||
if (b)
|
||||
MY_MACRO2(array);
|
||||
}
|
||||
@end
|
||||
38
clang/test/ARCMT/remove-statements.m.result
Normal file
38
clang/test/ARCMT/remove-statements.m.result
Normal file
@@ -0,0 +1,38 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface myController : NSObject
|
||||
-(id)test:(id)x;
|
||||
@end
|
||||
|
||||
#define MY_MACRO1(x)
|
||||
#define MY_MACRO2(x) (void)x
|
||||
|
||||
@implementation myController
|
||||
-(id) test:(id) x {
|
||||
return x;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
id array, array_already_empty;
|
||||
for (id element in array_already_empty) {
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
int b, b_array_already_empty;
|
||||
if (b_array_already_empty) ;
|
||||
|
||||
if (b_array_already_empty) {
|
||||
}
|
||||
|
||||
if (b)
|
||||
MY_MACRO1(array);
|
||||
if (b)
|
||||
MY_MACRO2(array);
|
||||
}
|
||||
@end
|
||||
71
clang/test/ARCMT/retains.m
Normal file
71
clang/test/ARCMT/retains.m
Normal file
@@ -0,0 +1,71 @@
|
||||
// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
id IhaveSideEffect();
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(id)obj;
|
||||
-(id)something;
|
||||
@end
|
||||
|
||||
#define Something_Macro(key, comment) \
|
||||
[[Foo new] something]
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(id)something {}
|
||||
|
||||
-(id)test:(id)obj {
|
||||
id x = self.bar;
|
||||
[x retain];
|
||||
self.bar = obj;
|
||||
if (obj)
|
||||
[obj retain];
|
||||
|
||||
[Something_Macro(@"foo", "@bar") retain];
|
||||
|
||||
[IhaveSideEffect() retain];
|
||||
|
||||
[[self something] retain];
|
||||
|
||||
[[self retain] something];
|
||||
|
||||
[[IhaveSideEffect() retain] autorelease];
|
||||
[[x retain] autorelease];
|
||||
// do stuff with x;
|
||||
[x release];
|
||||
return [self retain];
|
||||
}
|
||||
|
||||
- (id)test1 {
|
||||
id x=0;
|
||||
([x retain]);
|
||||
return ((([x retain])));
|
||||
}
|
||||
@end
|
||||
|
||||
id foo (Foo *p) {
|
||||
p = [p retain];
|
||||
return ([p retain]);
|
||||
}
|
||||
|
||||
void block_tests(Foo *p) {
|
||||
id (^B)() = ^() {
|
||||
if (p) {
|
||||
id (^IB)() = ^() {
|
||||
id bar = [p retain];
|
||||
return bar;
|
||||
};
|
||||
IB();
|
||||
}
|
||||
return [p retain];
|
||||
};
|
||||
}
|
||||
65
clang/test/ARCMT/retains.m.result
Normal file
65
clang/test/ARCMT/retains.m.result
Normal file
@@ -0,0 +1,65 @@
|
||||
// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
id IhaveSideEffect();
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(id)obj;
|
||||
-(id)something;
|
||||
@end
|
||||
|
||||
#define Something_Macro(key, comment) \
|
||||
[[Foo new] something]
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(id)something {}
|
||||
|
||||
-(id)test:(id)obj {
|
||||
id x = self.bar;
|
||||
self.bar = obj;
|
||||
|
||||
Something_Macro(@"foo", "@bar");
|
||||
|
||||
IhaveSideEffect();
|
||||
|
||||
[self something];
|
||||
|
||||
[self something];
|
||||
|
||||
IhaveSideEffect();
|
||||
// do stuff with x;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)test1 {
|
||||
id x=0;
|
||||
return (((x)));
|
||||
}
|
||||
@end
|
||||
|
||||
id foo (Foo *p) {
|
||||
p = p;
|
||||
return (p);
|
||||
}
|
||||
|
||||
void block_tests(Foo *p) {
|
||||
id (^B)() = ^() {
|
||||
if (p) {
|
||||
id (^IB)() = ^() {
|
||||
id bar = p;
|
||||
return bar;
|
||||
};
|
||||
IB();
|
||||
}
|
||||
return p;
|
||||
};
|
||||
}
|
||||
25
clang/test/ARCMT/rewrite-block-var.m
Normal file
25
clang/test/ARCMT/rewrite-block-var.m
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject
|
||||
-(Foo *)something;
|
||||
@end
|
||||
|
||||
void bar(void (^block)());
|
||||
|
||||
void test1(Foo *p) {
|
||||
__block Foo *x = p; // __block used just to break cycle.
|
||||
bar(^{
|
||||
[x something];
|
||||
});
|
||||
}
|
||||
|
||||
void test2(Foo *p) {
|
||||
__block Foo *x; // __block used as output variable.
|
||||
bar(^{
|
||||
x = [p something];
|
||||
});
|
||||
}
|
||||
25
clang/test/ARCMT/rewrite-block-var.m.result
Normal file
25
clang/test/ARCMT/rewrite-block-var.m.result
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000
|
||||
// RUN: arcmt-test --args -arch x86_64 %s -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
@interface Foo : NSObject
|
||||
-(Foo *)something;
|
||||
@end
|
||||
|
||||
void bar(void (^block)());
|
||||
|
||||
void test1(Foo *p) {
|
||||
__weak Foo *x = p; // __block used just to break cycle.
|
||||
bar(^{
|
||||
[x something];
|
||||
});
|
||||
}
|
||||
|
||||
void test2(Foo *p) {
|
||||
__block Foo *x; // __block used as output variable.
|
||||
bar(^{
|
||||
x = [p something];
|
||||
});
|
||||
}
|
||||
14
clang/test/ARCMT/safe-arc-assign.m
Normal file
14
clang/test/ARCMT/safe-arc-assign.m
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
void test12(id collection) {
|
||||
for (id x in collection) {
|
||||
x = 0;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
for (__strong id x in collection) {
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
14
clang/test/ARCMT/safe-arc-assign.m.result
Normal file
14
clang/test/ARCMT/safe-arc-assign.m.result
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -arch x86_64 %s > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
void test12(id collection) {
|
||||
for (__strong id x in collection) {
|
||||
x = 0;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
for (__strong id x in collection) {
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
47
clang/test/ARCMT/with-working-dir.m
Normal file
47
clang/test/ARCMT/with-working-dir.m
Normal file
@@ -0,0 +1,47 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -working-directory %S with-working-dir.m > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef int BOOL;
|
||||
id IhaveSideEffect();
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain;
|
||||
- (oneway void)release;
|
||||
- (id)something;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
@end
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(id)test:(id)obj {
|
||||
id x = self.bar;
|
||||
[x retain];
|
||||
self.bar = obj;
|
||||
if (obj)
|
||||
[obj retain];
|
||||
|
||||
[IhaveSideEffect() retain];
|
||||
|
||||
[[self something] retain];
|
||||
|
||||
[[self retain] something];
|
||||
|
||||
// do stuff with x;
|
||||
[x release];
|
||||
return [self retain];
|
||||
}
|
||||
|
||||
@end
|
||||
43
clang/test/ARCMT/with-working-dir.m.result
Normal file
43
clang/test/ARCMT/with-working-dir.m.result
Normal file
@@ -0,0 +1,43 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
|
||||
// RUN: arcmt-test --args -working-directory %S with-working-dir.m > %t
|
||||
// RUN: diff %t %s.result
|
||||
|
||||
typedef int BOOL;
|
||||
id IhaveSideEffect();
|
||||
|
||||
@protocol NSObject
|
||||
- (BOOL)isEqual:(id)object;
|
||||
- (id)retain;
|
||||
- (oneway void)release;
|
||||
- (id)something;
|
||||
@end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
@end
|
||||
|
||||
@interface Foo : NSObject {
|
||||
id bar;
|
||||
}
|
||||
@property (retain) id bar;
|
||||
-(id)test:(id)obj;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
|
||||
@synthesize bar;
|
||||
|
||||
-(id)test:(id)obj {
|
||||
id x = self.bar;
|
||||
self.bar = obj;
|
||||
|
||||
IhaveSideEffect();
|
||||
|
||||
[self something];
|
||||
|
||||
[self something];
|
||||
|
||||
// do stuff with x;
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,3 +1,4 @@
|
||||
add_subdirectory(libclang)
|
||||
add_subdirectory(c-index-test)
|
||||
add_subdirectory(arcmt-test)
|
||||
add_subdirectory(driver)
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
CLANG_LEVEL := ..
|
||||
DIRS := driver libclang c-index-test
|
||||
DIRS := driver libclang c-index-test arcmt-test
|
||||
|
||||
include $(CLANG_LEVEL)/../../Makefile.config
|
||||
|
||||
ifeq ($(OS), $(filter $(OS), Minix))
|
||||
DIRS := $(filter-out libclang c-index-test, $(DIRS))
|
||||
DIRS := $(filter-out libclang c-index-test, arcmt-test, $(DIRS))
|
||||
endif
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
14
clang/tools/arcmt-test/CMakeLists.txt
Normal file
14
clang/tools/arcmt-test/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(LLVM_USED_LIBS
|
||||
libclang
|
||||
clangARCMigrate
|
||||
clangRewrite
|
||||
)
|
||||
|
||||
set( LLVM_LINK_COMPONENTS
|
||||
support
|
||||
mc
|
||||
)
|
||||
|
||||
add_clang_executable(arcmt-test
|
||||
arcmt-test.cpp
|
||||
)
|
||||
24
clang/tools/arcmt-test/Makefile
Normal file
24
clang/tools/arcmt-test/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
##===- tools/arcmt-test/Makefile ---------------------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
CLANG_LEVEL := ../..
|
||||
|
||||
TOOLNAME = arcmt-test
|
||||
|
||||
# No plugins, optimize startup time.
|
||||
TOOL_NO_EXPORTS = 1
|
||||
|
||||
# Don't install this. It is used for tests.
|
||||
NO_INSTALL = 1
|
||||
|
||||
LINK_COMPONENTS := support mc
|
||||
USEDLIBS = clang.a clangIndex.a clangARCMigrate.a clangRewrite.a \
|
||||
clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
|
||||
clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
253
clang/tools/arcmt-test/arcmt-test.cpp
Normal file
253
clang/tools/arcmt-test/arcmt-test.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/ARCMigrate/ARCMT.h"
|
||||
#include "clang/Frontend/ASTUnit.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/VerifyDiagnosticsClient.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace arcmt;
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
CheckOnly("check-only",
|
||||
llvm::cl::desc("Just check for issues that need to be handled manually"));
|
||||
|
||||
//static llvm::cl::opt<bool>
|
||||
//TestResultForARC("test-result",
|
||||
//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode"));
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
OutputTransformations("output-transformations",
|
||||
llvm::cl::desc("Print the source transformations"));
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings"));
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
VerboseOpt("v", llvm::cl::desc("Enable verbose output"));
|
||||
|
||||
static llvm::cl::extrahelp extraHelp(
|
||||
"\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n");
|
||||
|
||||
// This function isn't referenced outside its translation unit, but it
|
||||
// can't use the "static" keyword because its address is used for
|
||||
// GetMainExecutable (since some platforms don't support taking the
|
||||
// address of main, and some platforms can't implement GetMainExecutable
|
||||
// without being given the address of a function in the main executable).
|
||||
llvm::sys::Path GetExecutablePath(const char *Argv0) {
|
||||
// This just needs to be some symbol in the binary; C++ doesn't
|
||||
// allow taking the address of ::main however.
|
||||
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
|
||||
return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
|
||||
}
|
||||
|
||||
static void printSourceLocation(SourceLocation loc, ASTContext &Ctx,
|
||||
llvm::raw_ostream &OS);
|
||||
static void printSourceRange(CharSourceRange range, ASTContext &Ctx,
|
||||
llvm::raw_ostream &OS);
|
||||
|
||||
namespace {
|
||||
|
||||
class PrintTransforms : public MigrationProcess::RewriteListener {
|
||||
ASTContext *Ctx;
|
||||
llvm::raw_ostream &OS;
|
||||
|
||||
public:
|
||||
PrintTransforms(llvm::raw_ostream &OS)
|
||||
: Ctx(0), OS(OS) { }
|
||||
|
||||
virtual void start(ASTContext &ctx) { Ctx = &ctx; }
|
||||
virtual void finish() { Ctx = 0; }
|
||||
|
||||
virtual void insert(SourceLocation loc, llvm::StringRef text) {
|
||||
assert(Ctx);
|
||||
OS << "Insert: ";
|
||||
printSourceLocation(loc, *Ctx, OS);
|
||||
OS << " \"" << text << "\"\n";
|
||||
}
|
||||
|
||||
virtual void remove(CharSourceRange range) {
|
||||
assert(Ctx);
|
||||
OS << "Remove: ";
|
||||
printSourceRange(range, *Ctx, OS);
|
||||
OS << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static bool checkForMigration(llvm::StringRef resourcesPath,
|
||||
llvm::ArrayRef<const char *> Args) {
|
||||
DiagnosticClient *DiagClient =
|
||||
new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions());
|
||||
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
llvm::IntrusiveRefCntPtr<Diagnostic> Diags(new Diagnostic(DiagID, DiagClient));
|
||||
// Chain in -verify checker, if requested.
|
||||
VerifyDiagnosticsClient *verifyDiag = 0;
|
||||
if (VerifyDiags) {
|
||||
verifyDiag = new VerifyDiagnosticsClient(*Diags, Diags->takeClient());
|
||||
Diags->setClient(verifyDiag);
|
||||
}
|
||||
|
||||
llvm::OwningPtr<CompilerInvocation> CI;
|
||||
CI.reset(clang::createInvocationFromCommandLine(Args, Diags));
|
||||
if (!CI)
|
||||
return true;
|
||||
|
||||
if (CI->getFrontendOpts().Inputs.empty()) {
|
||||
llvm::errs() << "error: no input files\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CI->getLangOpts().ObjC1)
|
||||
return false;
|
||||
|
||||
return arcmt::checkForManualIssues(*CI,
|
||||
CI->getFrontendOpts().Inputs[0].second,
|
||||
CI->getFrontendOpts().Inputs[0].first,
|
||||
Diags->getClient());
|
||||
}
|
||||
|
||||
static void printResult(FileRemapper &remapper, llvm::raw_ostream &OS) {
|
||||
CompilerInvocation CI;
|
||||
remapper.applyMappings(CI);
|
||||
PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
|
||||
// The changed files will be in memory buffers, print them.
|
||||
for (unsigned i = 0, e = PPOpts.RemappedFileBuffers.size(); i != e; ++i) {
|
||||
const llvm::MemoryBuffer *mem = PPOpts.RemappedFileBuffers[i].second;
|
||||
OS << mem->getBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
static bool performTransformations(llvm::StringRef resourcesPath,
|
||||
llvm::ArrayRef<const char *> Args) {
|
||||
// Check first.
|
||||
if (checkForMigration(resourcesPath, Args))
|
||||
return true;
|
||||
|
||||
DiagnosticClient *DiagClient =
|
||||
new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions());
|
||||
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
llvm::IntrusiveRefCntPtr<Diagnostic> TopDiags(new Diagnostic(DiagID, DiagClient));
|
||||
|
||||
llvm::OwningPtr<CompilerInvocation> origCI;
|
||||
origCI.reset(clang::createInvocationFromCommandLine(Args, TopDiags));
|
||||
if (!origCI)
|
||||
return true;
|
||||
|
||||
if (origCI->getFrontendOpts().Inputs.empty()) {
|
||||
llvm::errs() << "error: no input files\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!origCI->getLangOpts().ObjC1)
|
||||
return false;
|
||||
|
||||
MigrationProcess migration(*origCI, DiagClient);
|
||||
|
||||
std::vector<TransformFn> transforms = arcmt::getAllTransformations();
|
||||
assert(!transforms.empty());
|
||||
|
||||
llvm::OwningPtr<PrintTransforms> transformPrinter;
|
||||
if (OutputTransformations)
|
||||
transformPrinter.reset(new PrintTransforms(llvm::outs()));
|
||||
|
||||
for (unsigned i=0, e = transforms.size(); i != e; ++i) {
|
||||
bool err = migration.applyTransform(transforms[i], transformPrinter.get());
|
||||
if (err) return true;
|
||||
|
||||
if (VerboseOpt) {
|
||||
if (i == e-1)
|
||||
llvm::errs() << "\n##### FINAL RESULT #####\n";
|
||||
else
|
||||
llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n";
|
||||
printResult(migration.getRemapper(), llvm::errs());
|
||||
llvm::errs() << "\n##########################\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!OutputTransformations)
|
||||
printResult(migration.getRemapper(), llvm::outs());
|
||||
|
||||
// FIXME: TestResultForARC
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc. functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static void printSourceLocation(SourceLocation loc, ASTContext &Ctx,
|
||||
llvm::raw_ostream &OS) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
PresumedLoc PL = SM.getPresumedLoc(loc);
|
||||
|
||||
OS << llvm::sys::path::filename(PL.getFilename());
|
||||
OS << ":" << PL.getLine() << ":"
|
||||
<< PL.getColumn();
|
||||
}
|
||||
|
||||
static void printSourceRange(CharSourceRange range, ASTContext &Ctx,
|
||||
llvm::raw_ostream &OS) {
|
||||
SourceManager &SM = Ctx.getSourceManager();
|
||||
const LangOptions &langOpts = Ctx.getLangOptions();
|
||||
|
||||
PresumedLoc PL = SM.getPresumedLoc(range.getBegin());
|
||||
|
||||
OS << llvm::sys::path::filename(PL.getFilename());
|
||||
OS << " [" << PL.getLine() << ":"
|
||||
<< PL.getColumn();
|
||||
OS << " - ";
|
||||
|
||||
SourceLocation end = range.getEnd();
|
||||
PL = SM.getPresumedLoc(end);
|
||||
|
||||
unsigned endCol = PL.getColumn() - 1;
|
||||
if (!range.isTokenRange())
|
||||
endCol += Lexer::MeasureTokenLength(end, SM, langOpts);
|
||||
OS << PL.getLine() << ":" << endCol << "]";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Command line processing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
using llvm::StringRef;
|
||||
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
|
||||
llvm::sys::PrintStackTraceOnErrorSignal();
|
||||
|
||||
std::string
|
||||
resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
|
||||
|
||||
int optargc = 0;
|
||||
for (; optargc != argc; ++optargc) {
|
||||
if (StringRef(argv[optargc]) == "--args")
|
||||
break;
|
||||
}
|
||||
llvm::cl::ParseCommandLineOptions(optargc, const_cast<char **>(argv), "arcmt-test");
|
||||
|
||||
if (optargc == argc) {
|
||||
llvm::cl::PrintHelpMessage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1);
|
||||
|
||||
if (CheckOnly)
|
||||
return checkForMigration(resourcesPath, Args);
|
||||
|
||||
return performTransformations(resourcesPath, Args);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ set( LLVM_USED_LIBS
|
||||
clangIndex
|
||||
clangLex
|
||||
clangParse
|
||||
clangARCMigrate
|
||||
clangRewrite
|
||||
clangSema
|
||||
clangSerialization
|
||||
|
||||
@@ -41,7 +41,7 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
|
||||
clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
|
||||
clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \
|
||||
clangStaticAnalyzerCore.a \
|
||||
clangAnalysis.a clangIndex.a clangRewrite.a \
|
||||
clangAnalysis.a clangIndex.a clangARCMigrate.a clangRewrite.a \
|
||||
clangAST.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/Makefile
|
||||
|
||||
@@ -365,11 +365,9 @@ my %LangMap = (
|
||||
'cp' => 'c++',
|
||||
'cpp' => 'c++',
|
||||
'cc' => 'c++',
|
||||
'ii' => 'c++',
|
||||
'i' => 'c-cpp-output',
|
||||
'm' => 'objective-c',
|
||||
'mi' => 'objective-c-cpp-output',
|
||||
'mm' => 'objective-c++'
|
||||
'mi' => 'objective-c-cpp-output'
|
||||
);
|
||||
|
||||
my %UniqueOptions = (
|
||||
@@ -382,11 +380,14 @@ my %UniqueOptions = (
|
||||
|
||||
my %LangsAccepted = (
|
||||
"objective-c" => 1,
|
||||
"c" => 1,
|
||||
"c++" => 1,
|
||||
"objective-c++" => 1
|
||||
"c" => 1
|
||||
);
|
||||
|
||||
if (defined $ENV{'CCC_ANALYZER_CPLUSPLUS'}) {
|
||||
$LangsAccepted{"c++"} = 1;
|
||||
$LangsAccepted{"objective-c++"} = 1;
|
||||
}
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Main Logic.
|
||||
##----------------------------------------------------------------------------##
|
||||
@@ -620,9 +621,9 @@ if ($Action eq 'compile' or $Action eq 'link') {
|
||||
push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
|
||||
}
|
||||
|
||||
if (defined $Analyses) {
|
||||
push @AnalyzeArgs, split '\s+', $Analyses;
|
||||
}
|
||||
# if (defined $Analyses) {
|
||||
# push @AnalyzeArgs, split '\s+', $Analyses;
|
||||
# }
|
||||
|
||||
if (defined $OutputFormat) {
|
||||
push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
|
||||
|
||||
@@ -13,7 +13,7 @@ LINK_COMPONENTS := support mc
|
||||
USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
|
||||
clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
|
||||
clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \
|
||||
clangAnalysis.a clangIndex.a clangRewrite.a \
|
||||
clangAnalysis.a clangIndex.a clangARCMigrate.a clangRewrite.a \
|
||||
clangAST.a clangLex.a clangBasic.a
|
||||
|
||||
include $(CLANG_LEVEL)/unittests/Makefile
|
||||
|
||||
Reference in New Issue
Block a user