clang API Documentation
00001 //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 00010 #include "Transforms.h" 00011 #include "clang/ARCMigrate/ARCMT.h" 00012 #include "clang/ARCMigrate/ARCMTActions.h" 00013 #include "clang/AST/ASTConsumer.h" 00014 #include "clang/AST/ASTContext.h" 00015 #include "clang/AST/Attr.h" 00016 #include "clang/AST/NSAPI.h" 00017 #include "clang/AST/ParentMap.h" 00018 #include "clang/AST/RecursiveASTVisitor.h" 00019 #include "clang/Analysis/DomainSpecific/CocoaConventions.h" 00020 #include "clang/Basic/FileManager.h" 00021 #include "clang/Edit/Commit.h" 00022 #include "clang/Edit/EditedSource.h" 00023 #include "clang/Edit/EditsReceiver.h" 00024 #include "clang/Edit/Rewriters.h" 00025 #include "clang/Frontend/CompilerInstance.h" 00026 #include "clang/Frontend/MultiplexConsumer.h" 00027 #include "clang/Lex/PPConditionalDirectiveRecord.h" 00028 #include "clang/Lex/Preprocessor.h" 00029 #include "clang/Rewrite/Core/Rewriter.h" 00030 #include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h" 00031 #include "llvm/ADT/SmallString.h" 00032 #include "llvm/Support/Path.h" 00033 #include "llvm/Support/SourceMgr.h" 00034 #include "llvm/Support/YAMLParser.h" 00035 00036 using namespace clang; 00037 using namespace arcmt; 00038 using namespace ento::objc_retain; 00039 00040 namespace { 00041 00042 class ObjCMigrateASTConsumer : public ASTConsumer { 00043 enum CF_BRIDGING_KIND { 00044 CF_BRIDGING_NONE, 00045 CF_BRIDGING_ENABLE, 00046 CF_BRIDGING_MAY_INCLUDE 00047 }; 00048 00049 void migrateDecl(Decl *D); 00050 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCContainerDecl *D); 00051 void migrateProtocolConformance(ASTContext &Ctx, 00052 const ObjCImplementationDecl *ImpDecl); 00053 void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl); 00054 bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, 00055 const TypedefDecl *TypedefDcl); 00056 void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); 00057 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, 00058 ObjCMethodDecl *OM); 00059 bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM); 00060 void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM); 00061 void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P); 00062 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, 00063 ObjCMethodDecl *OM, 00064 ObjCInstanceTypeFamily OIT_Family = OIT_None); 00065 00066 void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl); 00067 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 00068 const FunctionDecl *FuncDecl, bool ResultAnnotated); 00069 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 00070 const ObjCMethodDecl *MethodDecl, bool ResultAnnotated); 00071 00072 void AnnotateImplicitBridging(ASTContext &Ctx); 00073 00074 CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx, 00075 const FunctionDecl *FuncDecl); 00076 00077 void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); 00078 00079 void migrateAddMethodAnnotation(ASTContext &Ctx, 00080 const ObjCMethodDecl *MethodDecl); 00081 00082 void inferDesignatedInitializers(ASTContext &Ctx, 00083 const ObjCImplementationDecl *ImplD); 00084 00085 bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc); 00086 00087 public: 00088 std::string MigrateDir; 00089 unsigned ASTMigrateActions; 00090 FileID FileId; 00091 const TypedefDecl *NSIntegerTypedefed; 00092 const TypedefDecl *NSUIntegerTypedefed; 00093 std::unique_ptr<NSAPI> NSAPIObj; 00094 std::unique_ptr<edit::EditedSource> Editor; 00095 FileRemapper &Remapper; 00096 FileManager &FileMgr; 00097 const PPConditionalDirectiveRecord *PPRec; 00098 Preprocessor &PP; 00099 bool IsOutputFile; 00100 bool FoundationIncluded; 00101 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; 00102 llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates; 00103 llvm::StringMap<char> WhiteListFilenames; 00104 00105 ObjCMigrateASTConsumer(StringRef migrateDir, 00106 unsigned astMigrateActions, 00107 FileRemapper &remapper, 00108 FileManager &fileMgr, 00109 const PPConditionalDirectiveRecord *PPRec, 00110 Preprocessor &PP, 00111 bool isOutputFile, 00112 ArrayRef<std::string> WhiteList) 00113 : MigrateDir(migrateDir), 00114 ASTMigrateActions(astMigrateActions), 00115 NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr), 00116 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), 00117 IsOutputFile(isOutputFile), 00118 FoundationIncluded(false){ 00119 00120 for (ArrayRef<std::string>::iterator 00121 I = WhiteList.begin(), E = WhiteList.end(); I != E; ++I) { 00122 WhiteListFilenames.GetOrCreateValue(*I); 00123 } 00124 } 00125 00126 protected: 00127 void Initialize(ASTContext &Context) override { 00128 NSAPIObj.reset(new NSAPI(Context)); 00129 Editor.reset(new edit::EditedSource(Context.getSourceManager(), 00130 Context.getLangOpts(), 00131 PPRec)); 00132 } 00133 00134 bool HandleTopLevelDecl(DeclGroupRef DG) override { 00135 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) 00136 migrateDecl(*I); 00137 return true; 00138 } 00139 void HandleInterestingDecl(DeclGroupRef DG) override { 00140 // Ignore decls from the PCH. 00141 } 00142 void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { 00143 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); 00144 } 00145 00146 void HandleTranslationUnit(ASTContext &Ctx) override; 00147 00148 bool canModifyFile(StringRef Path) { 00149 if (WhiteListFilenames.empty()) 00150 return true; 00151 return WhiteListFilenames.find(llvm::sys::path::filename(Path)) 00152 != WhiteListFilenames.end(); 00153 } 00154 bool canModifyFile(const FileEntry *FE) { 00155 if (!FE) 00156 return false; 00157 return canModifyFile(FE->getName()); 00158 } 00159 bool canModifyFile(FileID FID) { 00160 if (FID.isInvalid()) 00161 return false; 00162 return canModifyFile(PP.getSourceManager().getFileEntryForID(FID)); 00163 } 00164 00165 bool canModify(const Decl *D) { 00166 if (!D) 00167 return false; 00168 if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D)) 00169 return canModify(CatImpl->getCategoryDecl()); 00170 if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D)) 00171 return canModify(Impl->getClassInterface()); 00172 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 00173 return canModify(cast<Decl>(MD->getDeclContext())); 00174 00175 FileID FID = PP.getSourceManager().getFileID(D->getLocation()); 00176 return canModifyFile(FID); 00177 } 00178 }; 00179 00180 } 00181 00182 ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, 00183 StringRef migrateDir, 00184 unsigned migrateAction) 00185 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), 00186 ObjCMigAction(migrateAction), 00187 CompInst(nullptr) { 00188 if (MigrateDir.empty()) 00189 MigrateDir = "."; // user current directory if none is given. 00190 } 00191 00192 std::unique_ptr<ASTConsumer> 00193 ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 00194 PPConditionalDirectiveRecord * 00195 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager()); 00196 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 00197 std::vector<std::unique_ptr<ASTConsumer>> Consumers; 00198 Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile)); 00199 Consumers.push_back(llvm::make_unique<ObjCMigrateASTConsumer>( 00200 MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec, 00201 CompInst->getPreprocessor(), false, None)); 00202 return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); 00203 } 00204 00205 bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { 00206 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), 00207 /*ignoreIfFilesChanges=*/true); 00208 CompInst = &CI; 00209 CI.getDiagnostics().setIgnoreAllWarnings(true); 00210 return true; 00211 } 00212 00213 namespace { 00214 // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp 00215 bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 00216 const Expr* Expr = FullExpr->IgnoreImpCasts(); 00217 if (isa<ArraySubscriptExpr>(Expr) || 00218 isa<CallExpr>(Expr) || 00219 isa<DeclRefExpr>(Expr) || 00220 isa<CXXNamedCastExpr>(Expr) || 00221 isa<CXXConstructExpr>(Expr) || 00222 isa<CXXThisExpr>(Expr) || 00223 isa<CXXTypeidExpr>(Expr) || 00224 isa<CXXUnresolvedConstructExpr>(Expr) || 00225 isa<ObjCMessageExpr>(Expr) || 00226 isa<ObjCPropertyRefExpr>(Expr) || 00227 isa<ObjCProtocolExpr>(Expr) || 00228 isa<MemberExpr>(Expr) || 00229 isa<ObjCIvarRefExpr>(Expr) || 00230 isa<ParenExpr>(FullExpr) || 00231 isa<ParenListExpr>(Expr) || 00232 isa<SizeOfPackExpr>(Expr)) 00233 return false; 00234 00235 return true; 00236 } 00237 00238 /// \brief - Rewrite message expression for Objective-C setter and getters into 00239 /// property-dot syntax. 00240 bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg, 00241 Preprocessor &PP, 00242 const NSAPI &NS, edit::Commit &commit, 00243 const ParentMap *PMap) { 00244 if (!Msg || Msg->isImplicit() || 00245 Msg->getReceiverKind() != ObjCMessageExpr::Instance) 00246 return false; 00247 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 00248 if (!Method) 00249 return false; 00250 if (!Method->isPropertyAccessor()) 00251 return false; 00252 00253 const ObjCInterfaceDecl *IFace = 00254 NS.getASTContext().getObjContainingInterface(Method); 00255 if (!IFace) 00256 return false; 00257 00258 const ObjCPropertyDecl *Prop = Method->findPropertyDecl(); 00259 if (!Prop) 00260 return false; 00261 00262 SourceRange MsgRange = Msg->getSourceRange(); 00263 const Expr *receiver = Msg->getInstanceReceiver(); 00264 bool NeedsParen = subscriptOperatorNeedsParens(receiver); 00265 bool IsGetter = (Msg->getNumArgs() == 0); 00266 if (IsGetter) { 00267 // Find space location range between receiver expression and getter method. 00268 SourceLocation BegLoc = receiver->getLocEnd(); 00269 BegLoc = PP.getLocForEndOfToken(BegLoc); 00270 SourceLocation EndLoc = Msg->getSelectorLoc(0); 00271 SourceRange SpaceRange(BegLoc, EndLoc); 00272 std::string PropertyDotString; 00273 // rewrite getter method expression into: receiver.property or 00274 // (receiver).property 00275 if (NeedsParen) { 00276 commit.insertBefore(receiver->getLocStart(), "("); 00277 PropertyDotString = ")."; 00278 } 00279 else 00280 PropertyDotString = "."; 00281 PropertyDotString += Prop->getName(); 00282 commit.replace(SpaceRange, PropertyDotString); 00283 00284 // remove '[' ']' 00285 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 00286 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 00287 } else { 00288 SourceRange ReceiverRange = receiver->getSourceRange(); 00289 if (NeedsParen) 00290 commit.insertWrap("(", ReceiverRange, ")"); 00291 std::string PropertyDotString = "."; 00292 PropertyDotString += Prop->getName(); 00293 PropertyDotString += " ="; 00294 const Expr*const* Args = Msg->getArgs(); 00295 const Expr *RHS = Args[0]; 00296 if (!RHS) 00297 return false; 00298 SourceLocation BegLoc = ReceiverRange.getEnd(); 00299 BegLoc = PP.getLocForEndOfToken(BegLoc); 00300 SourceLocation EndLoc = RHS->getLocStart(); 00301 EndLoc = EndLoc.getLocWithOffset(-1); 00302 SourceRange Range(BegLoc, EndLoc); 00303 commit.replace(Range, PropertyDotString); 00304 // remove '[' ']' 00305 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 00306 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 00307 } 00308 return true; 00309 } 00310 00311 00312 class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 00313 ObjCMigrateASTConsumer &Consumer; 00314 ParentMap &PMap; 00315 00316 public: 00317 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap) 00318 : Consumer(consumer), PMap(PMap) { } 00319 00320 bool shouldVisitTemplateInstantiations() const { return false; } 00321 bool shouldWalkTypesOfTypeLocs() const { return false; } 00322 00323 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 00324 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) { 00325 edit::Commit commit(*Consumer.Editor); 00326 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); 00327 Consumer.Editor->commit(commit); 00328 } 00329 00330 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) { 00331 edit::Commit commit(*Consumer.Editor); 00332 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); 00333 Consumer.Editor->commit(commit); 00334 } 00335 00336 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) { 00337 edit::Commit commit(*Consumer.Editor); 00338 rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj, 00339 commit, &PMap); 00340 Consumer.Editor->commit(commit); 00341 } 00342 00343 return true; 00344 } 00345 00346 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { 00347 // Do depth first; we want to rewrite the subexpressions first so that if 00348 // we have to move expressions we will move them already rewritten. 00349 for (Stmt::child_range range = E->children(); range; ++range) 00350 if (!TraverseStmt(*range)) 00351 return false; 00352 00353 return WalkUpFromObjCMessageExpr(E); 00354 } 00355 }; 00356 00357 class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 00358 ObjCMigrateASTConsumer &Consumer; 00359 std::unique_ptr<ParentMap> PMap; 00360 00361 public: 00362 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } 00363 00364 bool shouldVisitTemplateInstantiations() const { return false; } 00365 bool shouldWalkTypesOfTypeLocs() const { return false; } 00366 00367 bool TraverseStmt(Stmt *S) { 00368 PMap.reset(new ParentMap(S)); 00369 ObjCMigrator(Consumer, *PMap).TraverseStmt(S); 00370 return true; 00371 } 00372 }; 00373 } 00374 00375 void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { 00376 if (!D) 00377 return; 00378 if (isa<ObjCMethodDecl>(D)) 00379 return; // Wait for the ObjC container declaration. 00380 00381 BodyMigrator(*this).TraverseDecl(D); 00382 } 00383 00384 static void append_attr(std::string &PropertyString, const char *attr, 00385 bool &LParenAdded) { 00386 if (!LParenAdded) { 00387 PropertyString += "("; 00388 LParenAdded = true; 00389 } 00390 else 00391 PropertyString += ", "; 00392 PropertyString += attr; 00393 } 00394 00395 static 00396 void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString, 00397 const std::string& TypeString, 00398 const char *name) { 00399 const char *argPtr = TypeString.c_str(); 00400 int paren = 0; 00401 while (*argPtr) { 00402 switch (*argPtr) { 00403 case '(': 00404 PropertyString += *argPtr; 00405 paren++; 00406 break; 00407 case ')': 00408 PropertyString += *argPtr; 00409 paren--; 00410 break; 00411 case '^': 00412 case '*': 00413 PropertyString += (*argPtr); 00414 if (paren == 1) { 00415 PropertyString += name; 00416 name = ""; 00417 } 00418 break; 00419 default: 00420 PropertyString += *argPtr; 00421 break; 00422 } 00423 argPtr++; 00424 } 00425 } 00426 00427 static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) { 00428 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 00429 bool RetainableObject = ArgType->isObjCRetainableType(); 00430 if (RetainableObject && 00431 (propertyLifetime == Qualifiers::OCL_Strong 00432 || propertyLifetime == Qualifiers::OCL_None)) { 00433 if (const ObjCObjectPointerType *ObjPtrTy = 00434 ArgType->getAs<ObjCObjectPointerType>()) { 00435 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 00436 if (IDecl && 00437 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 00438 return "copy"; 00439 else 00440 return "strong"; 00441 } 00442 else if (ArgType->isBlockPointerType()) 00443 return "copy"; 00444 } else if (propertyLifetime == Qualifiers::OCL_Weak) 00445 // TODO. More precise determination of 'weak' attribute requires 00446 // looking into setter's implementation for backing weak ivar. 00447 return "weak"; 00448 else if (RetainableObject) 00449 return ArgType->isBlockPointerType() ? "copy" : "strong"; 00450 return nullptr; 00451 } 00452 00453 static void rewriteToObjCProperty(const ObjCMethodDecl *Getter, 00454 const ObjCMethodDecl *Setter, 00455 const NSAPI &NS, edit::Commit &commit, 00456 unsigned LengthOfPrefix, 00457 bool Atomic, bool UseNsIosOnlyMacro, 00458 bool AvailabilityArgsMatch) { 00459 ASTContext &Context = NS.getASTContext(); 00460 bool LParenAdded = false; 00461 std::string PropertyString = "@property "; 00462 if (UseNsIosOnlyMacro && Context.Idents.get("NS_NONATOMIC_IOSONLY").hasMacroDefinition()) { 00463 PropertyString += "(NS_NONATOMIC_IOSONLY"; 00464 LParenAdded = true; 00465 } else if (!Atomic) { 00466 PropertyString += "(nonatomic"; 00467 LParenAdded = true; 00468 } 00469 00470 std::string PropertyNameString = Getter->getNameAsString(); 00471 StringRef PropertyName(PropertyNameString); 00472 if (LengthOfPrefix > 0) { 00473 if (!LParenAdded) { 00474 PropertyString += "(getter="; 00475 LParenAdded = true; 00476 } 00477 else 00478 PropertyString += ", getter="; 00479 PropertyString += PropertyNameString; 00480 } 00481 // Property with no setter may be suggested as a 'readonly' property. 00482 if (!Setter) 00483 append_attr(PropertyString, "readonly", LParenAdded); 00484 00485 00486 // Short circuit 'delegate' properties that contain the name "delegate" or 00487 // "dataSource", or have exact name "target" to have 'assign' attribute. 00488 if (PropertyName.equals("target") || 00489 (PropertyName.find("delegate") != StringRef::npos) || 00490 (PropertyName.find("dataSource") != StringRef::npos)) { 00491 QualType QT = Getter->getReturnType(); 00492 if (!QT->isRealType()) 00493 append_attr(PropertyString, "assign", LParenAdded); 00494 } else if (!Setter) { 00495 QualType ResType = Context.getCanonicalType(Getter->getReturnType()); 00496 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType)) 00497 append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 00498 } else { 00499 const ParmVarDecl *argDecl = *Setter->param_begin(); 00500 QualType ArgType = Context.getCanonicalType(argDecl->getType()); 00501 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType)) 00502 append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 00503 } 00504 if (LParenAdded) 00505 PropertyString += ')'; 00506 QualType RT = Getter->getReturnType(); 00507 if (!isa<TypedefType>(RT)) { 00508 // strip off any ARC lifetime qualifier. 00509 QualType CanResultTy = Context.getCanonicalType(RT); 00510 if (CanResultTy.getQualifiers().hasObjCLifetime()) { 00511 Qualifiers Qs = CanResultTy.getQualifiers(); 00512 Qs.removeObjCLifetime(); 00513 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 00514 } 00515 } 00516 PropertyString += " "; 00517 PrintingPolicy SubPolicy(Context.getPrintingPolicy()); 00518 SubPolicy.SuppressStrongLifetime = true; 00519 SubPolicy.SuppressLifetimeQualifiers = true; 00520 std::string TypeString = RT.getAsString(SubPolicy); 00521 if (LengthOfPrefix > 0) { 00522 // property name must strip off "is" and lower case the first character 00523 // after that; e.g. isContinuous will become continuous. 00524 StringRef PropertyNameStringRef(PropertyNameString); 00525 PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix); 00526 PropertyNameString = PropertyNameStringRef; 00527 bool NoLowering = (isUppercase(PropertyNameString[0]) && 00528 PropertyNameString.size() > 1 && 00529 isUppercase(PropertyNameString[1])); 00530 if (!NoLowering) 00531 PropertyNameString[0] = toLowercase(PropertyNameString[0]); 00532 } 00533 if (RT->isBlockPointerType() || RT->isFunctionPointerType()) 00534 MigrateBlockOrFunctionPointerTypeVariable(PropertyString, 00535 TypeString, 00536 PropertyNameString.c_str()); 00537 else { 00538 char LastChar = TypeString[TypeString.size()-1]; 00539 PropertyString += TypeString; 00540 if (LastChar != '*') 00541 PropertyString += ' '; 00542 PropertyString += PropertyNameString; 00543 } 00544 SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc(); 00545 Selector GetterSelector = Getter->getSelector(); 00546 00547 SourceLocation EndGetterSelectorLoc = 00548 StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size()); 00549 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 00550 EndGetterSelectorLoc), 00551 PropertyString); 00552 if (Setter && AvailabilityArgsMatch) { 00553 SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 00554 // Get location past ';' 00555 EndLoc = EndLoc.getLocWithOffset(1); 00556 SourceLocation BeginOfSetterDclLoc = Setter->getLocStart(); 00557 // FIXME. This assumes that setter decl; is immediately preceded by eoln. 00558 // It is trying to remove the setter method decl. line entirely. 00559 BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1); 00560 commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc)); 00561 } 00562 } 00563 00564 static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) { 00565 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) { 00566 StringRef Name = CatDecl->getName(); 00567 return Name.endswith("Deprecated"); 00568 } 00569 return false; 00570 } 00571 00572 void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, 00573 ObjCContainerDecl *D) { 00574 if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D)) 00575 return; 00576 00577 for (auto *Method : D->methods()) { 00578 if (Method->isDeprecated()) 00579 continue; 00580 bool PropertyInferred = migrateProperty(Ctx, D, Method); 00581 // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to 00582 // the getter method as it ends up on the property itself which we don't want 00583 // to do unless -objcmt-returns-innerpointer-property option is on. 00584 if (!PropertyInferred || 00585 (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 00586 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 00587 migrateNsReturnsInnerPointer(Ctx, Method); 00588 } 00589 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 00590 return; 00591 00592 for (auto *Prop : D->properties()) { 00593 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 00594 !Prop->isDeprecated()) 00595 migratePropertyNsReturnsInnerPointer(Ctx, Prop); 00596 } 00597 } 00598 00599 static bool 00600 ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 00601 const ObjCImplementationDecl *ImpDecl, 00602 const ObjCInterfaceDecl *IDecl, 00603 ObjCProtocolDecl *Protocol) { 00604 // In auto-synthesis, protocol properties are not synthesized. So, 00605 // a conforming protocol must have its required properties declared 00606 // in class interface. 00607 bool HasAtleastOneRequiredProperty = false; 00608 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 00609 for (const auto *Property : PDecl->properties()) { 00610 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 00611 continue; 00612 HasAtleastOneRequiredProperty = true; 00613 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); 00614 if (R.size() == 0) { 00615 // Relax the rule and look into class's implementation for a synthesize 00616 // or dynamic declaration. Class is implementing a property coming from 00617 // another protocol. This still makes the target protocol as conforming. 00618 if (!ImpDecl->FindPropertyImplDecl( 00619 Property->getDeclName().getAsIdentifierInfo())) 00620 return false; 00621 } 00622 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { 00623 if ((ClassProperty->getPropertyAttributes() 00624 != Property->getPropertyAttributes()) || 00625 !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 00626 return false; 00627 } 00628 else 00629 return false; 00630 } 00631 00632 // At this point, all required properties in this protocol conform to those 00633 // declared in the class. 00634 // Check that class implements the required methods of the protocol too. 00635 bool HasAtleastOneRequiredMethod = false; 00636 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 00637 if (PDecl->meth_begin() == PDecl->meth_end()) 00638 return HasAtleastOneRequiredProperty; 00639 for (const auto *MD : PDecl->methods()) { 00640 if (MD->isImplicit()) 00641 continue; 00642 if (MD->getImplementationControl() == ObjCMethodDecl::Optional) 00643 continue; 00644 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); 00645 if (R.size() == 0) 00646 return false; 00647 bool match = false; 00648 HasAtleastOneRequiredMethod = true; 00649 for (unsigned I = 0, N = R.size(); I != N; ++I) 00650 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) 00651 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 00652 match = true; 00653 break; 00654 } 00655 if (!match) 00656 return false; 00657 } 00658 } 00659 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) 00660 return true; 00661 return false; 00662 } 00663 00664 static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 00665 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 00666 const NSAPI &NS, edit::Commit &commit) { 00667 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 00668 std::string ClassString; 00669 SourceLocation EndLoc = 00670 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 00671 00672 if (Protocols.empty()) { 00673 ClassString = '<'; 00674 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 00675 ClassString += ConformingProtocols[i]->getNameAsString(); 00676 if (i != (e-1)) 00677 ClassString += ", "; 00678 } 00679 ClassString += "> "; 00680 } 00681 else { 00682 ClassString = ", "; 00683 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 00684 ClassString += ConformingProtocols[i]->getNameAsString(); 00685 if (i != (e-1)) 00686 ClassString += ", "; 00687 } 00688 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 00689 EndLoc = *PL; 00690 } 00691 00692 commit.insertAfterToken(EndLoc, ClassString); 00693 return true; 00694 } 00695 00696 static StringRef GetUnsignedName(StringRef NSIntegerName) { 00697 StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName) 00698 .Case("int8_t", "uint8_t") 00699 .Case("int16_t", "uint16_t") 00700 .Case("int32_t", "uint32_t") 00701 .Case("NSInteger", "NSUInteger") 00702 .Case("int64_t", "uint64_t") 00703 .Default(NSIntegerName); 00704 return UnsignedName; 00705 } 00706 00707 static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 00708 const TypedefDecl *TypedefDcl, 00709 const NSAPI &NS, edit::Commit &commit, 00710 StringRef NSIntegerName, 00711 bool NSOptions) { 00712 std::string ClassString; 00713 if (NSOptions) { 00714 ClassString = "typedef NS_OPTIONS("; 00715 ClassString += GetUnsignedName(NSIntegerName); 00716 } 00717 else { 00718 ClassString = "typedef NS_ENUM("; 00719 ClassString += NSIntegerName; 00720 } 00721 ClassString += ", "; 00722 00723 ClassString += TypedefDcl->getIdentifier()->getName(); 00724 ClassString += ')'; 00725 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 00726 commit.replace(R, ClassString); 00727 SourceLocation EndOfEnumDclLoc = EnumDcl->getLocEnd(); 00728 EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc, 00729 NS.getASTContext(), /*IsDecl*/true); 00730 if (!EndOfEnumDclLoc.isInvalid()) { 00731 SourceRange EnumDclRange(EnumDcl->getLocStart(), EndOfEnumDclLoc); 00732 commit.insertFromRange(TypedefDcl->getLocStart(), EnumDclRange); 00733 } 00734 else 00735 return false; 00736 00737 SourceLocation EndTypedefDclLoc = TypedefDcl->getLocEnd(); 00738 EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc, 00739 NS.getASTContext(), /*IsDecl*/true); 00740 if (!EndTypedefDclLoc.isInvalid()) { 00741 SourceRange TDRange(TypedefDcl->getLocStart(), EndTypedefDclLoc); 00742 commit.remove(TDRange); 00743 } 00744 else 00745 return false; 00746 00747 EndOfEnumDclLoc = trans::findLocationAfterSemi(EnumDcl->getLocEnd(), NS.getASTContext(), 00748 /*IsDecl*/true); 00749 if (!EndOfEnumDclLoc.isInvalid()) { 00750 SourceLocation BeginOfEnumDclLoc = EnumDcl->getLocStart(); 00751 // FIXME. This assumes that enum decl; is immediately preceded by eoln. 00752 // It is trying to remove the enum decl. lines entirely. 00753 BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1); 00754 commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc)); 00755 return true; 00756 } 00757 return false; 00758 } 00759 00760 static void rewriteToNSMacroDecl(const EnumDecl *EnumDcl, 00761 const TypedefDecl *TypedefDcl, 00762 const NSAPI &NS, edit::Commit &commit, 00763 bool IsNSIntegerType) { 00764 std::string ClassString = 00765 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, "; 00766 ClassString += TypedefDcl->getIdentifier()->getName(); 00767 ClassString += ')'; 00768 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 00769 commit.replace(R, ClassString); 00770 // This is to remove spaces between '}' and typedef name. 00771 SourceLocation StartTypedefLoc = EnumDcl->getLocEnd(); 00772 StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1); 00773 SourceLocation EndTypedefLoc = TypedefDcl->getLocEnd(); 00774 00775 commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc)); 00776 } 00777 00778 static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx, 00779 const EnumDecl *EnumDcl) { 00780 bool PowerOfTwo = true; 00781 bool AllHexdecimalEnumerator = true; 00782 uint64_t MaxPowerOfTwoVal = 0; 00783 for (auto Enumerator : EnumDcl->enumerators()) { 00784 const Expr *InitExpr = Enumerator->getInitExpr(); 00785 if (!InitExpr) { 00786 PowerOfTwo = false; 00787 AllHexdecimalEnumerator = false; 00788 continue; 00789 } 00790 InitExpr = InitExpr->IgnoreParenCasts(); 00791 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 00792 if (BO->isShiftOp() || BO->isBitwiseOp()) 00793 return true; 00794 00795 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 00796 if (PowerOfTwo && EnumVal) { 00797 if (!llvm::isPowerOf2_64(EnumVal)) 00798 PowerOfTwo = false; 00799 else if (EnumVal > MaxPowerOfTwoVal) 00800 MaxPowerOfTwoVal = EnumVal; 00801 } 00802 if (AllHexdecimalEnumerator && EnumVal) { 00803 bool FoundHexdecimalEnumerator = false; 00804 SourceLocation EndLoc = Enumerator->getLocEnd(); 00805 Token Tok; 00806 if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true)) 00807 if (Tok.isLiteral() && Tok.getLength() > 2) { 00808 if (const char *StringLit = Tok.getLiteralData()) 00809 FoundHexdecimalEnumerator = 00810 (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x')); 00811 } 00812 if (!FoundHexdecimalEnumerator) 00813 AllHexdecimalEnumerator = false; 00814 } 00815 } 00816 return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2)); 00817 } 00818 00819 void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 00820 const ObjCImplementationDecl *ImpDecl) { 00821 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 00822 if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated()) 00823 return; 00824 // Find all implicit conforming protocols for this class 00825 // and make them explicit. 00826 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 00827 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 00828 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 00829 00830 for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls) 00831 if (!ExplicitProtocols.count(ProtDecl)) 00832 PotentialImplicitProtocols.push_back(ProtDecl); 00833 00834 if (PotentialImplicitProtocols.empty()) 00835 return; 00836 00837 // go through list of non-optional methods and properties in each protocol 00838 // in the PotentialImplicitProtocols list. If class implements every one of the 00839 // methods and properties, then this class conforms to this protocol. 00840 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 00841 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 00842 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 00843 PotentialImplicitProtocols[i])) 00844 ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 00845 00846 if (ConformingProtocols.empty()) 00847 return; 00848 00849 // Further reduce number of conforming protocols. If protocol P1 is in the list 00850 // protocol P2 (P2<P1>), No need to include P1. 00851 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 00852 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 00853 bool DropIt = false; 00854 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 00855 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 00856 ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 00857 if (PDecl == TargetPDecl) 00858 continue; 00859 if (PDecl->lookupProtocolNamed( 00860 TargetPDecl->getDeclName().getAsIdentifierInfo())) { 00861 DropIt = true; 00862 break; 00863 } 00864 } 00865 if (!DropIt) 00866 MinimalConformingProtocols.push_back(TargetPDecl); 00867 } 00868 if (MinimalConformingProtocols.empty()) 00869 return; 00870 edit::Commit commit(*Editor); 00871 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 00872 *NSAPIObj, commit); 00873 Editor->commit(commit); 00874 } 00875 00876 void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed( 00877 const TypedefDecl *TypedefDcl) { 00878 00879 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 00880 if (NSAPIObj->isObjCNSIntegerType(qt)) 00881 NSIntegerTypedefed = TypedefDcl; 00882 else if (NSAPIObj->isObjCNSUIntegerType(qt)) 00883 NSUIntegerTypedefed = TypedefDcl; 00884 } 00885 00886 bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 00887 const EnumDecl *EnumDcl, 00888 const TypedefDecl *TypedefDcl) { 00889 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 00890 EnumDcl->isDeprecated() || EnumDcl->getIntegerTypeSourceInfo()) 00891 return false; 00892 if (!TypedefDcl) { 00893 if (NSIntegerTypedefed) { 00894 TypedefDcl = NSIntegerTypedefed; 00895 NSIntegerTypedefed = nullptr; 00896 } 00897 else if (NSUIntegerTypedefed) { 00898 TypedefDcl = NSUIntegerTypedefed; 00899 NSUIntegerTypedefed = nullptr; 00900 } 00901 else 00902 return false; 00903 FileID FileIdOfTypedefDcl = 00904 PP.getSourceManager().getFileID(TypedefDcl->getLocation()); 00905 FileID FileIdOfEnumDcl = 00906 PP.getSourceManager().getFileID(EnumDcl->getLocation()); 00907 if (FileIdOfTypedefDcl != FileIdOfEnumDcl) 00908 return false; 00909 } 00910 if (TypedefDcl->isDeprecated()) 00911 return false; 00912 00913 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 00914 StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt); 00915 00916 if (NSIntegerName.empty()) { 00917 // Also check for typedef enum {...} TD; 00918 if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 00919 if (EnumTy->getDecl() == EnumDcl) { 00920 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 00921 if (!InsertFoundation(Ctx, TypedefDcl->getLocStart())) 00922 return false; 00923 edit::Commit commit(*Editor); 00924 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 00925 Editor->commit(commit); 00926 return true; 00927 } 00928 } 00929 return false; 00930 } 00931 00932 // We may still use NS_OPTIONS based on what we find in the enumertor list. 00933 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 00934 if (!InsertFoundation(Ctx, TypedefDcl->getLocStart())) 00935 return false; 00936 edit::Commit commit(*Editor); 00937 bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, 00938 commit, NSIntegerName, NSOptions); 00939 Editor->commit(commit); 00940 return Res; 00941 } 00942 00943 static void ReplaceWithInstancetype(ASTContext &Ctx, 00944 const ObjCMigrateASTConsumer &ASTC, 00945 ObjCMethodDecl *OM) { 00946 if (OM->getReturnType() == Ctx.getObjCInstanceType()) 00947 return; // already has instancetype. 00948 00949 SourceRange R; 00950 std::string ClassString; 00951 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 00952 TypeLoc TL = TSInfo->getTypeLoc(); 00953 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 00954 ClassString = "instancetype"; 00955 } 00956 else { 00957 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 00958 ClassString = OM->isInstanceMethod() ? '-' : '+'; 00959 ClassString += " (instancetype)"; 00960 } 00961 edit::Commit commit(*ASTC.Editor); 00962 commit.replace(R, ClassString); 00963 ASTC.Editor->commit(commit); 00964 } 00965 00966 static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC, 00967 ObjCMethodDecl *OM) { 00968 ObjCInterfaceDecl *IDecl = OM->getClassInterface(); 00969 SourceRange R; 00970 std::string ClassString; 00971 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 00972 TypeLoc TL = TSInfo->getTypeLoc(); 00973 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); { 00974 ClassString = IDecl->getName(); 00975 ClassString += "*"; 00976 } 00977 } 00978 else { 00979 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 00980 ClassString = "+ ("; 00981 ClassString += IDecl->getName(); ClassString += "*)"; 00982 } 00983 edit::Commit commit(*ASTC.Editor); 00984 commit.replace(R, ClassString); 00985 ASTC.Editor->commit(commit); 00986 } 00987 00988 void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 00989 ObjCContainerDecl *CDecl, 00990 ObjCMethodDecl *OM) { 00991 ObjCInstanceTypeFamily OIT_Family = 00992 Selector::getInstTypeMethodFamily(OM->getSelector()); 00993 00994 std::string ClassName; 00995 switch (OIT_Family) { 00996 case OIT_None: 00997 migrateFactoryMethod(Ctx, CDecl, OM); 00998 return; 00999 case OIT_Array: 01000 ClassName = "NSArray"; 01001 break; 01002 case OIT_Dictionary: 01003 ClassName = "NSDictionary"; 01004 break; 01005 case OIT_Singleton: 01006 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 01007 return; 01008 case OIT_Init: 01009 if (OM->getReturnType()->isObjCIdType()) 01010 ReplaceWithInstancetype(Ctx, *this, OM); 01011 return; 01012 case OIT_ReturnsSelf: 01013 migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf); 01014 return; 01015 } 01016 if (!OM->getReturnType()->isObjCIdType()) 01017 return; 01018 01019 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 01020 if (!IDecl) { 01021 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 01022 IDecl = CatDecl->getClassInterface(); 01023 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 01024 IDecl = ImpDecl->getClassInterface(); 01025 } 01026 if (!IDecl || 01027 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 01028 migrateFactoryMethod(Ctx, CDecl, OM); 01029 return; 01030 } 01031 ReplaceWithInstancetype(Ctx, *this, OM); 01032 } 01033 01034 static bool TypeIsInnerPointer(QualType T) { 01035 if (!T->isAnyPointerType()) 01036 return false; 01037 if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() || 01038 T->isBlockPointerType() || T->isFunctionPointerType() || 01039 ento::coreFoundation::isCFObjectRef(T)) 01040 return false; 01041 // Also, typedef-of-pointer-to-incomplete-struct is something that we assume 01042 // is not an innter pointer type. 01043 QualType OrigT = T; 01044 while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) 01045 T = TD->getDecl()->getUnderlyingType(); 01046 if (OrigT == T || !T->isPointerType()) 01047 return true; 01048 const PointerType* PT = T->getAs<PointerType>(); 01049 QualType UPointeeT = PT->getPointeeType().getUnqualifiedType(); 01050 if (UPointeeT->isRecordType()) { 01051 const RecordType *RecordTy = UPointeeT->getAs<RecordType>(); 01052 if (!RecordTy->getDecl()->isCompleteDefinition()) 01053 return false; 01054 } 01055 return true; 01056 } 01057 01058 /// \brief Check whether the two versions match. 01059 static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) { 01060 return (X == Y); 01061 } 01062 01063 /// AvailabilityAttrsMatch - This routine checks that if comparing two 01064 /// availability attributes, all their components match. It returns 01065 /// true, if not dealing with availability or when all components of 01066 /// availability attributes match. This routine is only called when 01067 /// the attributes are of the same kind. 01068 static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) { 01069 const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1); 01070 if (!AA1) 01071 return true; 01072 const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2); 01073 01074 VersionTuple Introduced1 = AA1->getIntroduced(); 01075 VersionTuple Deprecated1 = AA1->getDeprecated(); 01076 VersionTuple Obsoleted1 = AA1->getObsoleted(); 01077 bool IsUnavailable1 = AA1->getUnavailable(); 01078 VersionTuple Introduced2 = AA2->getIntroduced(); 01079 VersionTuple Deprecated2 = AA2->getDeprecated(); 01080 VersionTuple Obsoleted2 = AA2->getObsoleted(); 01081 bool IsUnavailable2 = AA2->getUnavailable(); 01082 return (versionsMatch(Introduced1, Introduced2) && 01083 versionsMatch(Deprecated1, Deprecated2) && 01084 versionsMatch(Obsoleted1, Obsoleted2) && 01085 IsUnavailable1 == IsUnavailable2); 01086 01087 } 01088 01089 static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2, 01090 bool &AvailabilityArgsMatch) { 01091 // This list is very small, so this need not be optimized. 01092 for (unsigned i = 0, e = Attrs1.size(); i != e; i++) { 01093 bool match = false; 01094 for (unsigned j = 0, f = Attrs2.size(); j != f; j++) { 01095 // Matching attribute kind only. Except for Availabilty attributes, 01096 // we are not getting into details of the attributes. For all practical purposes 01097 // this is sufficient. 01098 if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) { 01099 if (AvailabilityArgsMatch) 01100 AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]); 01101 match = true; 01102 break; 01103 } 01104 } 01105 if (!match) 01106 return false; 01107 } 01108 return true; 01109 } 01110 01111 /// AttributesMatch - This routine checks list of attributes for two 01112 /// decls. It returns false, if there is a mismatch in kind of 01113 /// attributes seen in the decls. It returns true if the two decls 01114 /// have list of same kind of attributes. Furthermore, when there 01115 /// are availability attributes in the two decls, it sets the 01116 /// AvailabilityArgsMatch to false if availability attributes have 01117 /// different versions, etc. 01118 static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2, 01119 bool &AvailabilityArgsMatch) { 01120 if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) { 01121 AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs()); 01122 return true; 01123 } 01124 AvailabilityArgsMatch = true; 01125 const AttrVec &Attrs1 = Decl1->getAttrs(); 01126 const AttrVec &Attrs2 = Decl2->getAttrs(); 01127 bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch); 01128 if (match && (Attrs2.size() > Attrs1.size())) 01129 return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch); 01130 return match; 01131 } 01132 01133 static bool IsValidIdentifier(ASTContext &Ctx, 01134 const char *Name) { 01135 if (!isIdentifierHead(Name[0])) 01136 return false; 01137 std::string NameString = Name; 01138 NameString[0] = toLowercase(NameString[0]); 01139 IdentifierInfo *II = &Ctx.Idents.get(NameString); 01140 return II->getTokenID() == tok::identifier; 01141 } 01142 01143 bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx, 01144 ObjCContainerDecl *D, 01145 ObjCMethodDecl *Method) { 01146 if (Method->isPropertyAccessor() || !Method->isInstanceMethod() || 01147 Method->param_size() != 0) 01148 return false; 01149 // Is this method candidate to be a getter? 01150 QualType GRT = Method->getReturnType(); 01151 if (GRT->isVoidType()) 01152 return false; 01153 01154 Selector GetterSelector = Method->getSelector(); 01155 ObjCInstanceTypeFamily OIT_Family = 01156 Selector::getInstTypeMethodFamily(GetterSelector); 01157 01158 if (OIT_Family != OIT_None) 01159 return false; 01160 01161 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 01162 Selector SetterSelector = 01163 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 01164 PP.getSelectorTable(), 01165 getterName); 01166 ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector); 01167 unsigned LengthOfPrefix = 0; 01168 if (!SetterMethod) { 01169 // try a different naming convention for getter: isXxxxx 01170 StringRef getterNameString = getterName->getName(); 01171 bool IsPrefix = getterNameString.startswith("is"); 01172 // Note that we don't want to change an isXXX method of retainable object 01173 // type to property (readonly or otherwise). 01174 if (IsPrefix && GRT->isObjCRetainableType()) 01175 return false; 01176 if (IsPrefix || getterNameString.startswith("get")) { 01177 LengthOfPrefix = (IsPrefix ? 2 : 3); 01178 const char *CGetterName = getterNameString.data() + LengthOfPrefix; 01179 // Make sure that first character after "is" or "get" prefix can 01180 // start an identifier. 01181 if (!IsValidIdentifier(Ctx, CGetterName)) 01182 return false; 01183 if (CGetterName[0] && isUppercase(CGetterName[0])) { 01184 getterName = &Ctx.Idents.get(CGetterName); 01185 SetterSelector = 01186 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 01187 PP.getSelectorTable(), 01188 getterName); 01189 SetterMethod = D->getInstanceMethod(SetterSelector); 01190 } 01191 } 01192 } 01193 01194 if (SetterMethod) { 01195 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0) 01196 return false; 01197 bool AvailabilityArgsMatch; 01198 if (SetterMethod->isDeprecated() || 01199 !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch)) 01200 return false; 01201 01202 // Is this a valid setter, matching the target getter? 01203 QualType SRT = SetterMethod->getReturnType(); 01204 if (!SRT->isVoidType()) 01205 return false; 01206 const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 01207 QualType ArgType = argDecl->getType(); 01208 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT)) 01209 return false; 01210 edit::Commit commit(*Editor); 01211 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit, 01212 LengthOfPrefix, 01213 (ASTMigrateActions & 01214 FrontendOptions::ObjCMT_AtomicProperty) != 0, 01215 (ASTMigrateActions & 01216 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 01217 AvailabilityArgsMatch); 01218 Editor->commit(commit); 01219 return true; 01220 } 01221 else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) { 01222 // Try a non-void method with no argument (and no setter or property of same name 01223 // as a 'readonly' property. 01224 edit::Commit commit(*Editor); 01225 rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit, 01226 LengthOfPrefix, 01227 (ASTMigrateActions & 01228 FrontendOptions::ObjCMT_AtomicProperty) != 0, 01229 (ASTMigrateActions & 01230 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 01231 /*AvailabilityArgsMatch*/false); 01232 Editor->commit(commit); 01233 return true; 01234 } 01235 return false; 01236 } 01237 01238 void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx, 01239 ObjCMethodDecl *OM) { 01240 if (OM->isImplicit() || 01241 !OM->isInstanceMethod() || 01242 OM->hasAttr<ObjCReturnsInnerPointerAttr>()) 01243 return; 01244 01245 QualType RT = OM->getReturnType(); 01246 if (!TypeIsInnerPointer(RT) || 01247 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 01248 return; 01249 01250 edit::Commit commit(*Editor); 01251 commit.insertBefore(OM->getLocEnd(), " NS_RETURNS_INNER_POINTER"); 01252 Editor->commit(commit); 01253 } 01254 01255 void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, 01256 ObjCPropertyDecl *P) { 01257 QualType T = P->getType(); 01258 01259 if (!TypeIsInnerPointer(T) || 01260 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 01261 return; 01262 edit::Commit commit(*Editor); 01263 commit.insertBefore(P->getLocEnd(), " NS_RETURNS_INNER_POINTER "); 01264 Editor->commit(commit); 01265 } 01266 01267 void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx, 01268 ObjCContainerDecl *CDecl) { 01269 if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl)) 01270 return; 01271 01272 // migrate methods which can have instancetype as their result type. 01273 for (auto *Method : CDecl->methods()) { 01274 if (Method->isDeprecated()) 01275 continue; 01276 migrateMethodInstanceType(Ctx, CDecl, Method); 01277 } 01278 } 01279 01280 void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 01281 ObjCContainerDecl *CDecl, 01282 ObjCMethodDecl *OM, 01283 ObjCInstanceTypeFamily OIT_Family) { 01284 if (OM->isInstanceMethod() || 01285 OM->getReturnType() == Ctx.getObjCInstanceType() || 01286 !OM->getReturnType()->isObjCIdType()) 01287 return; 01288 01289 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 01290 // NSYYYNamE with matching names be at least 3 characters long. 01291 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 01292 if (!IDecl) { 01293 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 01294 IDecl = CatDecl->getClassInterface(); 01295 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 01296 IDecl = ImpDecl->getClassInterface(); 01297 } 01298 if (!IDecl) 01299 return; 01300 01301 std::string StringClassName = IDecl->getName(); 01302 StringRef LoweredClassName(StringClassName); 01303 std::string StringLoweredClassName = LoweredClassName.lower(); 01304 LoweredClassName = StringLoweredClassName; 01305 01306 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); 01307 // Handle method with no name at its first selector slot; e.g. + (id):(int)x. 01308 if (!MethodIdName) 01309 return; 01310 01311 std::string MethodName = MethodIdName->getName(); 01312 if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) { 01313 StringRef STRefMethodName(MethodName); 01314 size_t len = 0; 01315 if (STRefMethodName.startswith("standard")) 01316 len = strlen("standard"); 01317 else if (STRefMethodName.startswith("shared")) 01318 len = strlen("shared"); 01319 else if (STRefMethodName.startswith("default")) 01320 len = strlen("default"); 01321 else 01322 return; 01323 MethodName = STRefMethodName.substr(len); 01324 } 01325 std::string MethodNameSubStr = MethodName.substr(0, 3); 01326 StringRef MethodNamePrefix(MethodNameSubStr); 01327 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 01328 MethodNamePrefix = StringLoweredMethodNamePrefix; 01329 size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 01330 if (Ix == StringRef::npos) 01331 return; 01332 std::string ClassNamePostfix = LoweredClassName.substr(Ix); 01333 StringRef LoweredMethodName(MethodName); 01334 std::string StringLoweredMethodName = LoweredMethodName.lower(); 01335 LoweredMethodName = StringLoweredMethodName; 01336 if (!LoweredMethodName.startswith(ClassNamePostfix)) 01337 return; 01338 if (OIT_Family == OIT_ReturnsSelf) 01339 ReplaceWithClasstype(*this, OM); 01340 else 01341 ReplaceWithInstancetype(Ctx, *this, OM); 01342 } 01343 01344 static bool IsVoidStarType(QualType Ty) { 01345 if (!Ty->isPointerType()) 01346 return false; 01347 01348 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr())) 01349 Ty = TD->getDecl()->getUnderlyingType(); 01350 01351 // Is the type void*? 01352 const PointerType* PT = Ty->getAs<PointerType>(); 01353 if (PT->getPointeeType().getUnqualifiedType()->isVoidType()) 01354 return true; 01355 return IsVoidStarType(PT->getPointeeType()); 01356 } 01357 01358 /// AuditedType - This routine audits the type AT and returns false if it is one of known 01359 /// CF object types or of the "void *" variety. It returns true if we don't care about the type 01360 /// such as a non-pointer or pointers which have no ownership issues (such as "int *"). 01361 static bool AuditedType (QualType AT) { 01362 if (!AT->isAnyPointerType() && !AT->isBlockPointerType()) 01363 return true; 01364 // FIXME. There isn't much we can say about CF pointer type; or is there? 01365 if (ento::coreFoundation::isCFObjectRef(AT) || 01366 IsVoidStarType(AT) || 01367 // If an ObjC object is type, assuming that it is not a CF function and 01368 // that it is an un-audited function. 01369 AT->isObjCObjectPointerType() || AT->isObjCBuiltinType()) 01370 return false; 01371 // All other pointers are assumed audited as harmless. 01372 return true; 01373 } 01374 01375 void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { 01376 if (CFFunctionIBCandidates.empty()) 01377 return; 01378 if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) { 01379 CFFunctionIBCandidates.clear(); 01380 FileId = FileID(); 01381 return; 01382 } 01383 // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED 01384 const Decl *FirstFD = CFFunctionIBCandidates[0]; 01385 const Decl *LastFD = 01386 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; 01387 const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; 01388 edit::Commit commit(*Editor); 01389 commit.insertBefore(FirstFD->getLocStart(), PragmaString); 01390 PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n"; 01391 SourceLocation EndLoc = LastFD->getLocEnd(); 01392 // get location just past end of function location. 01393 EndLoc = PP.getLocForEndOfToken(EndLoc); 01394 if (isa<FunctionDecl>(LastFD)) { 01395 // For Methods, EndLoc points to the ending semcolon. So, 01396 // not of these extra work is needed. 01397 Token Tok; 01398 // get locaiton of token that comes after end of function. 01399 bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); 01400 if (!Failed) 01401 EndLoc = Tok.getLocation(); 01402 } 01403 commit.insertAfterToken(EndLoc, PragmaString); 01404 Editor->commit(commit); 01405 FileId = FileID(); 01406 CFFunctionIBCandidates.clear(); 01407 } 01408 01409 void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) { 01410 if (Decl->isDeprecated()) 01411 return; 01412 01413 if (Decl->hasAttr<CFAuditedTransferAttr>()) { 01414 assert(CFFunctionIBCandidates.empty() && 01415 "Cannot have audited functions/methods inside user " 01416 "provided CF_IMPLICIT_BRIDGING_ENABLE"); 01417 return; 01418 } 01419 01420 // Finction must be annotated first. 01421 if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) { 01422 CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl); 01423 if (AuditKind == CF_BRIDGING_ENABLE) { 01424 CFFunctionIBCandidates.push_back(Decl); 01425 if (FileId.isInvalid()) 01426 FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 01427 } 01428 else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) { 01429 if (!CFFunctionIBCandidates.empty()) { 01430 CFFunctionIBCandidates.push_back(Decl); 01431 if (FileId.isInvalid()) 01432 FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 01433 } 01434 } 01435 else 01436 AnnotateImplicitBridging(Ctx); 01437 } 01438 else { 01439 migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl)); 01440 AnnotateImplicitBridging(Ctx); 01441 } 01442 } 01443 01444 void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 01445 const CallEffects &CE, 01446 const FunctionDecl *FuncDecl, 01447 bool ResultAnnotated) { 01448 // Annotate function. 01449 if (!ResultAnnotated) { 01450 RetEffect Ret = CE.getReturnValue(); 01451 const char *AnnotationString = nullptr; 01452 if (Ret.getObjKind() == RetEffect::CF) { 01453 if (Ret.isOwned() && 01454 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 01455 AnnotationString = " CF_RETURNS_RETAINED"; 01456 else if (Ret.notOwned() && 01457 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 01458 AnnotationString = " CF_RETURNS_NOT_RETAINED"; 01459 } 01460 else if (Ret.getObjKind() == RetEffect::ObjC) { 01461 if (Ret.isOwned() && 01462 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 01463 AnnotationString = " NS_RETURNS_RETAINED"; 01464 } 01465 01466 if (AnnotationString) { 01467 edit::Commit commit(*Editor); 01468 commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString); 01469 Editor->commit(commit); 01470 } 01471 } 01472 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 01473 unsigned i = 0; 01474 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 01475 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 01476 const ParmVarDecl *pd = *pi; 01477 ArgEffect AE = AEArgs[i]; 01478 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() && 01479 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 01480 edit::Commit commit(*Editor); 01481 commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 01482 Editor->commit(commit); 01483 } 01484 else if (AE == DecRefMsg && !pd->hasAttr<NSConsumedAttr>() && 01485 Ctx.Idents.get("NS_CONSUMED").hasMacroDefinition()) { 01486 edit::Commit commit(*Editor); 01487 commit.insertBefore(pd->getLocation(), "NS_CONSUMED "); 01488 Editor->commit(commit); 01489 } 01490 } 01491 } 01492 01493 01494 ObjCMigrateASTConsumer::CF_BRIDGING_KIND 01495 ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( 01496 ASTContext &Ctx, 01497 const FunctionDecl *FuncDecl) { 01498 if (FuncDecl->hasBody()) 01499 return CF_BRIDGING_NONE; 01500 01501 CallEffects CE = CallEffects::getEffect(FuncDecl); 01502 bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() || 01503 FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() || 01504 FuncDecl->hasAttr<NSReturnsRetainedAttr>() || 01505 FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() || 01506 FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 01507 01508 // Trivial case of when funciton is annotated and has no argument. 01509 if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0) 01510 return CF_BRIDGING_NONE; 01511 01512 bool ReturnCFAudited = false; 01513 if (!FuncIsReturnAnnotated) { 01514 RetEffect Ret = CE.getReturnValue(); 01515 if (Ret.getObjKind() == RetEffect::CF && 01516 (Ret.isOwned() || Ret.notOwned())) 01517 ReturnCFAudited = true; 01518 else if (!AuditedType(FuncDecl->getReturnType())) 01519 return CF_BRIDGING_NONE; 01520 } 01521 01522 // At this point result type is audited for potential inclusion. 01523 // Now, how about argument types. 01524 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 01525 unsigned i = 0; 01526 bool ArgCFAudited = false; 01527 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 01528 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 01529 const ParmVarDecl *pd = *pi; 01530 ArgEffect AE = AEArgs[i]; 01531 if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) { 01532 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>()) 01533 ArgCFAudited = true; 01534 else if (AE == IncRef) 01535 ArgCFAudited = true; 01536 } 01537 else { 01538 QualType AT = pd->getType(); 01539 if (!AuditedType(AT)) { 01540 AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated); 01541 return CF_BRIDGING_NONE; 01542 } 01543 } 01544 } 01545 if (ReturnCFAudited || ArgCFAudited) 01546 return CF_BRIDGING_ENABLE; 01547 01548 return CF_BRIDGING_MAY_INCLUDE; 01549 } 01550 01551 void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, 01552 ObjCContainerDecl *CDecl) { 01553 if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated()) 01554 return; 01555 01556 // migrate methods which can have instancetype as their result type. 01557 for (const auto *Method : CDecl->methods()) 01558 migrateCFAnnotation(Ctx, Method); 01559 } 01560 01561 void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 01562 const CallEffects &CE, 01563 const ObjCMethodDecl *MethodDecl, 01564 bool ResultAnnotated) { 01565 // Annotate function. 01566 if (!ResultAnnotated) { 01567 RetEffect Ret = CE.getReturnValue(); 01568 const char *AnnotationString = nullptr; 01569 if (Ret.getObjKind() == RetEffect::CF) { 01570 if (Ret.isOwned() && 01571 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 01572 AnnotationString = " CF_RETURNS_RETAINED"; 01573 else if (Ret.notOwned() && 01574 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 01575 AnnotationString = " CF_RETURNS_NOT_RETAINED"; 01576 } 01577 else if (Ret.getObjKind() == RetEffect::ObjC) { 01578 ObjCMethodFamily OMF = MethodDecl->getMethodFamily(); 01579 switch (OMF) { 01580 case clang::OMF_alloc: 01581 case clang::OMF_new: 01582 case clang::OMF_copy: 01583 case clang::OMF_init: 01584 case clang::OMF_mutableCopy: 01585 break; 01586 01587 default: 01588 if (Ret.isOwned() && 01589 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 01590 AnnotationString = " NS_RETURNS_RETAINED"; 01591 break; 01592 } 01593 } 01594 01595 if (AnnotationString) { 01596 edit::Commit commit(*Editor); 01597 commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString); 01598 Editor->commit(commit); 01599 } 01600 } 01601 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 01602 unsigned i = 0; 01603 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 01604 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 01605 const ParmVarDecl *pd = *pi; 01606 ArgEffect AE = AEArgs[i]; 01607 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() && 01608 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 01609 edit::Commit commit(*Editor); 01610 commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 01611 Editor->commit(commit); 01612 } 01613 } 01614 } 01615 01616 void ObjCMigrateASTConsumer::migrateAddMethodAnnotation( 01617 ASTContext &Ctx, 01618 const ObjCMethodDecl *MethodDecl) { 01619 if (MethodDecl->hasBody() || MethodDecl->isImplicit()) 01620 return; 01621 01622 CallEffects CE = CallEffects::getEffect(MethodDecl); 01623 bool MethodIsReturnAnnotated = (MethodDecl->hasAttr<CFReturnsRetainedAttr>() || 01624 MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() || 01625 MethodDecl->hasAttr<NSReturnsRetainedAttr>() || 01626 MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() || 01627 MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 01628 01629 if (CE.getReceiver() == DecRefMsg && 01630 !MethodDecl->hasAttr<NSConsumesSelfAttr>() && 01631 MethodDecl->getMethodFamily() != OMF_init && 01632 MethodDecl->getMethodFamily() != OMF_release && 01633 Ctx.Idents.get("NS_CONSUMES_SELF").hasMacroDefinition()) { 01634 edit::Commit commit(*Editor); 01635 commit.insertBefore(MethodDecl->getLocEnd(), " NS_CONSUMES_SELF"); 01636 Editor->commit(commit); 01637 } 01638 01639 // Trivial case of when funciton is annotated and has no argument. 01640 if (MethodIsReturnAnnotated && 01641 (MethodDecl->param_begin() == MethodDecl->param_end())) 01642 return; 01643 01644 if (!MethodIsReturnAnnotated) { 01645 RetEffect Ret = CE.getReturnValue(); 01646 if ((Ret.getObjKind() == RetEffect::CF || 01647 Ret.getObjKind() == RetEffect::ObjC) && 01648 (Ret.isOwned() || Ret.notOwned())) { 01649 AddCFAnnotations(Ctx, CE, MethodDecl, false); 01650 return; 01651 } else if (!AuditedType(MethodDecl->getReturnType())) 01652 return; 01653 } 01654 01655 // At this point result type is either annotated or audited. 01656 // Now, how about argument types. 01657 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 01658 unsigned i = 0; 01659 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 01660 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 01661 const ParmVarDecl *pd = *pi; 01662 ArgEffect AE = AEArgs[i]; 01663 if ((AE == DecRef && !pd->hasAttr<CFConsumedAttr>()) || AE == IncRef || 01664 !AuditedType(pd->getType())) { 01665 AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated); 01666 return; 01667 } 01668 } 01669 return; 01670 } 01671 01672 namespace { 01673 class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> { 01674 public: 01675 bool shouldVisitTemplateInstantiations() const { return false; } 01676 bool shouldWalkTypesOfTypeLocs() const { return false; } 01677 01678 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 01679 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) { 01680 if (E->getMethodFamily() == OMF_init) 01681 return false; 01682 } 01683 return true; 01684 } 01685 }; 01686 } // anonymous namespace 01687 01688 static bool hasSuperInitCall(const ObjCMethodDecl *MD) { 01689 return !SuperInitChecker().TraverseStmt(MD->getBody()); 01690 } 01691 01692 void ObjCMigrateASTConsumer::inferDesignatedInitializers( 01693 ASTContext &Ctx, 01694 const ObjCImplementationDecl *ImplD) { 01695 01696 const ObjCInterfaceDecl *IFace = ImplD->getClassInterface(); 01697 if (!IFace || IFace->hasDesignatedInitializers()) 01698 return; 01699 if (!Ctx.Idents.get("NS_DESIGNATED_INITIALIZER").hasMacroDefinition()) 01700 return; 01701 01702 for (const auto *MD : ImplD->instance_methods()) { 01703 if (MD->isDeprecated() || 01704 MD->getMethodFamily() != OMF_init || 01705 MD->isDesignatedInitializerForTheInterface()) 01706 continue; 01707 const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(), 01708 /*isInstance=*/true); 01709 if (!IFaceM) 01710 continue; 01711 if (hasSuperInitCall(MD)) { 01712 edit::Commit commit(*Editor); 01713 commit.insert(IFaceM->getLocEnd(), " NS_DESIGNATED_INITIALIZER"); 01714 Editor->commit(commit); 01715 } 01716 } 01717 } 01718 01719 bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx, 01720 SourceLocation Loc) { 01721 if (FoundationIncluded) 01722 return true; 01723 if (Loc.isInvalid()) 01724 return false; 01725 edit::Commit commit(*Editor); 01726 if (Ctx.getLangOpts().Modules) 01727 commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n"); 01728 else 01729 commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n"); 01730 Editor->commit(commit); 01731 FoundationIncluded = true; 01732 return true; 01733 } 01734 01735 namespace { 01736 01737 class RewritesReceiver : public edit::EditsReceiver { 01738 Rewriter &Rewrite; 01739 01740 public: 01741 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 01742 01743 void insert(SourceLocation loc, StringRef text) override { 01744 Rewrite.InsertText(loc, text); 01745 } 01746 void replace(CharSourceRange range, StringRef text) override { 01747 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 01748 } 01749 }; 01750 01751 class JSONEditWriter : public edit::EditsReceiver { 01752 SourceManager &SourceMgr; 01753 llvm::raw_ostream &OS; 01754 01755 public: 01756 JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS) 01757 : SourceMgr(SM), OS(OS) { 01758 OS << "[\n"; 01759 } 01760 ~JSONEditWriter() { 01761 OS << "]\n"; 01762 } 01763 01764 private: 01765 struct EntryWriter { 01766 SourceManager &SourceMgr; 01767 llvm::raw_ostream &OS; 01768 01769 EntryWriter(SourceManager &SM, llvm::raw_ostream &OS) 01770 : SourceMgr(SM), OS(OS) { 01771 OS << " {\n"; 01772 } 01773 ~EntryWriter() { 01774 OS << " },\n"; 01775 } 01776 01777 void writeLoc(SourceLocation Loc) { 01778 FileID FID; 01779 unsigned Offset; 01780 std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc); 01781 assert(!FID.isInvalid()); 01782 SmallString<200> Path = 01783 StringRef(SourceMgr.getFileEntryForID(FID)->getName()); 01784 llvm::sys::fs::make_absolute(Path); 01785 OS << " \"file\": \""; 01786 OS.write_escaped(Path.str()) << "\",\n"; 01787 OS << " \"offset\": " << Offset << ",\n"; 01788 } 01789 01790 void writeRemove(CharSourceRange Range) { 01791 assert(Range.isCharRange()); 01792 std::pair<FileID, unsigned> Begin = 01793 SourceMgr.getDecomposedLoc(Range.getBegin()); 01794 std::pair<FileID, unsigned> End = 01795 SourceMgr.getDecomposedLoc(Range.getEnd()); 01796 assert(Begin.first == End.first); 01797 assert(Begin.second <= End.second); 01798 unsigned Length = End.second - Begin.second; 01799 01800 OS << " \"remove\": " << Length << ",\n"; 01801 } 01802 01803 void writeText(StringRef Text) { 01804 OS << " \"text\": \""; 01805 OS.write_escaped(Text) << "\",\n"; 01806 } 01807 }; 01808 01809 void insert(SourceLocation Loc, StringRef Text) override { 01810 EntryWriter Writer(SourceMgr, OS); 01811 Writer.writeLoc(Loc); 01812 Writer.writeText(Text); 01813 } 01814 01815 void replace(CharSourceRange Range, StringRef Text) override { 01816 EntryWriter Writer(SourceMgr, OS); 01817 Writer.writeLoc(Range.getBegin()); 01818 Writer.writeRemove(Range); 01819 Writer.writeText(Text); 01820 } 01821 01822 void remove(CharSourceRange Range) override { 01823 EntryWriter Writer(SourceMgr, OS); 01824 Writer.writeLoc(Range.getBegin()); 01825 Writer.writeRemove(Range); 01826 } 01827 }; 01828 01829 } 01830 01831 void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 01832 01833 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 01834 if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) { 01835 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 01836 D != DEnd; ++D) { 01837 FileID FID = PP.getSourceManager().getFileID((*D)->getLocation()); 01838 if (!FID.isInvalid()) 01839 if (!FileId.isInvalid() && FileId != FID) { 01840 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 01841 AnnotateImplicitBridging(Ctx); 01842 } 01843 01844 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 01845 if (canModify(CDecl)) 01846 migrateObjCInterfaceDecl(Ctx, CDecl); 01847 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) { 01848 if (canModify(CatDecl)) 01849 migrateObjCInterfaceDecl(Ctx, CatDecl); 01850 } 01851 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) 01852 ObjCProtocolDecls.insert(PDecl->getCanonicalDecl()); 01853 else if (const ObjCImplementationDecl *ImpDecl = 01854 dyn_cast<ObjCImplementationDecl>(*D)) { 01855 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) && 01856 canModify(ImpDecl)) 01857 migrateProtocolConformance(Ctx, ImpDecl); 01858 } 01859 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 01860 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 01861 continue; 01862 if (!canModify(ED)) 01863 continue; 01864 DeclContext::decl_iterator N = D; 01865 if (++N != DEnd) { 01866 const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N); 01867 if (migrateNSEnumDecl(Ctx, ED, TD) && TD) 01868 D++; 01869 } 01870 else 01871 migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr); 01872 } 01873 else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) { 01874 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 01875 continue; 01876 if (!canModify(TD)) 01877 continue; 01878 DeclContext::decl_iterator N = D; 01879 if (++N == DEnd) 01880 continue; 01881 if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) { 01882 if (++N != DEnd) 01883 if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) { 01884 // prefer typedef-follows-enum to enum-follows-typedef pattern. 01885 if (migrateNSEnumDecl(Ctx, ED, TDF)) { 01886 ++D; ++D; 01887 CacheObjCNSIntegerTypedefed(TD); 01888 continue; 01889 } 01890 } 01891 if (migrateNSEnumDecl(Ctx, ED, TD)) { 01892 ++D; 01893 continue; 01894 } 01895 } 01896 CacheObjCNSIntegerTypedefed(TD); 01897 } 01898 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) { 01899 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 01900 canModify(FD)) 01901 migrateCFAnnotation(Ctx, FD); 01902 } 01903 01904 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) { 01905 bool CanModify = canModify(CDecl); 01906 // migrate methods which can have instancetype as their result type. 01907 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) && 01908 CanModify) 01909 migrateAllMethodInstaceType(Ctx, CDecl); 01910 // annotate methods with CF annotations. 01911 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 01912 CanModify) 01913 migrateARCSafeAnnotation(Ctx, CDecl); 01914 } 01915 01916 if (const ObjCImplementationDecl * 01917 ImplD = dyn_cast<ObjCImplementationDecl>(*D)) { 01918 if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) && 01919 canModify(ImplD)) 01920 inferDesignatedInitializers(Ctx, ImplD); 01921 } 01922 } 01923 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 01924 AnnotateImplicitBridging(Ctx); 01925 } 01926 01927 if (IsOutputFile) { 01928 std::error_code EC; 01929 llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::F_None); 01930 if (EC) { 01931 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 01932 Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 01933 << EC.message(); 01934 return; 01935 } 01936 01937 JSONEditWriter Writer(Ctx.getSourceManager(), OS); 01938 Editor->applyRewrites(Writer); 01939 return; 01940 } 01941 01942 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 01943 RewritesReceiver Rec(rewriter); 01944 Editor->applyRewrites(Rec); 01945 01946 for (Rewriter::buffer_iterator 01947 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 01948 FileID FID = I->first; 01949 RewriteBuffer &buf = I->second; 01950 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 01951 assert(file); 01952 SmallString<512> newText; 01953 llvm::raw_svector_ostream vecOS(newText); 01954 buf.write(vecOS); 01955 vecOS.flush(); 01956 std::unique_ptr<llvm::MemoryBuffer> memBuf( 01957 llvm::MemoryBuffer::getMemBufferCopy( 01958 StringRef(newText.data(), newText.size()), file->getName())); 01959 SmallString<64> filePath(file->getName()); 01960 FileMgr.FixupRelativePath(filePath); 01961 Remapper.remap(filePath.str(), std::move(memBuf)); 01962 } 01963 01964 if (IsOutputFile) { 01965 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 01966 } else { 01967 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 01968 } 01969 } 01970 01971 bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 01972 CI.getDiagnostics().setIgnoreAllWarnings(true); 01973 return true; 01974 } 01975 01976 static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) { 01977 using namespace llvm::sys::fs; 01978 using namespace llvm::sys::path; 01979 01980 std::vector<std::string> Filenames; 01981 if (DirPath.empty() || !is_directory(DirPath)) 01982 return Filenames; 01983 01984 std::error_code EC; 01985 directory_iterator DI = directory_iterator(DirPath, EC); 01986 directory_iterator DE; 01987 for (; !EC && DI != DE; DI = DI.increment(EC)) { 01988 if (is_regular_file(DI->path())) 01989 Filenames.push_back(filename(DI->path())); 01990 } 01991 01992 return Filenames; 01993 } 01994 01995 std::unique_ptr<ASTConsumer> 01996 MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 01997 PPConditionalDirectiveRecord * 01998 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 01999 unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction; 02000 unsigned ObjCMTOpts = ObjCMTAction; 02001 // These are companion flags, they do not enable transformations. 02002 ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty | 02003 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty); 02004 if (ObjCMTOpts == FrontendOptions::ObjCMT_None) { 02005 // If no specific option was given, enable literals+subscripting transforms 02006 // by default. 02007 ObjCMTAction |= FrontendOptions::ObjCMT_Literals | 02008 FrontendOptions::ObjCMT_Subscripting; 02009 } 02010 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 02011 std::vector<std::string> WhiteList = 02012 getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath); 02013 return llvm::make_unique<ObjCMigrateASTConsumer>( 02014 CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper, 02015 CI.getFileManager(), PPRec, CI.getPreprocessor(), 02016 /*isOutputFile=*/true, WhiteList); 02017 } 02018 02019 namespace { 02020 struct EditEntry { 02021 const FileEntry *File; 02022 unsigned Offset; 02023 unsigned RemoveLen; 02024 std::string Text; 02025 02026 EditEntry() : File(), Offset(), RemoveLen() {} 02027 }; 02028 } 02029 02030 namespace llvm { 02031 template<> struct DenseMapInfo<EditEntry> { 02032 static inline EditEntry getEmptyKey() { 02033 EditEntry Entry; 02034 Entry.Offset = unsigned(-1); 02035 return Entry; 02036 } 02037 static inline EditEntry getTombstoneKey() { 02038 EditEntry Entry; 02039 Entry.Offset = unsigned(-2); 02040 return Entry; 02041 } 02042 static unsigned getHashValue(const EditEntry& Val) { 02043 llvm::FoldingSetNodeID ID; 02044 ID.AddPointer(Val.File); 02045 ID.AddInteger(Val.Offset); 02046 ID.AddInteger(Val.RemoveLen); 02047 ID.AddString(Val.Text); 02048 return ID.ComputeHash(); 02049 } 02050 static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) { 02051 return LHS.File == RHS.File && 02052 LHS.Offset == RHS.Offset && 02053 LHS.RemoveLen == RHS.RemoveLen && 02054 LHS.Text == RHS.Text; 02055 } 02056 }; 02057 } 02058 02059 namespace { 02060 class RemapFileParser { 02061 FileManager &FileMgr; 02062 02063 public: 02064 RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { } 02065 02066 bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) { 02067 using namespace llvm::yaml; 02068 02069 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = 02070 llvm::MemoryBuffer::getFile(File); 02071 if (!FileBufOrErr) 02072 return true; 02073 02074 llvm::SourceMgr SM; 02075 Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM); 02076 document_iterator I = YAMLStream.begin(); 02077 if (I == YAMLStream.end()) 02078 return true; 02079 Node *Root = I->getRoot(); 02080 if (!Root) 02081 return true; 02082 02083 SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root); 02084 if (!SeqNode) 02085 return true; 02086 02087 for (SequenceNode::iterator 02088 AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) { 02089 MappingNode *MapNode = dyn_cast<MappingNode>(&*AI); 02090 if (!MapNode) 02091 continue; 02092 parseEdit(MapNode, Entries); 02093 } 02094 02095 return false; 02096 } 02097 02098 private: 02099 void parseEdit(llvm::yaml::MappingNode *Node, 02100 SmallVectorImpl<EditEntry> &Entries) { 02101 using namespace llvm::yaml; 02102 EditEntry Entry; 02103 bool Ignore = false; 02104 02105 for (MappingNode::iterator 02106 KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) { 02107 ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey()); 02108 if (!KeyString) 02109 continue; 02110 SmallString<10> KeyStorage; 02111 StringRef Key = KeyString->getValue(KeyStorage); 02112 02113 ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue()); 02114 if (!ValueString) 02115 continue; 02116 SmallString<64> ValueStorage; 02117 StringRef Val = ValueString->getValue(ValueStorage); 02118 02119 if (Key == "file") { 02120 const FileEntry *FE = FileMgr.getFile(Val); 02121 if (!FE) 02122 Ignore = true; 02123 Entry.File = FE; 02124 } else if (Key == "offset") { 02125 if (Val.getAsInteger(10, Entry.Offset)) 02126 Ignore = true; 02127 } else if (Key == "remove") { 02128 if (Val.getAsInteger(10, Entry.RemoveLen)) 02129 Ignore = true; 02130 } else if (Key == "text") { 02131 Entry.Text = Val; 02132 } 02133 } 02134 02135 if (!Ignore) 02136 Entries.push_back(Entry); 02137 } 02138 }; 02139 } 02140 02141 static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) { 02142 Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 02143 << Err.str(); 02144 return true; 02145 } 02146 02147 static std::string applyEditsToTemp(const FileEntry *FE, 02148 ArrayRef<EditEntry> Edits, 02149 FileManager &FileMgr, 02150 DiagnosticsEngine &Diag) { 02151 using namespace llvm::sys; 02152 02153 SourceManager SM(Diag, FileMgr); 02154 FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); 02155 LangOptions LangOpts; 02156 edit::EditedSource Editor(SM, LangOpts); 02157 for (ArrayRef<EditEntry>::iterator 02158 I = Edits.begin(), E = Edits.end(); I != E; ++I) { 02159 const EditEntry &Entry = *I; 02160 assert(Entry.File == FE); 02161 SourceLocation Loc = 02162 SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset); 02163 CharSourceRange Range; 02164 if (Entry.RemoveLen != 0) { 02165 Range = CharSourceRange::getCharRange(Loc, 02166 Loc.getLocWithOffset(Entry.RemoveLen)); 02167 } 02168 02169 edit::Commit commit(Editor); 02170 if (Range.isInvalid()) { 02171 commit.insert(Loc, Entry.Text); 02172 } else if (Entry.Text.empty()) { 02173 commit.remove(Range); 02174 } else { 02175 commit.replace(Range, Entry.Text); 02176 } 02177 Editor.commit(commit); 02178 } 02179 02180 Rewriter rewriter(SM, LangOpts); 02181 RewritesReceiver Rec(rewriter); 02182 Editor.applyRewrites(Rec); 02183 02184 const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID); 02185 SmallString<512> NewText; 02186 llvm::raw_svector_ostream OS(NewText); 02187 Buf->write(OS); 02188 OS.flush(); 02189 02190 SmallString<64> TempPath; 02191 int FD; 02192 if (fs::createTemporaryFile(path::filename(FE->getName()), 02193 path::extension(FE->getName()), FD, 02194 TempPath)) { 02195 reportDiag("Could not create file: " + TempPath.str(), Diag); 02196 return std::string(); 02197 } 02198 02199 llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true); 02200 TmpOut.write(NewText.data(), NewText.size()); 02201 TmpOut.close(); 02202 02203 return TempPath.str(); 02204 } 02205 02206 bool arcmt::getFileRemappingsFromFileList( 02207 std::vector<std::pair<std::string,std::string> > &remap, 02208 ArrayRef<StringRef> remapFiles, 02209 DiagnosticConsumer *DiagClient) { 02210 bool hasErrorOccurred = false; 02211 02212 FileSystemOptions FSOpts; 02213 FileManager FileMgr(FSOpts); 02214 RemapFileParser Parser(FileMgr); 02215 02216 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 02217 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 02218 new DiagnosticsEngine(DiagID, new DiagnosticOptions, 02219 DiagClient, /*ShouldOwnClient=*/false)); 02220 02221 typedef llvm::DenseMap<const FileEntry *, std::vector<EditEntry> > 02222 FileEditEntriesTy; 02223 FileEditEntriesTy FileEditEntries; 02224 02225 llvm::DenseSet<EditEntry> EntriesSet; 02226 02227 for (ArrayRef<StringRef>::iterator 02228 I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { 02229 SmallVector<EditEntry, 16> Entries; 02230 if (Parser.parse(*I, Entries)) 02231 continue; 02232 02233 for (SmallVectorImpl<EditEntry>::iterator 02234 EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) { 02235 EditEntry &Entry = *EI; 02236 if (!Entry.File) 02237 continue; 02238 std::pair<llvm::DenseSet<EditEntry>::iterator, bool> 02239 Insert = EntriesSet.insert(Entry); 02240 if (!Insert.second) 02241 continue; 02242 02243 FileEditEntries[Entry.File].push_back(Entry); 02244 } 02245 } 02246 02247 for (FileEditEntriesTy::iterator 02248 I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) { 02249 std::string TempFile = applyEditsToTemp(I->first, I->second, 02250 FileMgr, *Diags); 02251 if (TempFile.empty()) { 02252 hasErrorOccurred = true; 02253 continue; 02254 } 02255 02256 remap.push_back(std::make_pair(I->first->getName(), TempFile)); 02257 } 02258 02259 return hasErrorOccurred; 02260 }