diff options
author | dim <dim@FreeBSD.org> | 2014-03-21 17:53:59 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2014-03-21 17:53:59 +0000 |
commit | 9cedb8bb69b89b0f0c529937247a6a80cabdbaec (patch) | |
tree | c978f0e9ec1ab92dc8123783f30b08a7fd1e2a39 /contrib/llvm/tools/clang/lib/ARCMigrate | |
parent | 03fdc2934eb61c44c049a02b02aa974cfdd8a0eb (diff) | |
download | FreeBSD-src-9cedb8bb69b89b0f0c529937247a6a80cabdbaec.zip FreeBSD-src-9cedb8bb69b89b0f0c529937247a6a80cabdbaec.tar.gz |
MFC 261991:
Upgrade our copy of llvm/clang to 3.4 release. This version supports
all of the features in the current working draft of the upcoming C++
standard, provisionally named C++1y.
The code generator's performance is greatly increased, and the loop
auto-vectorizer is now enabled at -Os and -O2 in addition to -O3. The
PowerPC backend has made several major improvements to code generation
quality and compile time, and the X86, SPARC, ARM32, Aarch64 and SystemZ
backends have all seen major feature work.
Release notes for llvm and clang can be found here:
<http://llvm.org/releases/3.4/docs/ReleaseNotes.html>
<http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html>
MFC 262121 (by emaste):
Update lldb for clang/llvm 3.4 import
This commit largely restores the lldb source to the upstream r196259
snapshot with the addition of threaded inferior support and a few bug
fixes.
Specific upstream lldb revisions restored include:
SVN git
181387 779e6ac
181703 7bef4e2
182099 b31044e
182650 f2dcf35
182683 0d91b80
183862 15c1774
183929 99447a6
184177 0b2934b
184948 4dc3761
184954 007e7bc
186990 eebd175
Sponsored by: DARPA, AFRL
MFC 262186 (by emaste):
Fix mismerge in r262121
A break statement was lost in the merge. The error had no functional
impact, but restore it to reduce the diff against upstream.
MFC 262303:
Pull in r197521 from upstream clang trunk (by rdivacky):
Use the integrated assembler by default on FreeBSD/ppc and ppc64.
Requested by: jhibbits
MFC 262611:
Pull in r196874 from upstream llvm trunk:
Fix a crash that occurs when PWD is invalid.
MCJIT needs to be able to run in hostile environments, even when PWD
is invalid. There's no need to crash MCJIT in this case.
The obvious fix is to simply leave MCContext's CompilationDir empty
when PWD can't be determined. This way, MCJIT clients,
and other clients that link with LLVM don't need a valid working directory.
If we do want to guarantee valid CompilationDir, that should be done
only for clients of getCompilationDir(). This is as simple as checking
for an empty string.
The only current use of getCompilationDir is EmitGenDwarfInfo, which
won't conceivably run with an invalid working dir. However, in the
purely hypothetically and untestable case that this happens, the
AT_comp_dir will be omitted from the compilation_unit DIE.
This should help fix assertions occurring with ports-mgmt/tinderbox,
when it is using jails, and sometimes invalidates clang's current
working directory.
Reported by: decke
MFC 262809:
Pull in r203007 from upstream clang trunk:
Don't produce an alias between destructors with different calling conventions.
Fixes pr19007.
(Please note that is an LLVM PR identifier, not a FreeBSD one.)
This should fix Firefox and/or libxul crashes (due to problems with
regparm/stdcall calling conventions) on i386.
Reported by: multiple users on freebsd-current
PR: bin/187103
MFC 263048:
Repair recognition of "CC" as an alias for the C++ compiler, since it
was silently broken by upstream for a Windows-specific use-case.
Apparently some versions of CMake still rely on this archaic feature...
Reported by: rakuco
MFC 263049:
Garbage collect the old way of adding the libstdc++ include directories
in clang's InitHeaderSearch.cpp. This has been superseded by David
Chisnall's commit in r255321.
Moreover, if libc++ is used, the libstdc++ include directories should
not be in the search path at all. These directories are now only used
if you pass -stdlib=libstdc++.
Diffstat (limited to 'contrib/llvm/tools/clang/lib/ARCMigrate')
6 files changed, 1600 insertions, 81 deletions
diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp index a6d4876..3e429be 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp @@ -150,7 +150,7 @@ static bool HasARCRuntime(CompilerInvocation &origCI) { // and avoid unrelated complications. llvm::Triple triple(origCI.getTargetOpts().Triple); - if (triple.getOS() == llvm::Triple::IOS) + if (triple.isiOS()) return triple.getOSMajorVersion() >= 5; if (triple.getOS() == llvm::Triple::Darwin) @@ -321,10 +321,6 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, DiagClient->EndSourceFile(); errRec.FinishCapture(); - // If we are migrating code that gets the '-fobjc-arc' flag, make sure - // to remove it so that we don't get errors from normal compilation. - origCI.getLangOpts()->ObjCAutoRefCount = false; - return capturedDiags.hasErrors() || testAct.hasReportedErrors(); } @@ -374,9 +370,6 @@ static bool applyTransforms(CompilerInvocation &origCI, origCI.getLangOpts()->ObjCAutoRefCount = true; return migration.getRemapper().overwriteOriginal(*Diags); } else { - // If we are migrating code that gets the '-fobjc-arc' flag, make sure - // to remove it so that we don't get errors from normal compilation. - origCI.getLangOpts()->ObjCAutoRefCount = false; return migration.getRemapper().flushToDisk(outputDir, *Diags); } } @@ -545,7 +538,7 @@ MigrationProcess::RewriteListener::~RewriteListener() { } MigrationProcess::MigrationProcess(const CompilerInvocation &CI, DiagnosticConsumer *diagClient, StringRef outputDir) - : OrigCI(CI), DiagClient(diagClient) { + : OrigCI(CI), DiagClient(diagClient), HadARCErrors(false) { if (!outputDir.empty()) { IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); IntrusiveRefCntPtr<DiagnosticsEngine> Diags( @@ -588,6 +581,8 @@ bool MigrationProcess::applyTransform(TransformFn trans, } Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. + HadARCErrors = HadARCErrors || capturedDiags.hasErrors(); + // Don't filter diagnostics anymore. Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp index 6a8686c..a14226e 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp @@ -43,10 +43,9 @@ void FileRemapper::clear(StringRef outputDir) { std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { assert(!outputDir.empty()); - llvm::sys::Path dir(outputDir); - llvm::sys::Path infoFile = dir; - infoFile.appendComponent("remap"); - return infoFile.str(); + SmallString<128> InfoFile = outputDir; + llvm::sys::path::append(InfoFile, "remap"); + return InfoFile.str(); } bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, @@ -127,7 +126,7 @@ bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { std::string errMsg; std::string infoFile = outputPath; llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, - llvm::raw_fd_ostream::F_Binary); + llvm::sys::fs::F_Binary); if (!errMsg.empty()) return report(errMsg, Diag); @@ -147,11 +146,10 @@ bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { } else { 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) + if (fs::createTemporaryFile(path::filename(origFE->getName()), + path::extension(origFE->getName()), fd, + tempPath)) return report("Could not create file: " + tempPath.str(), Diag); llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); @@ -176,29 +174,22 @@ bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 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) - return report(StringRef("Could not copy file '") + newFE->getName() + - "' to file '" + origFE->getName() + "'", Diag); - } else { - - bool fileExists = false; - fs::exists(origFE->getName(), fileExists); - if (!fileExists) - return report(StringRef("File does not exist: ") + origFE->getName(), - Diag); + assert(I->second.is<llvm::MemoryBuffer *>()); + bool fileExists = false; + fs::exists(origFE->getName(), fileExists); + if (!fileExists) + return report(StringRef("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()) - return report(errMsg, Diag); + std::string errMsg; + llvm::raw_fd_ostream Out(origFE->getName(), errMsg, + llvm::sys::fs::F_Binary); + if (!errMsg.empty()) + return report(errMsg, Diag); - llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); - Out.write(mem->getBufferStart(), mem->getBufferSize()); - Out.close(); - } + llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); + Out.write(mem->getBufferStart(), mem->getBufferSize()); + Out.close(); } clear(outputDir); @@ -239,12 +230,6 @@ void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) { remap(getOriginalFile(filePath), memBuf); } -void FileRemapper::remap(StringRef filePath, 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]; diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp index 57fac03..cac0fb0 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/ObjCMT.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "Transforms.h" #include "clang/ARCMigrate/ARCMTActions.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -23,46 +24,101 @@ #include "clang/Lex/PPConditionalDirectiveRecord.h" #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h" +#include "clang/AST/Attr.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" using namespace clang; using namespace arcmt; +using namespace ento::objc_retain; namespace { class ObjCMigrateASTConsumer : public ASTConsumer { + enum CF_BRIDGING_KIND { + CF_BRIDGING_NONE, + CF_BRIDGING_ENABLE, + CF_BRIDGING_MAY_INCLUDE + }; + void migrateDecl(Decl *D); - + void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCContainerDecl *D); + void migrateDeprecatedAnnotation(ASTContext &Ctx, ObjCCategoryDecl *CatDecl); + void migrateProtocolConformance(ASTContext &Ctx, + const ObjCImplementationDecl *ImpDecl); + void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl); + bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, + const TypedefDecl *TypedefDcl); + void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); + void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, + ObjCMethodDecl *OM); + bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM); + void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM); + void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P); + void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, + ObjCMethodDecl *OM, + ObjCInstanceTypeFamily OIT_Family = OIT_None); + + void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl); + void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, + const FunctionDecl *FuncDecl, bool ResultAnnotated); + void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, + const ObjCMethodDecl *MethodDecl, bool ResultAnnotated); + + void AnnotateImplicitBridging(ASTContext &Ctx); + + CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx, + const FunctionDecl *FuncDecl); + + void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); + + void migrateAddMethodAnnotation(ASTContext &Ctx, + const ObjCMethodDecl *MethodDecl); public: std::string MigrateDir; - bool MigrateLiterals; - bool MigrateSubscripting; + unsigned ASTMigrateActions; + FileID FileId; + const TypedefDecl *NSIntegerTypedefed; + const TypedefDecl *NSUIntegerTypedefed; OwningPtr<NSAPI> NSAPIObj; OwningPtr<edit::EditedSource> Editor; FileRemapper &Remapper; FileManager &FileMgr; const PPConditionalDirectiveRecord *PPRec; + Preprocessor &PP; bool IsOutputFile; - + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; + llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates; + llvm::StringMap<char> WhiteListFilenames; + ObjCMigrateASTConsumer(StringRef migrateDir, - bool migrateLiterals, - bool migrateSubscripting, + unsigned astMigrateActions, FileRemapper &remapper, FileManager &fileMgr, const PPConditionalDirectiveRecord *PPRec, - bool isOutputFile = false) + Preprocessor &PP, + bool isOutputFile, + ArrayRef<std::string> WhiteList) : MigrateDir(migrateDir), - MigrateLiterals(migrateLiterals), - MigrateSubscripting(migrateSubscripting), - Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), - IsOutputFile(isOutputFile) { } + ASTMigrateActions(astMigrateActions), + NSIntegerTypedefed(0), NSUIntegerTypedefed(0), + Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), + IsOutputFile(isOutputFile) { + + for (ArrayRef<std::string>::iterator + I = WhiteList.begin(), E = WhiteList.end(); I != E; ++I) { + WhiteListFilenames.GetOrCreateValue(*I); + } + } protected: virtual void Initialize(ASTContext &Context) { NSAPIObj.reset(new NSAPI(Context)); Editor.reset(new edit::EditedSource(Context.getSourceManager(), Context.getLangOpts(), - PPRec)); + PPRec, false)); } virtual bool HandleTopLevelDecl(DeclGroupRef DG) { @@ -78,16 +134,22 @@ protected: } virtual void HandleTranslationUnit(ASTContext &Ctx); + + bool canModifyFile(StringRef Path) { + if (WhiteListFilenames.empty()) + return true; + return WhiteListFilenames.find(llvm::sys::path::filename(Path)) + != WhiteListFilenames.end(); + } }; } ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, - StringRef migrateDir, - bool migrateLiterals, - bool migrateSubscripting) + StringRef migrateDir, + unsigned migrateAction) : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), - MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting), + ObjCMigAction(migrateAction), CompInst(0) { if (MigrateDir.empty()) MigrateDir = "."; // user current directory if none is given. @@ -101,11 +163,13 @@ ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, ASTConsumer * WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir, - MigrateLiterals, - MigrateSubscripting, + ObjCMigAction, Remapper, CompInst->getFileManager(), - PPRec); + PPRec, + CompInst->getPreprocessor(), + false, + ArrayRef<std::string>()); ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer }; return new MultiplexConsumer(Consumers); } @@ -131,13 +195,13 @@ public: bool shouldWalkTypesOfTypeLocs() const { return false; } bool VisitObjCMessageExpr(ObjCMessageExpr *E) { - if (Consumer.MigrateLiterals) { + if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) { edit::Commit commit(*Consumer.Editor); edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); Consumer.Editor->commit(commit); } - if (Consumer.MigrateSubscripting) { + if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) { edit::Commit commit(*Consumer.Editor); edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); Consumer.Editor->commit(commit); @@ -184,6 +248,1322 @@ void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { BodyMigrator(*this).TraverseDecl(D); } +static void append_attr(std::string &PropertyString, const char *attr, + bool &LParenAdded) { + if (!LParenAdded) { + PropertyString += "("; + LParenAdded = true; + } + else + PropertyString += ", "; + PropertyString += attr; +} + +static +void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString, + const std::string& TypeString, + const char *name) { + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + PropertyString += *argPtr; + paren++; + break; + case ')': + PropertyString += *argPtr; + paren--; + break; + case '^': + case '*': + PropertyString += (*argPtr); + if (paren == 1) { + PropertyString += name; + name = ""; + } + break; + default: + PropertyString += *argPtr; + break; + } + argPtr++; + } +} + +static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) { + Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); + bool RetainableObject = ArgType->isObjCRetainableType(); + if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) { + if (const ObjCObjectPointerType *ObjPtrTy = + ArgType->getAs<ObjCObjectPointerType>()) { + ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); + if (IDecl && + IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) + return "copy"; + else + return "retain"; + } + else if (ArgType->isBlockPointerType()) + return "copy"; + } else if (propertyLifetime == Qualifiers::OCL_Weak) + // TODO. More precise determination of 'weak' attribute requires + // looking into setter's implementation for backing weak ivar. + return "weak"; + else if (RetainableObject) + return ArgType->isBlockPointerType() ? "copy" : "retain"; + return 0; +} + +static void rewriteToObjCProperty(const ObjCMethodDecl *Getter, + const ObjCMethodDecl *Setter, + const NSAPI &NS, edit::Commit &commit, + unsigned LengthOfPrefix, + bool Atomic, bool UseNsIosOnlyMacro, + bool AvailabilityArgsMatch) { + ASTContext &Context = NS.getASTContext(); + bool LParenAdded = false; + std::string PropertyString = "@property "; + if (UseNsIosOnlyMacro && Context.Idents.get("NS_NONATOMIC_IOSONLY").hasMacroDefinition()) { + PropertyString += "(NS_NONATOMIC_IOSONLY"; + LParenAdded = true; + } else if (!Atomic) { + PropertyString += "(nonatomic"; + LParenAdded = true; + } + + std::string PropertyNameString = Getter->getNameAsString(); + StringRef PropertyName(PropertyNameString); + if (LengthOfPrefix > 0) { + if (!LParenAdded) { + PropertyString += "(getter="; + LParenAdded = true; + } + else + PropertyString += ", getter="; + PropertyString += PropertyNameString; + } + // Property with no setter may be suggested as a 'readonly' property. + if (!Setter) { + append_attr(PropertyString, "readonly", LParenAdded); + QualType ResType = Context.getCanonicalType(Getter->getResultType()); + if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType)) + append_attr(PropertyString, MemoryManagementAttr, LParenAdded); + } + + // Short circuit 'delegate' properties that contain the name "delegate" or + // "dataSource", or have exact name "target" to have 'assign' attribute. + if (PropertyName.equals("target") || + (PropertyName.find("delegate") != StringRef::npos) || + (PropertyName.find("dataSource") != StringRef::npos)) { + QualType QT = Getter->getResultType(); + if (!QT->isRealType()) + append_attr(PropertyString, "assign", LParenAdded); + } + else if (Setter) { + const ParmVarDecl *argDecl = *Setter->param_begin(); + QualType ArgType = Context.getCanonicalType(argDecl->getType()); + if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType)) + append_attr(PropertyString, MemoryManagementAttr, LParenAdded); + } + if (LParenAdded) + PropertyString += ')'; + QualType RT = Getter->getResultType(); + if (!isa<TypedefType>(RT)) { + // strip off any ARC lifetime qualifier. + QualType CanResultTy = Context.getCanonicalType(RT); + if (CanResultTy.getQualifiers().hasObjCLifetime()) { + Qualifiers Qs = CanResultTy.getQualifiers(); + Qs.removeObjCLifetime(); + RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); + } + } + PropertyString += " "; + PrintingPolicy SubPolicy(Context.getPrintingPolicy()); + SubPolicy.SuppressStrongLifetime = true; + SubPolicy.SuppressLifetimeQualifiers = true; + std::string TypeString = RT.getAsString(SubPolicy); + if (LengthOfPrefix > 0) { + // property name must strip off "is" and lower case the first character + // after that; e.g. isContinuous will become continuous. + StringRef PropertyNameStringRef(PropertyNameString); + PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix); + PropertyNameString = PropertyNameStringRef; + bool NoLowering = (isUppercase(PropertyNameString[0]) && + PropertyNameString.size() > 1 && + isUppercase(PropertyNameString[1])); + if (!NoLowering) + PropertyNameString[0] = toLowercase(PropertyNameString[0]); + } + if (RT->isBlockPointerType() || RT->isFunctionPointerType()) + MigrateBlockOrFunctionPointerTypeVariable(PropertyString, + TypeString, + PropertyNameString.c_str()); + else { + char LastChar = TypeString[TypeString.size()-1]; + PropertyString += TypeString; + if (LastChar != '*') + PropertyString += ' '; + PropertyString += PropertyNameString; + } + SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc(); + Selector GetterSelector = Getter->getSelector(); + + SourceLocation EndGetterSelectorLoc = + StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size()); + commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), + EndGetterSelectorLoc), + PropertyString); + if (Setter && AvailabilityArgsMatch) { + SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); + // Get location past ';' + EndLoc = EndLoc.getLocWithOffset(1); + SourceLocation BeginOfSetterDclLoc = Setter->getLocStart(); + // FIXME. This assumes that setter decl; is immediately preceeded by eoln. + // It is trying to remove the setter method decl. line entirely. + BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1); + commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc)); + } +} + +void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, + ObjCContainerDecl *D) { + if (D->isDeprecated()) + return; + + for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); + M != MEnd; ++M) { + ObjCMethodDecl *Method = (*M); + if (Method->isDeprecated()) + continue; + bool PropertyInferred = migrateProperty(Ctx, D, Method); + // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to + // the getter method as it ends up on the property itself which we don't want + // to do unless -objcmt-returns-innerpointer-property option is on. + if (!PropertyInferred || + (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + migrateNsReturnsInnerPointer(Ctx, Method); + } + if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) + return; + + for (ObjCContainerDecl::prop_iterator P = D->prop_begin(), + E = D->prop_end(); P != E; ++P) { + ObjCPropertyDecl *Prop = *P; + if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && + !Prop->isDeprecated()) + migratePropertyNsReturnsInnerPointer(Ctx, Prop); + } +} + +void ObjCMigrateASTConsumer::migrateDeprecatedAnnotation(ASTContext &Ctx, + ObjCCategoryDecl *CatDecl) { + StringRef Name = CatDecl->getName(); + if (!Name.endswith("Deprecated")) + return; + + if (!Ctx.Idents.get("DEPRECATED").hasMacroDefinition()) + return; + + ObjCContainerDecl *D = cast<ObjCContainerDecl>(CatDecl); + + for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); + M != MEnd; ++M) { + ObjCMethodDecl *Method = (*M); + if (Method->isDeprecated() || Method->isImplicit()) + continue; + // Annotate with DEPRECATED + edit::Commit commit(*Editor); + commit.insertBefore(Method->getLocEnd(), " DEPRECATED"); + Editor->commit(commit); + } + for (ObjCContainerDecl::prop_iterator P = D->prop_begin(), + E = D->prop_end(); P != E; ++P) { + ObjCPropertyDecl *Prop = *P; + if (Prop->isDeprecated()) + continue; + // Annotate with DEPRECATED + edit::Commit commit(*Editor); + commit.insertAfterToken(Prop->getLocEnd(), " DEPRECATED"); + Editor->commit(commit); + } +} + +static bool +ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, + const ObjCImplementationDecl *ImpDecl, + const ObjCInterfaceDecl *IDecl, + ObjCProtocolDecl *Protocol) { + // In auto-synthesis, protocol properties are not synthesized. So, + // a conforming protocol must have its required properties declared + // in class interface. + bool HasAtleastOneRequiredProperty = false; + if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) + for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), + E = PDecl->prop_end(); P != E; ++P) { + ObjCPropertyDecl *Property = *P; + if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) + continue; + HasAtleastOneRequiredProperty = true; + DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); + if (R.size() == 0) { + // Relax the rule and look into class's implementation for a synthesize + // or dynamic declaration. Class is implementing a property coming from + // another protocol. This still makes the target protocol as conforming. + if (!ImpDecl->FindPropertyImplDecl( + Property->getDeclName().getAsIdentifierInfo())) + return false; + } + else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { + if ((ClassProperty->getPropertyAttributes() + != Property->getPropertyAttributes()) || + !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) + return false; + } + else + return false; + } + + // At this point, all required properties in this protocol conform to those + // declared in the class. + // Check that class implements the required methods of the protocol too. + bool HasAtleastOneRequiredMethod = false; + if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { + if (PDecl->meth_begin() == PDecl->meth_end()) + return HasAtleastOneRequiredProperty; + for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(), + MEnd = PDecl->meth_end(); M != MEnd; ++M) { + ObjCMethodDecl *MD = (*M); + if (MD->isImplicit()) + continue; + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) + continue; + DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); + if (R.size() == 0) + return false; + bool match = false; + HasAtleastOneRequiredMethod = true; + for (unsigned I = 0, N = R.size(); I != N; ++I) + if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) + if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { + match = true; + break; + } + if (!match) + return false; + } + } + if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) + return true; + return false; +} + +static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, + llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, + const NSAPI &NS, edit::Commit &commit) { + const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); + std::string ClassString; + SourceLocation EndLoc = + IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); + + if (Protocols.empty()) { + ClassString = '<'; + for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { + ClassString += ConformingProtocols[i]->getNameAsString(); + if (i != (e-1)) + ClassString += ", "; + } + ClassString += "> "; + } + else { + ClassString = ", "; + for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { + ClassString += ConformingProtocols[i]->getNameAsString(); + if (i != (e-1)) + ClassString += ", "; + } + ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; + EndLoc = *PL; + } + + commit.insertAfterToken(EndLoc, ClassString); + return true; +} + +static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, + const TypedefDecl *TypedefDcl, + const NSAPI &NS, edit::Commit &commit, + bool IsNSIntegerType, + bool NSOptions) { + std::string ClassString; + if (NSOptions) + ClassString = "typedef NS_OPTIONS(NSUInteger, "; + else + ClassString = + IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " + : "typedef NS_ENUM(NSUInteger, "; + + ClassString += TypedefDcl->getIdentifier()->getName(); + ClassString += ')'; + SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); + commit.replace(R, ClassString); + SourceLocation EndOfEnumDclLoc = EnumDcl->getLocEnd(); + EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc, + NS.getASTContext(), /*IsDecl*/true); + if (!EndOfEnumDclLoc.isInvalid()) { + SourceRange EnumDclRange(EnumDcl->getLocStart(), EndOfEnumDclLoc); + commit.insertFromRange(TypedefDcl->getLocStart(), EnumDclRange); + } + else + return false; + + SourceLocation EndTypedefDclLoc = TypedefDcl->getLocEnd(); + EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc, + NS.getASTContext(), /*IsDecl*/true); + if (!EndTypedefDclLoc.isInvalid()) { + SourceRange TDRange(TypedefDcl->getLocStart(), EndTypedefDclLoc); + commit.remove(TDRange); + } + else + return false; + + EndOfEnumDclLoc = trans::findLocationAfterSemi(EnumDcl->getLocEnd(), NS.getASTContext(), + /*IsDecl*/true); + if (!EndOfEnumDclLoc.isInvalid()) { + SourceLocation BeginOfEnumDclLoc = EnumDcl->getLocStart(); + // FIXME. This assumes that enum decl; is immediately preceeded by eoln. + // It is trying to remove the enum decl. lines entirely. + BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1); + commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc)); + return true; + } + return false; +} + +static void rewriteToNSMacroDecl(const EnumDecl *EnumDcl, + const TypedefDecl *TypedefDcl, + const NSAPI &NS, edit::Commit &commit, + bool IsNSIntegerType) { + std::string ClassString = + IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, "; + ClassString += TypedefDcl->getIdentifier()->getName(); + ClassString += ')'; + SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); + commit.replace(R, ClassString); + SourceLocation TypedefLoc = TypedefDcl->getLocEnd(); + commit.remove(SourceRange(TypedefLoc, TypedefLoc)); +} + +static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx, + const EnumDecl *EnumDcl) { + bool PowerOfTwo = true; + bool AllHexdecimalEnumerator = true; + uint64_t MaxPowerOfTwoVal = 0; + for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(), + EE = EnumDcl->enumerator_end(); EI != EE; ++EI) { + EnumConstantDecl *Enumerator = (*EI); + const Expr *InitExpr = Enumerator->getInitExpr(); + if (!InitExpr) { + PowerOfTwo = false; + AllHexdecimalEnumerator = false; + continue; + } + InitExpr = InitExpr->IgnoreParenCasts(); + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) + if (BO->isShiftOp() || BO->isBitwiseOp()) + return true; + + uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); + if (PowerOfTwo && EnumVal) { + if (!llvm::isPowerOf2_64(EnumVal)) + PowerOfTwo = false; + else if (EnumVal > MaxPowerOfTwoVal) + MaxPowerOfTwoVal = EnumVal; + } + if (AllHexdecimalEnumerator && EnumVal) { + bool FoundHexdecimalEnumerator = false; + SourceLocation EndLoc = Enumerator->getLocEnd(); + Token Tok; + if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true)) + if (Tok.isLiteral() && Tok.getLength() > 2) { + if (const char *StringLit = Tok.getLiteralData()) + FoundHexdecimalEnumerator = + (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x')); + } + if (!FoundHexdecimalEnumerator) + AllHexdecimalEnumerator = false; + } + } + return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2)); +} + +void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, + const ObjCImplementationDecl *ImpDecl) { + const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); + if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated()) + return; + // Find all implicit conforming protocols for this class + // and make them explicit. + llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; + Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); + llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; + + for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I = + ObjCProtocolDecls.begin(), + E = ObjCProtocolDecls.end(); I != E; ++I) + if (!ExplicitProtocols.count(*I)) + PotentialImplicitProtocols.push_back(*I); + + if (PotentialImplicitProtocols.empty()) + return; + + // go through list of non-optional methods and properties in each protocol + // in the PotentialImplicitProtocols list. If class implements every one of the + // methods and properties, then this class conforms to this protocol. + llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; + for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) + if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, + PotentialImplicitProtocols[i])) + ConformingProtocols.push_back(PotentialImplicitProtocols[i]); + + if (ConformingProtocols.empty()) + return; + + // Further reduce number of conforming protocols. If protocol P1 is in the list + // protocol P2 (P2<P1>), No need to include P1. + llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; + for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { + bool DropIt = false; + ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; + for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { + ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; + if (PDecl == TargetPDecl) + continue; + if (PDecl->lookupProtocolNamed( + TargetPDecl->getDeclName().getAsIdentifierInfo())) { + DropIt = true; + break; + } + } + if (!DropIt) + MinimalConformingProtocols.push_back(TargetPDecl); + } + edit::Commit commit(*Editor); + rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, + *NSAPIObj, commit); + Editor->commit(commit); +} + +void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed( + const TypedefDecl *TypedefDcl) { + + QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); + if (NSAPIObj->isObjCNSIntegerType(qt)) + NSIntegerTypedefed = TypedefDcl; + else if (NSAPIObj->isObjCNSUIntegerType(qt)) + NSUIntegerTypedefed = TypedefDcl; +} + +bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, + const EnumDecl *EnumDcl, + const TypedefDecl *TypedefDcl) { + if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || + EnumDcl->isDeprecated()) + return false; + if (!TypedefDcl) { + if (NSIntegerTypedefed) { + TypedefDcl = NSIntegerTypedefed; + NSIntegerTypedefed = 0; + } + else if (NSUIntegerTypedefed) { + TypedefDcl = NSUIntegerTypedefed; + NSUIntegerTypedefed = 0; + } + else + return false; + FileID FileIdOfTypedefDcl = + PP.getSourceManager().getFileID(TypedefDcl->getLocation()); + FileID FileIdOfEnumDcl = + PP.getSourceManager().getFileID(EnumDcl->getLocation()); + if (FileIdOfTypedefDcl != FileIdOfEnumDcl) + return false; + } + if (TypedefDcl->isDeprecated()) + return false; + + QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); + bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt); + bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt); + + if (!IsNSIntegerType && !IsNSUIntegerType) { + // Also check for typedef enum {...} TD; + if (const EnumType *EnumTy = qt->getAs<EnumType>()) { + if (EnumTy->getDecl() == EnumDcl) { + bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); + if (NSOptions) { + if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) + return false; + } + else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) + return false; + edit::Commit commit(*Editor); + rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); + Editor->commit(commit); + return true; + } + } + return false; + } + + // We may still use NS_OPTIONS based on what we find in the enumertor list. + bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); + // NS_ENUM must be available. + if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) + return false; + // NS_OPTIONS must be available. + if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) + return false; + edit::Commit commit(*Editor); + bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, + commit, IsNSIntegerType, NSOptions); + Editor->commit(commit); + return Res; +} + +static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC, + ObjCMethodDecl *OM) { + SourceRange R; + std::string ClassString; + if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { + TypeLoc TL = TSInfo->getTypeLoc(); + R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); + ClassString = "instancetype"; + } + else { + R = SourceRange(OM->getLocStart(), OM->getLocStart()); + ClassString = OM->isInstanceMethod() ? '-' : '+'; + ClassString += " (instancetype)"; + } + edit::Commit commit(*ASTC.Editor); + commit.replace(R, ClassString); + ASTC.Editor->commit(commit); +} + +static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC, + ObjCMethodDecl *OM) { + ObjCInterfaceDecl *IDecl = OM->getClassInterface(); + SourceRange R; + std::string ClassString; + if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { + TypeLoc TL = TSInfo->getTypeLoc(); + R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); { + ClassString = IDecl->getName(); + ClassString += "*"; + } + } + else { + R = SourceRange(OM->getLocStart(), OM->getLocStart()); + ClassString = "+ ("; + ClassString += IDecl->getName(); ClassString += "*)"; + } + edit::Commit commit(*ASTC.Editor); + commit.replace(R, ClassString); + ASTC.Editor->commit(commit); +} + +void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, + ObjCContainerDecl *CDecl, + ObjCMethodDecl *OM) { + ObjCInstanceTypeFamily OIT_Family = + Selector::getInstTypeMethodFamily(OM->getSelector()); + + std::string ClassName; + switch (OIT_Family) { + case OIT_None: + migrateFactoryMethod(Ctx, CDecl, OM); + return; + case OIT_Array: + ClassName = "NSArray"; + break; + case OIT_Dictionary: + ClassName = "NSDictionary"; + break; + case OIT_Singleton: + migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); + return; + case OIT_Init: + if (OM->getResultType()->isObjCIdType()) + ReplaceWithInstancetype(*this, OM); + return; + case OIT_ReturnsSelf: + migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf); + return; + } + if (!OM->getResultType()->isObjCIdType()) + return; + + ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); + if (!IDecl) { + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + IDecl = CatDecl->getClassInterface(); + else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) + IDecl = ImpDecl->getClassInterface(); + } + if (!IDecl || + !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { + migrateFactoryMethod(Ctx, CDecl, OM); + return; + } + ReplaceWithInstancetype(*this, OM); +} + +static bool TypeIsInnerPointer(QualType T) { + if (!T->isAnyPointerType()) + return false; + if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() || + T->isBlockPointerType() || T->isFunctionPointerType() || + ento::coreFoundation::isCFObjectRef(T)) + return false; + // Also, typedef-of-pointer-to-incomplete-struct is something that we assume + // is not an innter pointer type. + QualType OrigT = T; + while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) + T = TD->getDecl()->getUnderlyingType(); + if (OrigT == T || !T->isPointerType()) + return true; + const PointerType* PT = T->getAs<PointerType>(); + QualType UPointeeT = PT->getPointeeType().getUnqualifiedType(); + if (UPointeeT->isRecordType()) { + const RecordType *RecordTy = UPointeeT->getAs<RecordType>(); + if (!RecordTy->getDecl()->isCompleteDefinition()) + return false; + } + return true; +} + +/// \brief Check whether the two versions match. +static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) { + return (X == Y); +} + +/// AvailabilityAttrsMatch - This routine checks that if comparing two +/// availability attributes, all their components match. It returns +/// true, if not dealing with availability or when all components of +/// availability attributes match. This routine is only called when +/// the attributes are of the same kind. +static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) { + const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1); + if (!AA1) + return true; + const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2); + + VersionTuple Introduced1 = AA1->getIntroduced(); + VersionTuple Deprecated1 = AA1->getDeprecated(); + VersionTuple Obsoleted1 = AA1->getObsoleted(); + bool IsUnavailable1 = AA1->getUnavailable(); + VersionTuple Introduced2 = AA2->getIntroduced(); + VersionTuple Deprecated2 = AA2->getDeprecated(); + VersionTuple Obsoleted2 = AA2->getObsoleted(); + bool IsUnavailable2 = AA2->getUnavailable(); + return (versionsMatch(Introduced1, Introduced2) && + versionsMatch(Deprecated1, Deprecated2) && + versionsMatch(Obsoleted1, Obsoleted2) && + IsUnavailable1 == IsUnavailable2); + +} + +static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2, + bool &AvailabilityArgsMatch) { + // This list is very small, so this need not be optimized. + for (unsigned i = 0, e = Attrs1.size(); i != e; i++) { + bool match = false; + for (unsigned j = 0, f = Attrs2.size(); j != f; j++) { + // Matching attribute kind only. Except for Availabilty attributes, + // we are not getting into details of the attributes. For all practical purposes + // this is sufficient. + if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) { + if (AvailabilityArgsMatch) + AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]); + match = true; + break; + } + } + if (!match) + return false; + } + return true; +} + +/// AttributesMatch - This routine checks list of attributes for two +/// decls. It returns false, if there is a mismatch in kind of +/// attributes seen in the decls. It returns true if the two decls +/// have list of same kind of attributes. Furthermore, when there +/// are availability attributes in the two decls, it sets the +/// AvailabilityArgsMatch to false if availability attributes have +/// different versions, etc. +static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2, + bool &AvailabilityArgsMatch) { + if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) { + AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs()); + return true; + } + AvailabilityArgsMatch = true; + const AttrVec &Attrs1 = Decl1->getAttrs(); + const AttrVec &Attrs2 = Decl2->getAttrs(); + bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch); + if (match && (Attrs2.size() > Attrs1.size())) + return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch); + return match; +} + +static bool IsValidIdentifier(ASTContext &Ctx, + const char *Name) { + if (!isIdentifierHead(Name[0])) + return false; + std::string NameString = Name; + NameString[0] = toLowercase(NameString[0]); + IdentifierInfo *II = &Ctx.Idents.get(NameString); + return II->getTokenID() == tok::identifier; +} + +bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx, + ObjCContainerDecl *D, + ObjCMethodDecl *Method) { + if (Method->isPropertyAccessor() || !Method->isInstanceMethod() || + Method->param_size() != 0) + return false; + // Is this method candidate to be a getter? + QualType GRT = Method->getResultType(); + if (GRT->isVoidType()) + return false; + + Selector GetterSelector = Method->getSelector(); + ObjCInstanceTypeFamily OIT_Family = + Selector::getInstTypeMethodFamily(GetterSelector); + + if (OIT_Family != OIT_None) + return false; + + IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); + Selector SetterSelector = + SelectorTable::constructSetterSelector(PP.getIdentifierTable(), + PP.getSelectorTable(), + getterName); + ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector); + unsigned LengthOfPrefix = 0; + if (!SetterMethod) { + // try a different naming convention for getter: isXxxxx + StringRef getterNameString = getterName->getName(); + bool IsPrefix = getterNameString.startswith("is"); + // Note that we don't want to change an isXXX method of retainable object + // type to property (readonly or otherwise). + if (IsPrefix && GRT->isObjCRetainableType()) + return false; + if (IsPrefix || getterNameString.startswith("get")) { + LengthOfPrefix = (IsPrefix ? 2 : 3); + const char *CGetterName = getterNameString.data() + LengthOfPrefix; + // Make sure that first character after "is" or "get" prefix can + // start an identifier. + if (!IsValidIdentifier(Ctx, CGetterName)) + return false; + if (CGetterName[0] && isUppercase(CGetterName[0])) { + getterName = &Ctx.Idents.get(CGetterName); + SetterSelector = + SelectorTable::constructSetterSelector(PP.getIdentifierTable(), + PP.getSelectorTable(), + getterName); + SetterMethod = D->getInstanceMethod(SetterSelector); + } + } + } + + if (SetterMethod) { + if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0) + return false; + bool AvailabilityArgsMatch; + if (SetterMethod->isDeprecated() || + !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch)) + return false; + + // Is this a valid setter, matching the target getter? + QualType SRT = SetterMethod->getResultType(); + if (!SRT->isVoidType()) + return false; + const ParmVarDecl *argDecl = *SetterMethod->param_begin(); + QualType ArgType = argDecl->getType(); + if (!Ctx.hasSameUnqualifiedType(ArgType, GRT)) + return false; + edit::Commit commit(*Editor); + rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit, + LengthOfPrefix, + (ASTMigrateActions & + FrontendOptions::ObjCMT_AtomicProperty) != 0, + (ASTMigrateActions & + FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, + AvailabilityArgsMatch); + Editor->commit(commit); + return true; + } + else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) { + // Try a non-void method with no argument (and no setter or property of same name + // as a 'readonly' property. + edit::Commit commit(*Editor); + rewriteToObjCProperty(Method, 0 /*SetterMethod*/, *NSAPIObj, commit, + LengthOfPrefix, + (ASTMigrateActions & + FrontendOptions::ObjCMT_AtomicProperty) != 0, + (ASTMigrateActions & + FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, + /*AvailabilityArgsMatch*/false); + Editor->commit(commit); + return true; + } + return false; +} + +void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx, + ObjCMethodDecl *OM) { + if (OM->isImplicit() || + !OM->isInstanceMethod() || + OM->hasAttr<ObjCReturnsInnerPointerAttr>()) + return; + + QualType RT = OM->getResultType(); + if (!TypeIsInnerPointer(RT) || + !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) + return; + + edit::Commit commit(*Editor); + commit.insertBefore(OM->getLocEnd(), " NS_RETURNS_INNER_POINTER"); + Editor->commit(commit); +} + +void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, + ObjCPropertyDecl *P) { + QualType T = P->getType(); + + if (!TypeIsInnerPointer(T) || + !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) + return; + edit::Commit commit(*Editor); + commit.insertBefore(P->getLocEnd(), " NS_RETURNS_INNER_POINTER "); + Editor->commit(commit); +} + +void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx, + ObjCContainerDecl *CDecl) { + if (CDecl->isDeprecated()) + return; + + // migrate methods which can have instancetype as their result type. + for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), + MEnd = CDecl->meth_end(); + M != MEnd; ++M) { + ObjCMethodDecl *Method = (*M); + if (Method->isDeprecated()) + continue; + migrateMethodInstanceType(Ctx, CDecl, Method); + } +} + +void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, + ObjCContainerDecl *CDecl, + ObjCMethodDecl *OM, + ObjCInstanceTypeFamily OIT_Family) { + if (OM->isInstanceMethod() || + OM->getResultType() == Ctx.getObjCInstanceType() || + !OM->getResultType()->isObjCIdType()) + return; + + // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class + // NSYYYNamE with matching names be at least 3 characters long. + ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); + if (!IDecl) { + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + IDecl = CatDecl->getClassInterface(); + else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) + IDecl = ImpDecl->getClassInterface(); + } + if (!IDecl) + return; + + std::string StringClassName = IDecl->getName(); + StringRef LoweredClassName(StringClassName); + std::string StringLoweredClassName = LoweredClassName.lower(); + LoweredClassName = StringLoweredClassName; + + IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); + // Handle method with no name at its first selector slot; e.g. + (id):(int)x. + if (!MethodIdName) + return; + + std::string MethodName = MethodIdName->getName(); + if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) { + StringRef STRefMethodName(MethodName); + size_t len = 0; + if (STRefMethodName.startswith("standard")) + len = strlen("standard"); + else if (STRefMethodName.startswith("shared")) + len = strlen("shared"); + else if (STRefMethodName.startswith("default")) + len = strlen("default"); + else + return; + MethodName = STRefMethodName.substr(len); + } + std::string MethodNameSubStr = MethodName.substr(0, 3); + StringRef MethodNamePrefix(MethodNameSubStr); + std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); + MethodNamePrefix = StringLoweredMethodNamePrefix; + size_t Ix = LoweredClassName.rfind(MethodNamePrefix); + if (Ix == StringRef::npos) + return; + std::string ClassNamePostfix = LoweredClassName.substr(Ix); + StringRef LoweredMethodName(MethodName); + std::string StringLoweredMethodName = LoweredMethodName.lower(); + LoweredMethodName = StringLoweredMethodName; + if (!LoweredMethodName.startswith(ClassNamePostfix)) + return; + if (OIT_Family == OIT_ReturnsSelf) + ReplaceWithClasstype(*this, OM); + else + ReplaceWithInstancetype(*this, OM); +} + +static bool IsVoidStarType(QualType Ty) { + if (!Ty->isPointerType()) + return false; + + while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr())) + Ty = TD->getDecl()->getUnderlyingType(); + + // Is the type void*? + const PointerType* PT = Ty->getAs<PointerType>(); + if (PT->getPointeeType().getUnqualifiedType()->isVoidType()) + return true; + return IsVoidStarType(PT->getPointeeType()); +} + +/// AuditedType - This routine audits the type AT and returns false if it is one of known +/// CF object types or of the "void *" variety. It returns true if we don't care about the type +/// such as a non-pointer or pointers which have no ownership issues (such as "int *"). +static bool AuditedType (QualType AT) { + if (!AT->isAnyPointerType() && !AT->isBlockPointerType()) + return true; + // FIXME. There isn't much we can say about CF pointer type; or is there? + if (ento::coreFoundation::isCFObjectRef(AT) || + IsVoidStarType(AT) || + // If an ObjC object is type, assuming that it is not a CF function and + // that it is an un-audited function. + AT->isObjCObjectPointerType() || AT->isObjCBuiltinType()) + return false; + // All other pointers are assumed audited as harmless. + return true; +} + +void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { + if (CFFunctionIBCandidates.empty()) + return; + if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) { + CFFunctionIBCandidates.clear(); + FileId = FileID(); + return; + } + // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED + const Decl *FirstFD = CFFunctionIBCandidates[0]; + const Decl *LastFD = + CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; + const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; + edit::Commit commit(*Editor); + commit.insertBefore(FirstFD->getLocStart(), PragmaString); + PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n"; + SourceLocation EndLoc = LastFD->getLocEnd(); + // get location just past end of function location. + EndLoc = PP.getLocForEndOfToken(EndLoc); + if (isa<FunctionDecl>(LastFD)) { + // For Methods, EndLoc points to the ending semcolon. So, + // not of these extra work is needed. + Token Tok; + // get locaiton of token that comes after end of function. + bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); + if (!Failed) + EndLoc = Tok.getLocation(); + } + commit.insertAfterToken(EndLoc, PragmaString); + Editor->commit(commit); + FileId = FileID(); + CFFunctionIBCandidates.clear(); +} + +void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) { + if (Decl->isDeprecated()) + return; + + if (Decl->hasAttr<CFAuditedTransferAttr>()) { + assert(CFFunctionIBCandidates.empty() && + "Cannot have audited functions/methods inside user " + "provided CF_IMPLICIT_BRIDGING_ENABLE"); + return; + } + + // Finction must be annotated first. + if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) { + CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl); + if (AuditKind == CF_BRIDGING_ENABLE) { + CFFunctionIBCandidates.push_back(Decl); + if (FileId.isInvalid()) + FileId = PP.getSourceManager().getFileID(Decl->getLocation()); + } + else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) { + if (!CFFunctionIBCandidates.empty()) { + CFFunctionIBCandidates.push_back(Decl); + if (FileId.isInvalid()) + FileId = PP.getSourceManager().getFileID(Decl->getLocation()); + } + } + else + AnnotateImplicitBridging(Ctx); + } + else { + migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl)); + AnnotateImplicitBridging(Ctx); + } +} + +void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, + const CallEffects &CE, + const FunctionDecl *FuncDecl, + bool ResultAnnotated) { + // Annotate function. + if (!ResultAnnotated) { + RetEffect Ret = CE.getReturnValue(); + const char *AnnotationString = 0; + if (Ret.getObjKind() == RetEffect::CF) { + if (Ret.isOwned() && + Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_RETAINED"; + else if (Ret.notOwned() && + Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_NOT_RETAINED"; + } + else if (Ret.getObjKind() == RetEffect::ObjC) { + if (Ret.isOwned() && + Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) + AnnotationString = " NS_RETURNS_RETAINED"; + } + + if (AnnotationString) { + edit::Commit commit(*Editor); + commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString); + Editor->commit(commit); + } + } + llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); + unsigned i = 0; + for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), + pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { + const ParmVarDecl *pd = *pi; + ArgEffect AE = AEArgs[i]; + if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() && + Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { + edit::Commit commit(*Editor); + commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); + Editor->commit(commit); + } + else if (AE == DecRefMsg && !pd->getAttr<NSConsumedAttr>() && + Ctx.Idents.get("NS_CONSUMED").hasMacroDefinition()) { + edit::Commit commit(*Editor); + commit.insertBefore(pd->getLocation(), "NS_CONSUMED "); + Editor->commit(commit); + } + } +} + + +ObjCMigrateASTConsumer::CF_BRIDGING_KIND + ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( + ASTContext &Ctx, + const FunctionDecl *FuncDecl) { + if (FuncDecl->hasBody()) + return CF_BRIDGING_NONE; + + CallEffects CE = CallEffects::getEffect(FuncDecl); + bool FuncIsReturnAnnotated = (FuncDecl->getAttr<CFReturnsRetainedAttr>() || + FuncDecl->getAttr<CFReturnsNotRetainedAttr>() || + FuncDecl->getAttr<NSReturnsRetainedAttr>() || + FuncDecl->getAttr<NSReturnsNotRetainedAttr>() || + FuncDecl->getAttr<NSReturnsAutoreleasedAttr>()); + + // Trivial case of when funciton is annotated and has no argument. + if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0) + return CF_BRIDGING_NONE; + + bool ReturnCFAudited = false; + if (!FuncIsReturnAnnotated) { + RetEffect Ret = CE.getReturnValue(); + if (Ret.getObjKind() == RetEffect::CF && + (Ret.isOwned() || Ret.notOwned())) + ReturnCFAudited = true; + else if (!AuditedType(FuncDecl->getResultType())) + return CF_BRIDGING_NONE; + } + + // At this point result type is audited for potential inclusion. + // Now, how about argument types. + llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); + unsigned i = 0; + bool ArgCFAudited = false; + for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), + pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { + const ParmVarDecl *pd = *pi; + ArgEffect AE = AEArgs[i]; + if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) { + if (AE == DecRef && !pd->getAttr<CFConsumedAttr>()) + ArgCFAudited = true; + else if (AE == IncRef) + ArgCFAudited = true; + } + else { + QualType AT = pd->getType(); + if (!AuditedType(AT)) { + AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated); + return CF_BRIDGING_NONE; + } + } + } + if (ReturnCFAudited || ArgCFAudited) + return CF_BRIDGING_ENABLE; + + return CF_BRIDGING_MAY_INCLUDE; +} + +void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, + ObjCContainerDecl *CDecl) { + if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated()) + return; + + // migrate methods which can have instancetype as their result type. + for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), + MEnd = CDecl->meth_end(); + M != MEnd; ++M) { + ObjCMethodDecl *Method = (*M); + migrateCFAnnotation(Ctx, Method); + } +} + +void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, + const CallEffects &CE, + const ObjCMethodDecl *MethodDecl, + bool ResultAnnotated) { + // Annotate function. + if (!ResultAnnotated) { + RetEffect Ret = CE.getReturnValue(); + const char *AnnotationString = 0; + if (Ret.getObjKind() == RetEffect::CF) { + if (Ret.isOwned() && + Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_RETAINED"; + else if (Ret.notOwned() && + Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_NOT_RETAINED"; + } + else if (Ret.getObjKind() == RetEffect::ObjC) { + ObjCMethodFamily OMF = MethodDecl->getMethodFamily(); + switch (OMF) { + case clang::OMF_alloc: + case clang::OMF_new: + case clang::OMF_copy: + case clang::OMF_init: + case clang::OMF_mutableCopy: + break; + + default: + if (Ret.isOwned() && + Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) + AnnotationString = " NS_RETURNS_RETAINED"; + break; + } + } + + if (AnnotationString) { + edit::Commit commit(*Editor); + commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString); + Editor->commit(commit); + } + } + llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); + unsigned i = 0; + for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), + pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { + const ParmVarDecl *pd = *pi; + ArgEffect AE = AEArgs[i]; + if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() && + Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { + edit::Commit commit(*Editor); + commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); + Editor->commit(commit); + } + } +} + +void ObjCMigrateASTConsumer::migrateAddMethodAnnotation( + ASTContext &Ctx, + const ObjCMethodDecl *MethodDecl) { + if (MethodDecl->hasBody() || MethodDecl->isImplicit()) + return; + + CallEffects CE = CallEffects::getEffect(MethodDecl); + bool MethodIsReturnAnnotated = (MethodDecl->getAttr<CFReturnsRetainedAttr>() || + MethodDecl->getAttr<CFReturnsNotRetainedAttr>() || + MethodDecl->getAttr<NSReturnsRetainedAttr>() || + MethodDecl->getAttr<NSReturnsNotRetainedAttr>() || + MethodDecl->getAttr<NSReturnsAutoreleasedAttr>()); + + if (CE.getReceiver() == DecRefMsg && + !MethodDecl->getAttr<NSConsumesSelfAttr>() && + MethodDecl->getMethodFamily() != OMF_init && + MethodDecl->getMethodFamily() != OMF_release && + Ctx.Idents.get("NS_CONSUMES_SELF").hasMacroDefinition()) { + edit::Commit commit(*Editor); + commit.insertBefore(MethodDecl->getLocEnd(), " NS_CONSUMES_SELF"); + Editor->commit(commit); + } + + // Trivial case of when funciton is annotated and has no argument. + if (MethodIsReturnAnnotated && + (MethodDecl->param_begin() == MethodDecl->param_end())) + return; + + if (!MethodIsReturnAnnotated) { + RetEffect Ret = CE.getReturnValue(); + if ((Ret.getObjKind() == RetEffect::CF || + Ret.getObjKind() == RetEffect::ObjC) && + (Ret.isOwned() || Ret.notOwned())) { + AddCFAnnotations(Ctx, CE, MethodDecl, false); + return; + } + else if (!AuditedType(MethodDecl->getResultType())) + return; + } + + // At this point result type is either annotated or audited. + // Now, how about argument types. + llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); + unsigned i = 0; + for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), + pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { + const ParmVarDecl *pd = *pi; + ArgEffect AE = AEArgs[i]; + if ((AE == DecRef && !pd->getAttr<CFConsumedAttr>()) || AE == IncRef || + !AuditedType(pd->getType())) { + AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated); + return; + } + } + return; +} + namespace { class RewritesReceiver : public edit::EditsReceiver { @@ -202,7 +1582,114 @@ public: } +static bool +IsReallyASystemHeader(ASTContext &Ctx, const FileEntry *file, FileID FID) { + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = + Ctx.getSourceManager().getSLocEntry(FID, &Invalid); + if (!Invalid && SEntry.isFile()) { + const SrcMgr::FileInfo &FI = SEntry.getFile(); + if (!FI.hasLineDirectives()) { + if (FI.getFileCharacteristic() == SrcMgr::C_ExternCSystem) + return true; + if (FI.getFileCharacteristic() == SrcMgr::C_System) { + // This file is in a system header directory. Continue with commiting change + // only if it is a user specified system directory because user put a + // .system_framework file in the framework directory. + StringRef Directory(file->getDir()->getName()); + size_t Ix = Directory.rfind(".framework"); + if (Ix == StringRef::npos) + return true; + std::string PatchToSystemFramework = Directory.slice(0, Ix+sizeof(".framework")); + PatchToSystemFramework += ".system_framework"; + if (!llvm::sys::fs::exists(PatchToSystemFramework.data())) + return true; + } + } + } + return false; +} + void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { + + TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); + if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) { + for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); + D != DEnd; ++D) { + FileID FID = PP.getSourceManager().getFileID((*D)->getLocation()); + if (!FID.isInvalid()) + if (!FileId.isInvalid() && FileId != FID) { + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + AnnotateImplicitBridging(Ctx); + } + + if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) + migrateObjCInterfaceDecl(Ctx, CDecl); + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) { + migrateObjCInterfaceDecl(Ctx, CatDecl); + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + migrateDeprecatedAnnotation(Ctx, CatDecl); + } + else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) + ObjCProtocolDecls.insert(PDecl); + else if (const ObjCImplementationDecl *ImpDecl = + dyn_cast<ObjCImplementationDecl>(*D)) { + if (ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) + migrateProtocolConformance(Ctx, ImpDecl); + } + else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { + if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) + continue; + DeclContext::decl_iterator N = D; + if (++N != DEnd) { + const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N); + if (migrateNSEnumDecl(Ctx, ED, TD) && TD) + D++; + } + else + migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */0); + } + else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) { + if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) + continue; + DeclContext::decl_iterator N = D; + if (++N == DEnd) + continue; + if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) { + if (++N != DEnd) + if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) { + // prefer typedef-follows-enum to enum-follows-typedef pattern. + if (migrateNSEnumDecl(Ctx, ED, TDF)) { + ++D; ++D; + CacheObjCNSIntegerTypedefed(TD); + continue; + } + } + if (migrateNSEnumDecl(Ctx, ED, TD)) { + ++D; + continue; + } + } + CacheObjCNSIntegerTypedefed(TD); + } + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) { + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + migrateCFAnnotation(Ctx, FD); + } + + if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) { + // migrate methods which can have instancetype as their result type. + if (ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) + migrateAllMethodInstaceType(Ctx, CDecl); + // annotate methods with CF annotations. + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + migrateARCSafeAnnotation(Ctx, CDecl); + } + } + if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) + AnnotateImplicitBridging(Ctx); + } + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); RewritesReceiver Rec(rewriter); Editor->applyRewrites(Rec); @@ -213,6 +1700,10 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { RewriteBuffer &buf = I->second; const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); assert(file); + if (IsReallyASystemHeader(Ctx, file, FID)) + continue; + if (!canModifyFile(file->getName())) + continue; SmallString<512> newText; llvm::raw_svector_ostream vecOS(newText); buf.write(vecOS); @@ -236,16 +1727,49 @@ bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { return true; } +static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) { + using namespace llvm::sys::fs; + using namespace llvm::sys::path; + + std::vector<std::string> Filenames; + if (DirPath.empty() || !is_directory(DirPath)) + return Filenames; + + llvm::error_code EC; + directory_iterator DI = directory_iterator(DirPath, EC); + directory_iterator DE; + for (; !EC && DI != DE; DI = DI.increment(EC)) { + if (is_regular_file(DI->path())) + Filenames.push_back(filename(DI->path())); + } + + return Filenames; +} + ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { PPConditionalDirectiveRecord * PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); + unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction; + unsigned ObjCMTOpts = ObjCMTAction; + // These are companion flags, they do not enable transformations. + ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty | + FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty); + if (ObjCMTOpts == FrontendOptions::ObjCMT_None) { + // If no specific option was given, enable literals+subscripting transforms + // by default. + ObjCMTAction |= FrontendOptions::ObjCMT_Literals | + FrontendOptions::ObjCMT_Subscripting; + } CI.getPreprocessor().addPPCallbacks(PPRec); + std::vector<std::string> WhiteList = + getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath); return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, - /*MigrateLiterals=*/true, - /*MigrateSubscripting=*/true, + ObjCMTAction, Remapper, CI.getFileManager(), PPRec, - /*isOutputFile=*/true); + CI.getPreprocessor(), + /*isOutputFile=*/true, + WhiteList); } diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp index fc4a75f..7b360c6 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -77,6 +77,13 @@ public: TraverseStmt(body); } + bool TraverseBlockDecl(BlockDecl *D) { + // ParentMap does not enter into a BlockDecl to record its stmts, so use a + // new UnbridgedCastRewriter to handle the block. + UnbridgedCastRewriter(Pass).transformBody(D->getBody(), D); + return true; + } + bool VisitCastExpr(CastExpr *E) { if (E->getCastKind() != CK_CPointerToObjCPointerCast && E->getCastKind() != CK_BitCast && @@ -148,7 +155,7 @@ private: if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage()) { + FD->isExternallyVisible()) { Expr *Arg = callE->getArg(0); if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { const Expr *sub = ICE->getSubExpr(); @@ -413,7 +420,7 @@ private: FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage()) + FD->isExternallyVisible()) return true; return false; diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp index 0872195..679b924 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.cpp @@ -49,7 +49,7 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type, return false; // iOS is always safe to use 'weak'. - if (Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::IOS) + if (Ctx.getTargetInfo().getTriple().isiOS()) AllowOnUnknownClass = true; while (const PointerType *ptr = T->getAs<PointerType>()) @@ -94,7 +94,7 @@ bool trans::isPlusOne(const Expr *E) { if (FD->isGlobal() && FD->getIdentifier() && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage() && + FD->isExternallyVisible() && ento::cocoa::isRefType(callE->getType(), "CF", FD->getIdentifier()->getName())) { StringRef fname = FD->getIdentifier()->getName(); @@ -122,8 +122,8 @@ bool trans::isPlusOne(const Expr *E) { /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. SourceLocation trans::findLocationAfterSemi(SourceLocation loc, - ASTContext &Ctx) { - SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx); + ASTContext &Ctx, bool IsDecl) { + SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl); if (SemiLoc.isInvalid()) return SourceLocation(); return SemiLoc.getLocWithOffset(1); @@ -134,7 +134,8 @@ SourceLocation trans::findLocationAfterSemi(SourceLocation loc, /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. SourceLocation trans::findSemiAfterLocation(SourceLocation loc, - ASTContext &Ctx) { + ASTContext &Ctx, + bool IsDecl) { SourceManager &SM = Ctx.getSourceManager(); if (loc.isMacroID()) { if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc)) @@ -159,8 +160,13 @@ SourceLocation trans::findSemiAfterLocation(SourceLocation loc, file.begin(), tokenBegin, file.end()); Token tok; lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::semi)) - return SourceLocation(); + if (tok.isNot(tok::semi)) { + if (!IsDecl) + return SourceLocation(); + // Declaration may be followed with other tokens; such as an __attribute, + // before ending with a semicolon. + return findSemiAfterLocation(tok.getLocation(), Ctx, /*IsDecl*/true); + } return tok.getLocation(); } @@ -198,7 +204,7 @@ bool trans::isGlobalVar(Expr *E) { E = E->IgnoreParenCasts(); if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) return DRE->getDecl()->getDeclContext()->isFileContext() && - DRE->getDecl()->hasExternalLinkage(); + DRE->getDecl()->isExternallyVisible(); if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E)) return isGlobalVar(condOp->getTrueExpr()) && isGlobalVar(condOp->getFalseExpr()); diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h index e20fe59..eab5e85 100644 --- a/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/Transforms.h @@ -167,13 +167,15 @@ bool isPlusOne(const Expr *E); /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. -SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx); +SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx, + bool IsDecl = false); /// \brief 'Loc' is the end of a statement range. This returns the location /// of the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. -SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx); +SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx, + bool IsDecl = false); bool hasSideEffects(Expr *E, ASTContext &Ctx); bool isGlobalVar(Expr *E); |