1//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "Transforms.h" 11#include "clang/ARCMigrate/ARCMT.h" 12#include "clang/ARCMigrate/ARCMTActions.h" 13#include "clang/AST/ASTConsumer.h" 14#include "clang/AST/ASTContext.h" 15#include "clang/AST/Attr.h" 16#include "clang/AST/NSAPI.h" 17#include "clang/AST/ParentMap.h" 18#include "clang/AST/RecursiveASTVisitor.h" 19#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 20#include "clang/Basic/FileManager.h" 21#include "clang/Edit/Commit.h" 22#include "clang/Edit/EditedSource.h" 23#include "clang/Edit/EditsReceiver.h" 24#include "clang/Edit/Rewriters.h" 25#include "clang/Frontend/CompilerInstance.h" 26#include "clang/Frontend/MultiplexConsumer.h" 27#include "clang/Lex/PPConditionalDirectiveRecord.h" 28#include "clang/Lex/Preprocessor.h" 29#include "clang/Rewrite/Core/Rewriter.h" 30#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h" 31#include "llvm/ADT/SmallString.h" 32#include "llvm/ADT/StringSet.h" 33#include "llvm/Support/Path.h" 34#include "llvm/Support/SourceMgr.h" 35#include "llvm/Support/YAMLParser.h" 36 37using namespace clang; 38using namespace arcmt; 39using namespace ento::objc_retain; 40 41namespace { 42 43class ObjCMigrateASTConsumer : public ASTConsumer { 44 enum CF_BRIDGING_KIND { 45 CF_BRIDGING_NONE, 46 CF_BRIDGING_ENABLE, 47 CF_BRIDGING_MAY_INCLUDE 48 }; 49 50 void migrateDecl(Decl *D); 51 void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D); 52 void migrateProtocolConformance(ASTContext &Ctx, 53 const ObjCImplementationDecl *ImpDecl); 54 void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl); 55 bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, 56 const TypedefDecl *TypedefDcl); 57 void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); 58 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, 59 ObjCMethodDecl *OM); 60 bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM); 61 void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM); 62 void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P); 63 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, 64 ObjCMethodDecl *OM, 65 ObjCInstanceTypeFamily OIT_Family = OIT_None); 66 67 void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl); 68 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 69 const FunctionDecl *FuncDecl, bool ResultAnnotated); 70 void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 71 const ObjCMethodDecl *MethodDecl, bool ResultAnnotated); 72 73 void AnnotateImplicitBridging(ASTContext &Ctx); 74 75 CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx, 76 const FunctionDecl *FuncDecl); 77 78 void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); 79 80 void migrateAddMethodAnnotation(ASTContext &Ctx, 81 const ObjCMethodDecl *MethodDecl); 82 83 void inferDesignatedInitializers(ASTContext &Ctx, 84 const ObjCImplementationDecl *ImplD); 85 86 bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc); 87 88public: 89 std::string MigrateDir; 90 unsigned ASTMigrateActions; 91 FileID FileId; 92 const TypedefDecl *NSIntegerTypedefed; 93 const TypedefDecl *NSUIntegerTypedefed; 94 std::unique_ptr<NSAPI> NSAPIObj; 95 std::unique_ptr<edit::EditedSource> Editor; 96 FileRemapper &Remapper; 97 FileManager &FileMgr; 98 const PPConditionalDirectiveRecord *PPRec; 99 Preprocessor &PP; 100 bool IsOutputFile; 101 bool FoundationIncluded; 102 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; 103 llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates; 104 llvm::StringSet<> WhiteListFilenames; 105 106 ObjCMigrateASTConsumer(StringRef migrateDir, 107 unsigned astMigrateActions, 108 FileRemapper &remapper, 109 FileManager &fileMgr, 110 const PPConditionalDirectiveRecord *PPRec, 111 Preprocessor &PP, 112 bool isOutputFile, 113 ArrayRef<std::string> WhiteList) 114 : MigrateDir(migrateDir), 115 ASTMigrateActions(astMigrateActions), 116 NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr), 117 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), 118 IsOutputFile(isOutputFile), 119 FoundationIncluded(false){ 120 121 // FIXME: StringSet should have insert(iter, iter) to use here. 122 for (const std::string &Val : WhiteList) 123 WhiteListFilenames.insert(Val); 124 } 125 126protected: 127 void Initialize(ASTContext &Context) override { 128 NSAPIObj.reset(new NSAPI(Context)); 129 Editor.reset(new edit::EditedSource(Context.getSourceManager(), 130 Context.getLangOpts(), 131 PPRec)); 132 } 133 134 bool HandleTopLevelDecl(DeclGroupRef DG) override { 135 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) 136 migrateDecl(*I); 137 return true; 138 } 139 void HandleInterestingDecl(DeclGroupRef DG) override { 140 // Ignore decls from the PCH. 141 } 142 void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { 143 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); 144 } 145 146 void HandleTranslationUnit(ASTContext &Ctx) override; 147 148 bool canModifyFile(StringRef Path) { 149 if (WhiteListFilenames.empty()) 150 return true; 151 return WhiteListFilenames.find(llvm::sys::path::filename(Path)) 152 != WhiteListFilenames.end(); 153 } 154 bool canModifyFile(const FileEntry *FE) { 155 if (!FE) 156 return false; 157 return canModifyFile(FE->getName()); 158 } 159 bool canModifyFile(FileID FID) { 160 if (FID.isInvalid()) 161 return false; 162 return canModifyFile(PP.getSourceManager().getFileEntryForID(FID)); 163 } 164 165 bool canModify(const Decl *D) { 166 if (!D) 167 return false; 168 if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D)) 169 return canModify(CatImpl->getCategoryDecl()); 170 if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D)) 171 return canModify(Impl->getClassInterface()); 172 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 173 return canModify(cast<Decl>(MD->getDeclContext())); 174 175 FileID FID = PP.getSourceManager().getFileID(D->getLocation()); 176 return canModifyFile(FID); 177 } 178}; 179 180} 181 182ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, 183 StringRef migrateDir, 184 unsigned migrateAction) 185 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), 186 ObjCMigAction(migrateAction), 187 CompInst(nullptr) { 188 if (MigrateDir.empty()) 189 MigrateDir = "."; // user current directory if none is given. 190} 191 192std::unique_ptr<ASTConsumer> 193ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 194 PPConditionalDirectiveRecord * 195 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager()); 196 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 197 std::vector<std::unique_ptr<ASTConsumer>> Consumers; 198 Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile)); 199 Consumers.push_back(llvm::make_unique<ObjCMigrateASTConsumer>( 200 MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec, 201 CompInst->getPreprocessor(), false, None)); 202 return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); 203} 204 205bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { 206 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), 207 /*ignoreIfFilesChanges=*/true); 208 CompInst = &CI; 209 CI.getDiagnostics().setIgnoreAllWarnings(true); 210 return true; 211} 212 213namespace { 214 // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp 215 bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 216 const Expr* Expr = FullExpr->IgnoreImpCasts(); 217 if (isa<ArraySubscriptExpr>(Expr) || 218 isa<CallExpr>(Expr) || 219 isa<DeclRefExpr>(Expr) || 220 isa<CXXNamedCastExpr>(Expr) || 221 isa<CXXConstructExpr>(Expr) || 222 isa<CXXThisExpr>(Expr) || 223 isa<CXXTypeidExpr>(Expr) || 224 isa<CXXUnresolvedConstructExpr>(Expr) || 225 isa<ObjCMessageExpr>(Expr) || 226 isa<ObjCPropertyRefExpr>(Expr) || 227 isa<ObjCProtocolExpr>(Expr) || 228 isa<MemberExpr>(Expr) || 229 isa<ObjCIvarRefExpr>(Expr) || 230 isa<ParenExpr>(FullExpr) || 231 isa<ParenListExpr>(Expr) || 232 isa<SizeOfPackExpr>(Expr)) 233 return false; 234 235 return true; 236 } 237 238 /// \brief - Rewrite message expression for Objective-C setter and getters into 239 /// property-dot syntax. 240 bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg, 241 Preprocessor &PP, 242 const NSAPI &NS, edit::Commit &commit, 243 const ParentMap *PMap) { 244 if (!Msg || Msg->isImplicit() || 245 (Msg->getReceiverKind() != ObjCMessageExpr::Instance && 246 Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance)) 247 return false; 248 if (const Expr *Receiver = Msg->getInstanceReceiver()) 249 if (Receiver->getType()->isObjCBuiltinType()) 250 return false; 251 252 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 253 if (!Method) 254 return false; 255 if (!Method->isPropertyAccessor()) 256 return false; 257 258 const ObjCPropertyDecl *Prop = Method->findPropertyDecl(); 259 if (!Prop) 260 return false; 261 262 SourceRange MsgRange = Msg->getSourceRange(); 263 bool ReceiverIsSuper = 264 (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance); 265 // for 'super' receiver is nullptr. 266 const Expr *receiver = Msg->getInstanceReceiver(); 267 bool NeedsParen = 268 ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver); 269 bool IsGetter = (Msg->getNumArgs() == 0); 270 if (IsGetter) { 271 // Find space location range between receiver expression and getter method. 272 SourceLocation BegLoc = 273 ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getLocEnd(); 274 BegLoc = PP.getLocForEndOfToken(BegLoc); 275 SourceLocation EndLoc = Msg->getSelectorLoc(0); 276 SourceRange SpaceRange(BegLoc, EndLoc); 277 std::string PropertyDotString; 278 // rewrite getter method expression into: receiver.property or 279 // (receiver).property 280 if (NeedsParen) { 281 commit.insertBefore(receiver->getLocStart(), "("); 282 PropertyDotString = ")."; 283 } 284 else 285 PropertyDotString = "."; 286 PropertyDotString += Prop->getName(); 287 commit.replace(SpaceRange, PropertyDotString); 288 289 // remove '[' ']' 290 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 291 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 292 } else { 293 if (NeedsParen) 294 commit.insertWrap("(", receiver->getSourceRange(), ")"); 295 std::string PropertyDotString = "."; 296 PropertyDotString += Prop->getName(); 297 PropertyDotString += " ="; 298 const Expr*const* Args = Msg->getArgs(); 299 const Expr *RHS = Args[0]; 300 if (!RHS) 301 return false; 302 SourceLocation BegLoc = 303 ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getLocEnd(); 304 BegLoc = PP.getLocForEndOfToken(BegLoc); 305 SourceLocation EndLoc = RHS->getLocStart(); 306 EndLoc = EndLoc.getLocWithOffset(-1); 307 const char *colon = PP.getSourceManager().getCharacterData(EndLoc); 308 // Add a space after '=' if there is no space between RHS and '=' 309 if (colon && colon[0] == ':') 310 PropertyDotString += " "; 311 SourceRange Range(BegLoc, EndLoc); 312 commit.replace(Range, PropertyDotString); 313 // remove '[' ']' 314 commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 315 commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 316 } 317 return true; 318 } 319 320 321class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 322 ObjCMigrateASTConsumer &Consumer; 323 ParentMap &PMap; 324 325public: 326 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap) 327 : Consumer(consumer), PMap(PMap) { } 328 329 bool shouldVisitTemplateInstantiations() const { return false; } 330 bool shouldWalkTypesOfTypeLocs() const { return false; } 331 332 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 333 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) { 334 edit::Commit commit(*Consumer.Editor); 335 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); 336 Consumer.Editor->commit(commit); 337 } 338 339 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) { 340 edit::Commit commit(*Consumer.Editor); 341 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); 342 Consumer.Editor->commit(commit); 343 } 344 345 if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) { 346 edit::Commit commit(*Consumer.Editor); 347 rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj, 348 commit, &PMap); 349 Consumer.Editor->commit(commit); 350 } 351 352 return true; 353 } 354 355 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { 356 // Do depth first; we want to rewrite the subexpressions first so that if 357 // we have to move expressions we will move them already rewritten. 358 for (Stmt::child_range range = E->children(); range; ++range) 359 if (!TraverseStmt(*range)) 360 return false; 361 362 return WalkUpFromObjCMessageExpr(E); 363 } 364}; 365 366class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 367 ObjCMigrateASTConsumer &Consumer; 368 std::unique_ptr<ParentMap> PMap; 369 370public: 371 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } 372 373 bool shouldVisitTemplateInstantiations() const { return false; } 374 bool shouldWalkTypesOfTypeLocs() const { return false; } 375 376 bool TraverseStmt(Stmt *S) { 377 PMap.reset(new ParentMap(S)); 378 ObjCMigrator(Consumer, *PMap).TraverseStmt(S); 379 return true; 380 } 381}; 382} 383 384void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { 385 if (!D) 386 return; 387 if (isa<ObjCMethodDecl>(D)) 388 return; // Wait for the ObjC container declaration. 389 390 BodyMigrator(*this).TraverseDecl(D); 391} 392 393static void append_attr(std::string &PropertyString, const char *attr, 394 bool &LParenAdded) { 395 if (!LParenAdded) { 396 PropertyString += "("; 397 LParenAdded = true; 398 } 399 else 400 PropertyString += ", "; 401 PropertyString += attr; 402} 403 404static 405void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString, 406 const std::string& TypeString, 407 const char *name) { 408 const char *argPtr = TypeString.c_str(); 409 int paren = 0; 410 while (*argPtr) { 411 switch (*argPtr) { 412 case '(': 413 PropertyString += *argPtr; 414 paren++; 415 break; 416 case ')': 417 PropertyString += *argPtr; 418 paren--; 419 break; 420 case '^': 421 case '*': 422 PropertyString += (*argPtr); 423 if (paren == 1) { 424 PropertyString += name; 425 name = ""; 426 } 427 break; 428 default: 429 PropertyString += *argPtr; 430 break; 431 } 432 argPtr++; 433 } 434} 435 436static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) { 437 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 438 bool RetainableObject = ArgType->isObjCRetainableType(); 439 if (RetainableObject && 440 (propertyLifetime == Qualifiers::OCL_Strong 441 || propertyLifetime == Qualifiers::OCL_None)) { 442 if (const ObjCObjectPointerType *ObjPtrTy = 443 ArgType->getAs<ObjCObjectPointerType>()) { 444 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 445 if (IDecl && 446 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 447 return "copy"; 448 else 449 return "strong"; 450 } 451 else if (ArgType->isBlockPointerType()) 452 return "copy"; 453 } else if (propertyLifetime == Qualifiers::OCL_Weak) 454 // TODO. More precise determination of 'weak' attribute requires 455 // looking into setter's implementation for backing weak ivar. 456 return "weak"; 457 else if (RetainableObject) 458 return ArgType->isBlockPointerType() ? "copy" : "strong"; 459 return nullptr; 460} 461 462static void rewriteToObjCProperty(const ObjCMethodDecl *Getter, 463 const ObjCMethodDecl *Setter, 464 const NSAPI &NS, edit::Commit &commit, 465 unsigned LengthOfPrefix, 466 bool Atomic, bool UseNsIosOnlyMacro, 467 bool AvailabilityArgsMatch) { 468 ASTContext &Context = NS.getASTContext(); 469 bool LParenAdded = false; 470 std::string PropertyString = "@property "; 471 if (UseNsIosOnlyMacro && Context.Idents.get("NS_NONATOMIC_IOSONLY").hasMacroDefinition()) { 472 PropertyString += "(NS_NONATOMIC_IOSONLY"; 473 LParenAdded = true; 474 } else if (!Atomic) { 475 PropertyString += "(nonatomic"; 476 LParenAdded = true; 477 } 478 479 std::string PropertyNameString = Getter->getNameAsString(); 480 StringRef PropertyName(PropertyNameString); 481 if (LengthOfPrefix > 0) { 482 if (!LParenAdded) { 483 PropertyString += "(getter="; 484 LParenAdded = true; 485 } 486 else 487 PropertyString += ", getter="; 488 PropertyString += PropertyNameString; 489 } 490 // Property with no setter may be suggested as a 'readonly' property. 491 if (!Setter) 492 append_attr(PropertyString, "readonly", LParenAdded); 493 494 495 // Short circuit 'delegate' properties that contain the name "delegate" or 496 // "dataSource", or have exact name "target" to have 'assign' attribute. 497 if (PropertyName.equals("target") || 498 (PropertyName.find("delegate") != StringRef::npos) || 499 (PropertyName.find("dataSource") != StringRef::npos)) { 500 QualType QT = Getter->getReturnType(); 501 if (!QT->isRealType()) 502 append_attr(PropertyString, "assign", LParenAdded); 503 } else if (!Setter) { 504 QualType ResType = Context.getCanonicalType(Getter->getReturnType()); 505 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType)) 506 append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 507 } else { 508 const ParmVarDecl *argDecl = *Setter->param_begin(); 509 QualType ArgType = Context.getCanonicalType(argDecl->getType()); 510 if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType)) 511 append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 512 } 513 if (LParenAdded) 514 PropertyString += ')'; 515 QualType RT = Getter->getReturnType(); 516 if (!isa<TypedefType>(RT)) { 517 // strip off any ARC lifetime qualifier. 518 QualType CanResultTy = Context.getCanonicalType(RT); 519 if (CanResultTy.getQualifiers().hasObjCLifetime()) { 520 Qualifiers Qs = CanResultTy.getQualifiers(); 521 Qs.removeObjCLifetime(); 522 RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 523 } 524 } 525 PropertyString += " "; 526 PrintingPolicy SubPolicy(Context.getPrintingPolicy()); 527 SubPolicy.SuppressStrongLifetime = true; 528 SubPolicy.SuppressLifetimeQualifiers = true; 529 std::string TypeString = RT.getAsString(SubPolicy); 530 if (LengthOfPrefix > 0) { 531 // property name must strip off "is" and lower case the first character 532 // after that; e.g. isContinuous will become continuous. 533 StringRef PropertyNameStringRef(PropertyNameString); 534 PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix); 535 PropertyNameString = PropertyNameStringRef; 536 bool NoLowering = (isUppercase(PropertyNameString[0]) && 537 PropertyNameString.size() > 1 && 538 isUppercase(PropertyNameString[1])); 539 if (!NoLowering) 540 PropertyNameString[0] = toLowercase(PropertyNameString[0]); 541 } 542 if (RT->isBlockPointerType() || RT->isFunctionPointerType()) 543 MigrateBlockOrFunctionPointerTypeVariable(PropertyString, 544 TypeString, 545 PropertyNameString.c_str()); 546 else { 547 char LastChar = TypeString[TypeString.size()-1]; 548 PropertyString += TypeString; 549 if (LastChar != '*') 550 PropertyString += ' '; 551 PropertyString += PropertyNameString; 552 } 553 SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc(); 554 Selector GetterSelector = Getter->getSelector(); 555 556 SourceLocation EndGetterSelectorLoc = 557 StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size()); 558 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 559 EndGetterSelectorLoc), 560 PropertyString); 561 if (Setter && AvailabilityArgsMatch) { 562 SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 563 // Get location past ';' 564 EndLoc = EndLoc.getLocWithOffset(1); 565 SourceLocation BeginOfSetterDclLoc = Setter->getLocStart(); 566 // FIXME. This assumes that setter decl; is immediately preceded by eoln. 567 // It is trying to remove the setter method decl. line entirely. 568 BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1); 569 commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc)); 570 } 571} 572 573static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) { 574 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) { 575 StringRef Name = CatDecl->getName(); 576 return Name.endswith("Deprecated"); 577 } 578 return false; 579} 580 581void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx, 582 ObjCContainerDecl *D) { 583 if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D)) 584 return; 585 586 for (auto *Method : D->methods()) { 587 if (Method->isDeprecated()) 588 continue; 589 bool PropertyInferred = migrateProperty(Ctx, D, Method); 590 // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to 591 // the getter method as it ends up on the property itself which we don't want 592 // to do unless -objcmt-returns-innerpointer-property option is on. 593 if (!PropertyInferred || 594 (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 595 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 596 migrateNsReturnsInnerPointer(Ctx, Method); 597 } 598 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 599 return; 600 601 for (auto *Prop : D->properties()) { 602 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 603 !Prop->isDeprecated()) 604 migratePropertyNsReturnsInnerPointer(Ctx, Prop); 605 } 606} 607 608static bool 609ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 610 const ObjCImplementationDecl *ImpDecl, 611 const ObjCInterfaceDecl *IDecl, 612 ObjCProtocolDecl *Protocol) { 613 // In auto-synthesis, protocol properties are not synthesized. So, 614 // a conforming protocol must have its required properties declared 615 // in class interface. 616 bool HasAtleastOneRequiredProperty = false; 617 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 618 for (const auto *Property : PDecl->properties()) { 619 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 620 continue; 621 HasAtleastOneRequiredProperty = true; 622 DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName()); 623 if (R.size() == 0) { 624 // Relax the rule and look into class's implementation for a synthesize 625 // or dynamic declaration. Class is implementing a property coming from 626 // another protocol. This still makes the target protocol as conforming. 627 if (!ImpDecl->FindPropertyImplDecl( 628 Property->getDeclName().getAsIdentifierInfo())) 629 return false; 630 } 631 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { 632 if ((ClassProperty->getPropertyAttributes() 633 != Property->getPropertyAttributes()) || 634 !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 635 return false; 636 } 637 else 638 return false; 639 } 640 641 // At this point, all required properties in this protocol conform to those 642 // declared in the class. 643 // Check that class implements the required methods of the protocol too. 644 bool HasAtleastOneRequiredMethod = false; 645 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 646 if (PDecl->meth_begin() == PDecl->meth_end()) 647 return HasAtleastOneRequiredProperty; 648 for (const auto *MD : PDecl->methods()) { 649 if (MD->isImplicit()) 650 continue; 651 if (MD->getImplementationControl() == ObjCMethodDecl::Optional) 652 continue; 653 DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName()); 654 if (R.size() == 0) 655 return false; 656 bool match = false; 657 HasAtleastOneRequiredMethod = true; 658 for (unsigned I = 0, N = R.size(); I != N; ++I) 659 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) 660 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 661 match = true; 662 break; 663 } 664 if (!match) 665 return false; 666 } 667 } 668 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) 669 return true; 670 return false; 671} 672 673static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 674 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 675 const NSAPI &NS, edit::Commit &commit) { 676 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 677 std::string ClassString; 678 SourceLocation EndLoc = 679 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 680 681 if (Protocols.empty()) { 682 ClassString = '<'; 683 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 684 ClassString += ConformingProtocols[i]->getNameAsString(); 685 if (i != (e-1)) 686 ClassString += ", "; 687 } 688 ClassString += "> "; 689 } 690 else { 691 ClassString = ", "; 692 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 693 ClassString += ConformingProtocols[i]->getNameAsString(); 694 if (i != (e-1)) 695 ClassString += ", "; 696 } 697 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 698 EndLoc = *PL; 699 } 700 701 commit.insertAfterToken(EndLoc, ClassString); 702 return true; 703} 704 705static StringRef GetUnsignedName(StringRef NSIntegerName) { 706 StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName) 707 .Case("int8_t", "uint8_t") 708 .Case("int16_t", "uint16_t") 709 .Case("int32_t", "uint32_t") 710 .Case("NSInteger", "NSUInteger") 711 .Case("int64_t", "uint64_t") 712 .Default(NSIntegerName); 713 return UnsignedName; 714} 715 716static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 717 const TypedefDecl *TypedefDcl, 718 const NSAPI &NS, edit::Commit &commit, 719 StringRef NSIntegerName, 720 bool NSOptions) { 721 std::string ClassString; 722 if (NSOptions) { 723 ClassString = "typedef NS_OPTIONS("; 724 ClassString += GetUnsignedName(NSIntegerName); 725 } 726 else { 727 ClassString = "typedef NS_ENUM("; 728 ClassString += NSIntegerName; 729 } 730 ClassString += ", "; 731 732 ClassString += TypedefDcl->getIdentifier()->getName(); 733 ClassString += ')'; 734 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 735 commit.replace(R, ClassString); 736 SourceLocation EndOfEnumDclLoc = EnumDcl->getLocEnd(); 737 EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc, 738 NS.getASTContext(), /*IsDecl*/true); 739 if (!EndOfEnumDclLoc.isInvalid()) { 740 SourceRange EnumDclRange(EnumDcl->getLocStart(), EndOfEnumDclLoc); 741 commit.insertFromRange(TypedefDcl->getLocStart(), EnumDclRange); 742 } 743 else 744 return false; 745 746 SourceLocation EndTypedefDclLoc = TypedefDcl->getLocEnd(); 747 EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc, 748 NS.getASTContext(), /*IsDecl*/true); 749 if (!EndTypedefDclLoc.isInvalid()) { 750 SourceRange TDRange(TypedefDcl->getLocStart(), EndTypedefDclLoc); 751 commit.remove(TDRange); 752 } 753 else 754 return false; 755 756 EndOfEnumDclLoc = trans::findLocationAfterSemi(EnumDcl->getLocEnd(), NS.getASTContext(), 757 /*IsDecl*/true); 758 if (!EndOfEnumDclLoc.isInvalid()) { 759 SourceLocation BeginOfEnumDclLoc = EnumDcl->getLocStart(); 760 // FIXME. This assumes that enum decl; is immediately preceded by eoln. 761 // It is trying to remove the enum decl. lines entirely. 762 BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1); 763 commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc)); 764 return true; 765 } 766 return false; 767} 768 769static void rewriteToNSMacroDecl(ASTContext &Ctx, 770 const EnumDecl *EnumDcl, 771 const TypedefDecl *TypedefDcl, 772 const NSAPI &NS, edit::Commit &commit, 773 bool IsNSIntegerType) { 774 QualType DesignatedEnumType = EnumDcl->getIntegerType(); 775 assert(!DesignatedEnumType.isNull() 776 && "rewriteToNSMacroDecl - underlying enum type is null"); 777 778 PrintingPolicy Policy(Ctx.getPrintingPolicy()); 779 std::string TypeString = DesignatedEnumType.getAsString(Policy); 780 std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS("; 781 ClassString += TypeString; 782 ClassString += ", "; 783 784 ClassString += TypedefDcl->getIdentifier()->getName(); 785 ClassString += ')'; 786 SourceLocation EndLoc; 787 if (EnumDcl->getIntegerTypeSourceInfo()) { 788 TypeSourceInfo *TSourceInfo = EnumDcl->getIntegerTypeSourceInfo(); 789 TypeLoc TLoc = TSourceInfo->getTypeLoc(); 790 EndLoc = TLoc.getLocEnd(); 791 const char *lbrace = Ctx.getSourceManager().getCharacterData(EndLoc); 792 unsigned count = 0; 793 if (lbrace) 794 while (lbrace[count] != '{') 795 ++count; 796 if (count > 0) 797 EndLoc = EndLoc.getLocWithOffset(count-1); 798 } 799 else 800 EndLoc = EnumDcl->getLocStart(); 801 SourceRange R(EnumDcl->getLocStart(), EndLoc); 802 commit.replace(R, ClassString); 803 // This is to remove spaces between '}' and typedef name. 804 SourceLocation StartTypedefLoc = EnumDcl->getLocEnd(); 805 StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1); 806 SourceLocation EndTypedefLoc = TypedefDcl->getLocEnd(); 807 808 commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc)); 809} 810 811static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx, 812 const EnumDecl *EnumDcl) { 813 bool PowerOfTwo = true; 814 bool AllHexdecimalEnumerator = true; 815 uint64_t MaxPowerOfTwoVal = 0; 816 for (auto Enumerator : EnumDcl->enumerators()) { 817 const Expr *InitExpr = Enumerator->getInitExpr(); 818 if (!InitExpr) { 819 PowerOfTwo = false; 820 AllHexdecimalEnumerator = false; 821 continue; 822 } 823 InitExpr = InitExpr->IgnoreParenCasts(); 824 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 825 if (BO->isShiftOp() || BO->isBitwiseOp()) 826 return true; 827 828 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 829 if (PowerOfTwo && EnumVal) { 830 if (!llvm::isPowerOf2_64(EnumVal)) 831 PowerOfTwo = false; 832 else if (EnumVal > MaxPowerOfTwoVal) 833 MaxPowerOfTwoVal = EnumVal; 834 } 835 if (AllHexdecimalEnumerator && EnumVal) { 836 bool FoundHexdecimalEnumerator = false; 837 SourceLocation EndLoc = Enumerator->getLocEnd(); 838 Token Tok; 839 if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true)) 840 if (Tok.isLiteral() && Tok.getLength() > 2) { 841 if (const char *StringLit = Tok.getLiteralData()) 842 FoundHexdecimalEnumerator = 843 (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x')); 844 } 845 if (!FoundHexdecimalEnumerator) 846 AllHexdecimalEnumerator = false; 847 } 848 } 849 return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2)); 850} 851 852void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 853 const ObjCImplementationDecl *ImpDecl) { 854 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 855 if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated()) 856 return; 857 // Find all implicit conforming protocols for this class 858 // and make them explicit. 859 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 860 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 861 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 862 863 for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls) 864 if (!ExplicitProtocols.count(ProtDecl)) 865 PotentialImplicitProtocols.push_back(ProtDecl); 866 867 if (PotentialImplicitProtocols.empty()) 868 return; 869 870 // go through list of non-optional methods and properties in each protocol 871 // in the PotentialImplicitProtocols list. If class implements every one of the 872 // methods and properties, then this class conforms to this protocol. 873 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 874 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 875 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 876 PotentialImplicitProtocols[i])) 877 ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 878 879 if (ConformingProtocols.empty()) 880 return; 881 882 // Further reduce number of conforming protocols. If protocol P1 is in the list 883 // protocol P2 (P2<P1>), No need to include P1. 884 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 885 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 886 bool DropIt = false; 887 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 888 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 889 ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 890 if (PDecl == TargetPDecl) 891 continue; 892 if (PDecl->lookupProtocolNamed( 893 TargetPDecl->getDeclName().getAsIdentifierInfo())) { 894 DropIt = true; 895 break; 896 } 897 } 898 if (!DropIt) 899 MinimalConformingProtocols.push_back(TargetPDecl); 900 } 901 if (MinimalConformingProtocols.empty()) 902 return; 903 edit::Commit commit(*Editor); 904 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 905 *NSAPIObj, commit); 906 Editor->commit(commit); 907} 908 909void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed( 910 const TypedefDecl *TypedefDcl) { 911 912 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 913 if (NSAPIObj->isObjCNSIntegerType(qt)) 914 NSIntegerTypedefed = TypedefDcl; 915 else if (NSAPIObj->isObjCNSUIntegerType(qt)) 916 NSUIntegerTypedefed = TypedefDcl; 917} 918 919bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 920 const EnumDecl *EnumDcl, 921 const TypedefDecl *TypedefDcl) { 922 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 923 EnumDcl->isDeprecated()) 924 return false; 925 if (!TypedefDcl) { 926 if (NSIntegerTypedefed) { 927 TypedefDcl = NSIntegerTypedefed; 928 NSIntegerTypedefed = nullptr; 929 } 930 else if (NSUIntegerTypedefed) { 931 TypedefDcl = NSUIntegerTypedefed; 932 NSUIntegerTypedefed = nullptr; 933 } 934 else 935 return false; 936 FileID FileIdOfTypedefDcl = 937 PP.getSourceManager().getFileID(TypedefDcl->getLocation()); 938 FileID FileIdOfEnumDcl = 939 PP.getSourceManager().getFileID(EnumDcl->getLocation()); 940 if (FileIdOfTypedefDcl != FileIdOfEnumDcl) 941 return false; 942 } 943 if (TypedefDcl->isDeprecated()) 944 return false; 945 946 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 947 StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt); 948 949 if (NSIntegerName.empty()) { 950 // Also check for typedef enum {...} TD; 951 if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 952 if (EnumTy->getDecl() == EnumDcl) { 953 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 954 if (!InsertFoundation(Ctx, TypedefDcl->getLocStart())) 955 return false; 956 edit::Commit commit(*Editor); 957 rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 958 Editor->commit(commit); 959 return true; 960 } 961 } 962 return false; 963 } 964 965 // We may still use NS_OPTIONS based on what we find in the enumertor list. 966 bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 967 if (!InsertFoundation(Ctx, TypedefDcl->getLocStart())) 968 return false; 969 edit::Commit commit(*Editor); 970 bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, 971 commit, NSIntegerName, NSOptions); 972 Editor->commit(commit); 973 return Res; 974} 975 976static void ReplaceWithInstancetype(ASTContext &Ctx, 977 const ObjCMigrateASTConsumer &ASTC, 978 ObjCMethodDecl *OM) { 979 if (OM->getReturnType() == Ctx.getObjCInstanceType()) 980 return; // already has instancetype. 981 982 SourceRange R; 983 std::string ClassString; 984 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 985 TypeLoc TL = TSInfo->getTypeLoc(); 986 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 987 ClassString = "instancetype"; 988 } 989 else { 990 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 991 ClassString = OM->isInstanceMethod() ? '-' : '+'; 992 ClassString += " (instancetype)"; 993 } 994 edit::Commit commit(*ASTC.Editor); 995 commit.replace(R, ClassString); 996 ASTC.Editor->commit(commit); 997} 998 999static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC, 1000 ObjCMethodDecl *OM) { 1001 ObjCInterfaceDecl *IDecl = OM->getClassInterface(); 1002 SourceRange R; 1003 std::string ClassString; 1004 if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 1005 TypeLoc TL = TSInfo->getTypeLoc(); 1006 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); { 1007 ClassString = IDecl->getName(); 1008 ClassString += "*"; 1009 } 1010 } 1011 else { 1012 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 1013 ClassString = "+ ("; 1014 ClassString += IDecl->getName(); ClassString += "*)"; 1015 } 1016 edit::Commit commit(*ASTC.Editor); 1017 commit.replace(R, ClassString); 1018 ASTC.Editor->commit(commit); 1019} 1020 1021void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 1022 ObjCContainerDecl *CDecl, 1023 ObjCMethodDecl *OM) { 1024 ObjCInstanceTypeFamily OIT_Family = 1025 Selector::getInstTypeMethodFamily(OM->getSelector()); 1026 1027 std::string ClassName; 1028 switch (OIT_Family) { 1029 case OIT_None: 1030 migrateFactoryMethod(Ctx, CDecl, OM); 1031 return; 1032 case OIT_Array: 1033 ClassName = "NSArray"; 1034 break; 1035 case OIT_Dictionary: 1036 ClassName = "NSDictionary"; 1037 break; 1038 case OIT_Singleton: 1039 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 1040 return; 1041 case OIT_Init: 1042 if (OM->getReturnType()->isObjCIdType()) 1043 ReplaceWithInstancetype(Ctx, *this, OM); 1044 return; 1045 case OIT_ReturnsSelf: 1046 migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf); 1047 return; 1048 } 1049 if (!OM->getReturnType()->isObjCIdType()) 1050 return; 1051 1052 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 1053 if (!IDecl) { 1054 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 1055 IDecl = CatDecl->getClassInterface(); 1056 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 1057 IDecl = ImpDecl->getClassInterface(); 1058 } 1059 if (!IDecl || 1060 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 1061 migrateFactoryMethod(Ctx, CDecl, OM); 1062 return; 1063 } 1064 ReplaceWithInstancetype(Ctx, *this, OM); 1065} 1066 1067static bool TypeIsInnerPointer(QualType T) { 1068 if (!T->isAnyPointerType()) 1069 return false; 1070 if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() || 1071 T->isBlockPointerType() || T->isFunctionPointerType() || 1072 ento::coreFoundation::isCFObjectRef(T)) 1073 return false; 1074 // Also, typedef-of-pointer-to-incomplete-struct is something that we assume 1075 // is not an innter pointer type. 1076 QualType OrigT = T; 1077 while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) 1078 T = TD->getDecl()->getUnderlyingType(); 1079 if (OrigT == T || !T->isPointerType()) 1080 return true; 1081 const PointerType* PT = T->getAs<PointerType>(); 1082 QualType UPointeeT = PT->getPointeeType().getUnqualifiedType(); 1083 if (UPointeeT->isRecordType()) { 1084 const RecordType *RecordTy = UPointeeT->getAs<RecordType>(); 1085 if (!RecordTy->getDecl()->isCompleteDefinition()) 1086 return false; 1087 } 1088 return true; 1089} 1090 1091/// \brief Check whether the two versions match. 1092static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) { 1093 return (X == Y); 1094} 1095 1096/// AvailabilityAttrsMatch - This routine checks that if comparing two 1097/// availability attributes, all their components match. It returns 1098/// true, if not dealing with availability or when all components of 1099/// availability attributes match. This routine is only called when 1100/// the attributes are of the same kind. 1101static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) { 1102 const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1); 1103 if (!AA1) 1104 return true; 1105 const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2); 1106 1107 VersionTuple Introduced1 = AA1->getIntroduced(); 1108 VersionTuple Deprecated1 = AA1->getDeprecated(); 1109 VersionTuple Obsoleted1 = AA1->getObsoleted(); 1110 bool IsUnavailable1 = AA1->getUnavailable(); 1111 VersionTuple Introduced2 = AA2->getIntroduced(); 1112 VersionTuple Deprecated2 = AA2->getDeprecated(); 1113 VersionTuple Obsoleted2 = AA2->getObsoleted(); 1114 bool IsUnavailable2 = AA2->getUnavailable(); 1115 return (versionsMatch(Introduced1, Introduced2) && 1116 versionsMatch(Deprecated1, Deprecated2) && 1117 versionsMatch(Obsoleted1, Obsoleted2) && 1118 IsUnavailable1 == IsUnavailable2); 1119 1120} 1121 1122static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2, 1123 bool &AvailabilityArgsMatch) { 1124 // This list is very small, so this need not be optimized. 1125 for (unsigned i = 0, e = Attrs1.size(); i != e; i++) { 1126 bool match = false; 1127 for (unsigned j = 0, f = Attrs2.size(); j != f; j++) { 1128 // Matching attribute kind only. Except for Availabilty attributes, 1129 // we are not getting into details of the attributes. For all practical purposes 1130 // this is sufficient. 1131 if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) { 1132 if (AvailabilityArgsMatch) 1133 AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]); 1134 match = true; 1135 break; 1136 } 1137 } 1138 if (!match) 1139 return false; 1140 } 1141 return true; 1142} 1143 1144/// AttributesMatch - This routine checks list of attributes for two 1145/// decls. It returns false, if there is a mismatch in kind of 1146/// attributes seen in the decls. It returns true if the two decls 1147/// have list of same kind of attributes. Furthermore, when there 1148/// are availability attributes in the two decls, it sets the 1149/// AvailabilityArgsMatch to false if availability attributes have 1150/// different versions, etc. 1151static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2, 1152 bool &AvailabilityArgsMatch) { 1153 if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) { 1154 AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs()); 1155 return true; 1156 } 1157 AvailabilityArgsMatch = true; 1158 const AttrVec &Attrs1 = Decl1->getAttrs(); 1159 const AttrVec &Attrs2 = Decl2->getAttrs(); 1160 bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch); 1161 if (match && (Attrs2.size() > Attrs1.size())) 1162 return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch); 1163 return match; 1164} 1165 1166static bool IsValidIdentifier(ASTContext &Ctx, 1167 const char *Name) { 1168 if (!isIdentifierHead(Name[0])) 1169 return false; 1170 std::string NameString = Name; 1171 NameString[0] = toLowercase(NameString[0]); 1172 IdentifierInfo *II = &Ctx.Idents.get(NameString); 1173 return II->getTokenID() == tok::identifier; 1174} 1175 1176bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx, 1177 ObjCContainerDecl *D, 1178 ObjCMethodDecl *Method) { 1179 if (Method->isPropertyAccessor() || !Method->isInstanceMethod() || 1180 Method->param_size() != 0) 1181 return false; 1182 // Is this method candidate to be a getter? 1183 QualType GRT = Method->getReturnType(); 1184 if (GRT->isVoidType()) 1185 return false; 1186 1187 Selector GetterSelector = Method->getSelector(); 1188 ObjCInstanceTypeFamily OIT_Family = 1189 Selector::getInstTypeMethodFamily(GetterSelector); 1190 1191 if (OIT_Family != OIT_None) 1192 return false; 1193 1194 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 1195 Selector SetterSelector = 1196 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 1197 PP.getSelectorTable(), 1198 getterName); 1199 ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector); 1200 unsigned LengthOfPrefix = 0; 1201 if (!SetterMethod) { 1202 // try a different naming convention for getter: isXxxxx 1203 StringRef getterNameString = getterName->getName(); 1204 bool IsPrefix = getterNameString.startswith("is"); 1205 // Note that we don't want to change an isXXX method of retainable object 1206 // type to property (readonly or otherwise). 1207 if (IsPrefix && GRT->isObjCRetainableType()) 1208 return false; 1209 if (IsPrefix || getterNameString.startswith("get")) { 1210 LengthOfPrefix = (IsPrefix ? 2 : 3); 1211 const char *CGetterName = getterNameString.data() + LengthOfPrefix; 1212 // Make sure that first character after "is" or "get" prefix can 1213 // start an identifier. 1214 if (!IsValidIdentifier(Ctx, CGetterName)) 1215 return false; 1216 if (CGetterName[0] && isUppercase(CGetterName[0])) { 1217 getterName = &Ctx.Idents.get(CGetterName); 1218 SetterSelector = 1219 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 1220 PP.getSelectorTable(), 1221 getterName); 1222 SetterMethod = D->getInstanceMethod(SetterSelector); 1223 } 1224 } 1225 } 1226 1227 if (SetterMethod) { 1228 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0) 1229 return false; 1230 bool AvailabilityArgsMatch; 1231 if (SetterMethod->isDeprecated() || 1232 !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch)) 1233 return false; 1234 1235 // Is this a valid setter, matching the target getter? 1236 QualType SRT = SetterMethod->getReturnType(); 1237 if (!SRT->isVoidType()) 1238 return false; 1239 const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 1240 QualType ArgType = argDecl->getType(); 1241 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT)) 1242 return false; 1243 edit::Commit commit(*Editor); 1244 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit, 1245 LengthOfPrefix, 1246 (ASTMigrateActions & 1247 FrontendOptions::ObjCMT_AtomicProperty) != 0, 1248 (ASTMigrateActions & 1249 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 1250 AvailabilityArgsMatch); 1251 Editor->commit(commit); 1252 return true; 1253 } 1254 else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) { 1255 // Try a non-void method with no argument (and no setter or property of same name 1256 // as a 'readonly' property. 1257 edit::Commit commit(*Editor); 1258 rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit, 1259 LengthOfPrefix, 1260 (ASTMigrateActions & 1261 FrontendOptions::ObjCMT_AtomicProperty) != 0, 1262 (ASTMigrateActions & 1263 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 1264 /*AvailabilityArgsMatch*/false); 1265 Editor->commit(commit); 1266 return true; 1267 } 1268 return false; 1269} 1270 1271void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx, 1272 ObjCMethodDecl *OM) { 1273 if (OM->isImplicit() || 1274 !OM->isInstanceMethod() || 1275 OM->hasAttr<ObjCReturnsInnerPointerAttr>()) 1276 return; 1277 1278 QualType RT = OM->getReturnType(); 1279 if (!TypeIsInnerPointer(RT) || 1280 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 1281 return; 1282 1283 edit::Commit commit(*Editor); 1284 commit.insertBefore(OM->getLocEnd(), " NS_RETURNS_INNER_POINTER"); 1285 Editor->commit(commit); 1286} 1287 1288void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, 1289 ObjCPropertyDecl *P) { 1290 QualType T = P->getType(); 1291 1292 if (!TypeIsInnerPointer(T) || 1293 !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 1294 return; 1295 edit::Commit commit(*Editor); 1296 commit.insertBefore(P->getLocEnd(), " NS_RETURNS_INNER_POINTER "); 1297 Editor->commit(commit); 1298} 1299 1300void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx, 1301 ObjCContainerDecl *CDecl) { 1302 if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl)) 1303 return; 1304 1305 // migrate methods which can have instancetype as their result type. 1306 for (auto *Method : CDecl->methods()) { 1307 if (Method->isDeprecated()) 1308 continue; 1309 migrateMethodInstanceType(Ctx, CDecl, Method); 1310 } 1311} 1312 1313void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 1314 ObjCContainerDecl *CDecl, 1315 ObjCMethodDecl *OM, 1316 ObjCInstanceTypeFamily OIT_Family) { 1317 if (OM->isInstanceMethod() || 1318 OM->getReturnType() == Ctx.getObjCInstanceType() || 1319 !OM->getReturnType()->isObjCIdType()) 1320 return; 1321 1322 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 1323 // NSYYYNamE with matching names be at least 3 characters long. 1324 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 1325 if (!IDecl) { 1326 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 1327 IDecl = CatDecl->getClassInterface(); 1328 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 1329 IDecl = ImpDecl->getClassInterface(); 1330 } 1331 if (!IDecl) 1332 return; 1333 1334 std::string StringClassName = IDecl->getName(); 1335 StringRef LoweredClassName(StringClassName); 1336 std::string StringLoweredClassName = LoweredClassName.lower(); 1337 LoweredClassName = StringLoweredClassName; 1338 1339 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); 1340 // Handle method with no name at its first selector slot; e.g. + (id):(int)x. 1341 if (!MethodIdName) 1342 return; 1343 1344 std::string MethodName = MethodIdName->getName(); 1345 if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) { 1346 StringRef STRefMethodName(MethodName); 1347 size_t len = 0; 1348 if (STRefMethodName.startswith("standard")) 1349 len = strlen("standard"); 1350 else if (STRefMethodName.startswith("shared")) 1351 len = strlen("shared"); 1352 else if (STRefMethodName.startswith("default")) 1353 len = strlen("default"); 1354 else 1355 return; 1356 MethodName = STRefMethodName.substr(len); 1357 } 1358 std::string MethodNameSubStr = MethodName.substr(0, 3); 1359 StringRef MethodNamePrefix(MethodNameSubStr); 1360 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 1361 MethodNamePrefix = StringLoweredMethodNamePrefix; 1362 size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 1363 if (Ix == StringRef::npos) 1364 return; 1365 std::string ClassNamePostfix = LoweredClassName.substr(Ix); 1366 StringRef LoweredMethodName(MethodName); 1367 std::string StringLoweredMethodName = LoweredMethodName.lower(); 1368 LoweredMethodName = StringLoweredMethodName; 1369 if (!LoweredMethodName.startswith(ClassNamePostfix)) 1370 return; 1371 if (OIT_Family == OIT_ReturnsSelf) 1372 ReplaceWithClasstype(*this, OM); 1373 else 1374 ReplaceWithInstancetype(Ctx, *this, OM); 1375} 1376 1377static bool IsVoidStarType(QualType Ty) { 1378 if (!Ty->isPointerType()) 1379 return false; 1380 1381 while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr())) 1382 Ty = TD->getDecl()->getUnderlyingType(); 1383 1384 // Is the type void*? 1385 const PointerType* PT = Ty->getAs<PointerType>(); 1386 if (PT->getPointeeType().getUnqualifiedType()->isVoidType()) 1387 return true; 1388 return IsVoidStarType(PT->getPointeeType()); 1389} 1390 1391/// AuditedType - This routine audits the type AT and returns false if it is one of known 1392/// CF object types or of the "void *" variety. It returns true if we don't care about the type 1393/// such as a non-pointer or pointers which have no ownership issues (such as "int *"). 1394static bool AuditedType (QualType AT) { 1395 if (!AT->isAnyPointerType() && !AT->isBlockPointerType()) 1396 return true; 1397 // FIXME. There isn't much we can say about CF pointer type; or is there? 1398 if (ento::coreFoundation::isCFObjectRef(AT) || 1399 IsVoidStarType(AT) || 1400 // If an ObjC object is type, assuming that it is not a CF function and 1401 // that it is an un-audited function. 1402 AT->isObjCObjectPointerType() || AT->isObjCBuiltinType()) 1403 return false; 1404 // All other pointers are assumed audited as harmless. 1405 return true; 1406} 1407 1408void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { 1409 if (CFFunctionIBCandidates.empty()) 1410 return; 1411 if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) { 1412 CFFunctionIBCandidates.clear(); 1413 FileId = FileID(); 1414 return; 1415 } 1416 // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED 1417 const Decl *FirstFD = CFFunctionIBCandidates[0]; 1418 const Decl *LastFD = 1419 CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; 1420 const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; 1421 edit::Commit commit(*Editor); 1422 commit.insertBefore(FirstFD->getLocStart(), PragmaString); 1423 PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n"; 1424 SourceLocation EndLoc = LastFD->getLocEnd(); 1425 // get location just past end of function location. 1426 EndLoc = PP.getLocForEndOfToken(EndLoc); 1427 if (isa<FunctionDecl>(LastFD)) { 1428 // For Methods, EndLoc points to the ending semcolon. So, 1429 // not of these extra work is needed. 1430 Token Tok; 1431 // get locaiton of token that comes after end of function. 1432 bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); 1433 if (!Failed) 1434 EndLoc = Tok.getLocation(); 1435 } 1436 commit.insertAfterToken(EndLoc, PragmaString); 1437 Editor->commit(commit); 1438 FileId = FileID(); 1439 CFFunctionIBCandidates.clear(); 1440} 1441 1442void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) { 1443 if (Decl->isDeprecated()) 1444 return; 1445 1446 if (Decl->hasAttr<CFAuditedTransferAttr>()) { 1447 assert(CFFunctionIBCandidates.empty() && 1448 "Cannot have audited functions/methods inside user " 1449 "provided CF_IMPLICIT_BRIDGING_ENABLE"); 1450 return; 1451 } 1452 1453 // Finction must be annotated first. 1454 if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) { 1455 CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl); 1456 if (AuditKind == CF_BRIDGING_ENABLE) { 1457 CFFunctionIBCandidates.push_back(Decl); 1458 if (FileId.isInvalid()) 1459 FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 1460 } 1461 else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) { 1462 if (!CFFunctionIBCandidates.empty()) { 1463 CFFunctionIBCandidates.push_back(Decl); 1464 if (FileId.isInvalid()) 1465 FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 1466 } 1467 } 1468 else 1469 AnnotateImplicitBridging(Ctx); 1470 } 1471 else { 1472 migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl)); 1473 AnnotateImplicitBridging(Ctx); 1474 } 1475} 1476 1477void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 1478 const CallEffects &CE, 1479 const FunctionDecl *FuncDecl, 1480 bool ResultAnnotated) { 1481 // Annotate function. 1482 if (!ResultAnnotated) { 1483 RetEffect Ret = CE.getReturnValue(); 1484 const char *AnnotationString = nullptr; 1485 if (Ret.getObjKind() == RetEffect::CF) { 1486 if (Ret.isOwned() && 1487 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 1488 AnnotationString = " CF_RETURNS_RETAINED"; 1489 else if (Ret.notOwned() && 1490 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 1491 AnnotationString = " CF_RETURNS_NOT_RETAINED"; 1492 } 1493 else if (Ret.getObjKind() == RetEffect::ObjC) { 1494 if (Ret.isOwned() && 1495 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 1496 AnnotationString = " NS_RETURNS_RETAINED"; 1497 } 1498 1499 if (AnnotationString) { 1500 edit::Commit commit(*Editor); 1501 commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString); 1502 Editor->commit(commit); 1503 } 1504 } 1505 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1506 unsigned i = 0; 1507 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 1508 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 1509 const ParmVarDecl *pd = *pi; 1510 ArgEffect AE = AEArgs[i]; 1511 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() && 1512 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 1513 edit::Commit commit(*Editor); 1514 commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 1515 Editor->commit(commit); 1516 } 1517 else if (AE == DecRefMsg && !pd->hasAttr<NSConsumedAttr>() && 1518 Ctx.Idents.get("NS_CONSUMED").hasMacroDefinition()) { 1519 edit::Commit commit(*Editor); 1520 commit.insertBefore(pd->getLocation(), "NS_CONSUMED "); 1521 Editor->commit(commit); 1522 } 1523 } 1524} 1525 1526 1527ObjCMigrateASTConsumer::CF_BRIDGING_KIND 1528 ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( 1529 ASTContext &Ctx, 1530 const FunctionDecl *FuncDecl) { 1531 if (FuncDecl->hasBody()) 1532 return CF_BRIDGING_NONE; 1533 1534 CallEffects CE = CallEffects::getEffect(FuncDecl); 1535 bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() || 1536 FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() || 1537 FuncDecl->hasAttr<NSReturnsRetainedAttr>() || 1538 FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() || 1539 FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 1540 1541 // Trivial case of when funciton is annotated and has no argument. 1542 if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0) 1543 return CF_BRIDGING_NONE; 1544 1545 bool ReturnCFAudited = false; 1546 if (!FuncIsReturnAnnotated) { 1547 RetEffect Ret = CE.getReturnValue(); 1548 if (Ret.getObjKind() == RetEffect::CF && 1549 (Ret.isOwned() || Ret.notOwned())) 1550 ReturnCFAudited = true; 1551 else if (!AuditedType(FuncDecl->getReturnType())) 1552 return CF_BRIDGING_NONE; 1553 } 1554 1555 // At this point result type is audited for potential inclusion. 1556 // Now, how about argument types. 1557 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1558 unsigned i = 0; 1559 bool ArgCFAudited = false; 1560 for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 1561 pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 1562 const ParmVarDecl *pd = *pi; 1563 ArgEffect AE = AEArgs[i]; 1564 if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) { 1565 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>()) 1566 ArgCFAudited = true; 1567 else if (AE == IncRef) 1568 ArgCFAudited = true; 1569 } 1570 else { 1571 QualType AT = pd->getType(); 1572 if (!AuditedType(AT)) { 1573 AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated); 1574 return CF_BRIDGING_NONE; 1575 } 1576 } 1577 } 1578 if (ReturnCFAudited || ArgCFAudited) 1579 return CF_BRIDGING_ENABLE; 1580 1581 return CF_BRIDGING_MAY_INCLUDE; 1582} 1583 1584void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, 1585 ObjCContainerDecl *CDecl) { 1586 if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated()) 1587 return; 1588 1589 // migrate methods which can have instancetype as their result type. 1590 for (const auto *Method : CDecl->methods()) 1591 migrateCFAnnotation(Ctx, Method); 1592} 1593 1594void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 1595 const CallEffects &CE, 1596 const ObjCMethodDecl *MethodDecl, 1597 bool ResultAnnotated) { 1598 // Annotate function. 1599 if (!ResultAnnotated) { 1600 RetEffect Ret = CE.getReturnValue(); 1601 const char *AnnotationString = nullptr; 1602 if (Ret.getObjKind() == RetEffect::CF) { 1603 if (Ret.isOwned() && 1604 Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 1605 AnnotationString = " CF_RETURNS_RETAINED"; 1606 else if (Ret.notOwned() && 1607 Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 1608 AnnotationString = " CF_RETURNS_NOT_RETAINED"; 1609 } 1610 else if (Ret.getObjKind() == RetEffect::ObjC) { 1611 ObjCMethodFamily OMF = MethodDecl->getMethodFamily(); 1612 switch (OMF) { 1613 case clang::OMF_alloc: 1614 case clang::OMF_new: 1615 case clang::OMF_copy: 1616 case clang::OMF_init: 1617 case clang::OMF_mutableCopy: 1618 break; 1619 1620 default: 1621 if (Ret.isOwned() && 1622 Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 1623 AnnotationString = " NS_RETURNS_RETAINED"; 1624 break; 1625 } 1626 } 1627 1628 if (AnnotationString) { 1629 edit::Commit commit(*Editor); 1630 commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString); 1631 Editor->commit(commit); 1632 } 1633 } 1634 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1635 unsigned i = 0; 1636 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 1637 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 1638 const ParmVarDecl *pd = *pi; 1639 ArgEffect AE = AEArgs[i]; 1640 if (AE == DecRef && !pd->hasAttr<CFConsumedAttr>() && 1641 Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 1642 edit::Commit commit(*Editor); 1643 commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 1644 Editor->commit(commit); 1645 } 1646 } 1647} 1648 1649void ObjCMigrateASTConsumer::migrateAddMethodAnnotation( 1650 ASTContext &Ctx, 1651 const ObjCMethodDecl *MethodDecl) { 1652 if (MethodDecl->hasBody() || MethodDecl->isImplicit()) 1653 return; 1654 1655 CallEffects CE = CallEffects::getEffect(MethodDecl); 1656 bool MethodIsReturnAnnotated = (MethodDecl->hasAttr<CFReturnsRetainedAttr>() || 1657 MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() || 1658 MethodDecl->hasAttr<NSReturnsRetainedAttr>() || 1659 MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() || 1660 MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 1661 1662 if (CE.getReceiver() == DecRefMsg && 1663 !MethodDecl->hasAttr<NSConsumesSelfAttr>() && 1664 MethodDecl->getMethodFamily() != OMF_init && 1665 MethodDecl->getMethodFamily() != OMF_release && 1666 Ctx.Idents.get("NS_CONSUMES_SELF").hasMacroDefinition()) { 1667 edit::Commit commit(*Editor); 1668 commit.insertBefore(MethodDecl->getLocEnd(), " NS_CONSUMES_SELF"); 1669 Editor->commit(commit); 1670 } 1671 1672 // Trivial case of when funciton is annotated and has no argument. 1673 if (MethodIsReturnAnnotated && 1674 (MethodDecl->param_begin() == MethodDecl->param_end())) 1675 return; 1676 1677 if (!MethodIsReturnAnnotated) { 1678 RetEffect Ret = CE.getReturnValue(); 1679 if ((Ret.getObjKind() == RetEffect::CF || 1680 Ret.getObjKind() == RetEffect::ObjC) && 1681 (Ret.isOwned() || Ret.notOwned())) { 1682 AddCFAnnotations(Ctx, CE, MethodDecl, false); 1683 return; 1684 } else if (!AuditedType(MethodDecl->getReturnType())) 1685 return; 1686 } 1687 1688 // At this point result type is either annotated or audited. 1689 // Now, how about argument types. 1690 ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1691 unsigned i = 0; 1692 for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 1693 pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 1694 const ParmVarDecl *pd = *pi; 1695 ArgEffect AE = AEArgs[i]; 1696 if ((AE == DecRef && !pd->hasAttr<CFConsumedAttr>()) || AE == IncRef || 1697 !AuditedType(pd->getType())) { 1698 AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated); 1699 return; 1700 } 1701 } 1702 return; 1703} 1704 1705namespace { 1706class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> { 1707public: 1708 bool shouldVisitTemplateInstantiations() const { return false; } 1709 bool shouldWalkTypesOfTypeLocs() const { return false; } 1710 1711 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 1712 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) { 1713 if (E->getMethodFamily() == OMF_init) 1714 return false; 1715 } 1716 return true; 1717 } 1718}; 1719} // anonymous namespace 1720 1721static bool hasSuperInitCall(const ObjCMethodDecl *MD) { 1722 return !SuperInitChecker().TraverseStmt(MD->getBody()); 1723} 1724 1725void ObjCMigrateASTConsumer::inferDesignatedInitializers( 1726 ASTContext &Ctx, 1727 const ObjCImplementationDecl *ImplD) { 1728 1729 const ObjCInterfaceDecl *IFace = ImplD->getClassInterface(); 1730 if (!IFace || IFace->hasDesignatedInitializers()) 1731 return; 1732 if (!Ctx.Idents.get("NS_DESIGNATED_INITIALIZER").hasMacroDefinition()) 1733 return; 1734 1735 for (const auto *MD : ImplD->instance_methods()) { 1736 if (MD->isDeprecated() || 1737 MD->getMethodFamily() != OMF_init || 1738 MD->isDesignatedInitializerForTheInterface()) 1739 continue; 1740 const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(), 1741 /*isInstance=*/true); 1742 if (!IFaceM) 1743 continue; 1744 if (hasSuperInitCall(MD)) { 1745 edit::Commit commit(*Editor); 1746 commit.insert(IFaceM->getLocEnd(), " NS_DESIGNATED_INITIALIZER"); 1747 Editor->commit(commit); 1748 } 1749 } 1750} 1751 1752bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx, 1753 SourceLocation Loc) { 1754 if (FoundationIncluded) 1755 return true; 1756 if (Loc.isInvalid()) 1757 return false; 1758 edit::Commit commit(*Editor); 1759 if (Ctx.getLangOpts().Modules) 1760 commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n"); 1761 else 1762 commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n"); 1763 Editor->commit(commit); 1764 FoundationIncluded = true; 1765 return true; 1766} 1767 1768namespace { 1769 1770class RewritesReceiver : public edit::EditsReceiver { 1771 Rewriter &Rewrite; 1772 1773public: 1774 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 1775 1776 void insert(SourceLocation loc, StringRef text) override { 1777 Rewrite.InsertText(loc, text); 1778 } 1779 void replace(CharSourceRange range, StringRef text) override { 1780 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 1781 } 1782}; 1783 1784class JSONEditWriter : public edit::EditsReceiver { 1785 SourceManager &SourceMgr; 1786 llvm::raw_ostream &OS; 1787 1788public: 1789 JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS) 1790 : SourceMgr(SM), OS(OS) { 1791 OS << "[\n"; 1792 } 1793 ~JSONEditWriter() override { OS << "]\n"; } 1794 1795private: 1796 struct EntryWriter { 1797 SourceManager &SourceMgr; 1798 llvm::raw_ostream &OS; 1799 1800 EntryWriter(SourceManager &SM, llvm::raw_ostream &OS) 1801 : SourceMgr(SM), OS(OS) { 1802 OS << " {\n"; 1803 } 1804 ~EntryWriter() { 1805 OS << " },\n"; 1806 } 1807 1808 void writeLoc(SourceLocation Loc) { 1809 FileID FID; 1810 unsigned Offset; 1811 std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc); 1812 assert(!FID.isInvalid()); 1813 SmallString<200> Path = 1814 StringRef(SourceMgr.getFileEntryForID(FID)->getName()); 1815 llvm::sys::fs::make_absolute(Path); 1816 OS << " \"file\": \""; 1817 OS.write_escaped(Path.str()) << "\",\n"; 1818 OS << " \"offset\": " << Offset << ",\n"; 1819 } 1820 1821 void writeRemove(CharSourceRange Range) { 1822 assert(Range.isCharRange()); 1823 std::pair<FileID, unsigned> Begin = 1824 SourceMgr.getDecomposedLoc(Range.getBegin()); 1825 std::pair<FileID, unsigned> End = 1826 SourceMgr.getDecomposedLoc(Range.getEnd()); 1827 assert(Begin.first == End.first); 1828 assert(Begin.second <= End.second); 1829 unsigned Length = End.second - Begin.second; 1830 1831 OS << " \"remove\": " << Length << ",\n"; 1832 } 1833 1834 void writeText(StringRef Text) { 1835 OS << " \"text\": \""; 1836 OS.write_escaped(Text) << "\",\n"; 1837 } 1838 }; 1839 1840 void insert(SourceLocation Loc, StringRef Text) override { 1841 EntryWriter Writer(SourceMgr, OS); 1842 Writer.writeLoc(Loc); 1843 Writer.writeText(Text); 1844 } 1845 1846 void replace(CharSourceRange Range, StringRef Text) override { 1847 EntryWriter Writer(SourceMgr, OS); 1848 Writer.writeLoc(Range.getBegin()); 1849 Writer.writeRemove(Range); 1850 Writer.writeText(Text); 1851 } 1852 1853 void remove(CharSourceRange Range) override { 1854 EntryWriter Writer(SourceMgr, OS); 1855 Writer.writeLoc(Range.getBegin()); 1856 Writer.writeRemove(Range); 1857 } 1858}; 1859 1860} 1861 1862void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 1863 1864 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 1865 if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) { 1866 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 1867 D != DEnd; ++D) { 1868 FileID FID = PP.getSourceManager().getFileID((*D)->getLocation()); 1869 if (!FID.isInvalid()) 1870 if (!FileId.isInvalid() && FileId != FID) { 1871 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1872 AnnotateImplicitBridging(Ctx); 1873 } 1874 1875 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 1876 if (canModify(CDecl)) 1877 migrateObjCContainerDecl(Ctx, CDecl); 1878 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) { 1879 if (canModify(CatDecl)) 1880 migrateObjCContainerDecl(Ctx, CatDecl); 1881 } 1882 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) { 1883 ObjCProtocolDecls.insert(PDecl->getCanonicalDecl()); 1884 if (canModify(PDecl)) 1885 migrateObjCContainerDecl(Ctx, PDecl); 1886 } 1887 else if (const ObjCImplementationDecl *ImpDecl = 1888 dyn_cast<ObjCImplementationDecl>(*D)) { 1889 if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) && 1890 canModify(ImpDecl)) 1891 migrateProtocolConformance(Ctx, ImpDecl); 1892 } 1893 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 1894 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 1895 continue; 1896 if (!canModify(ED)) 1897 continue; 1898 DeclContext::decl_iterator N = D; 1899 if (++N != DEnd) { 1900 const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N); 1901 if (migrateNSEnumDecl(Ctx, ED, TD) && TD) 1902 D++; 1903 } 1904 else 1905 migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr); 1906 } 1907 else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) { 1908 if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 1909 continue; 1910 if (!canModify(TD)) 1911 continue; 1912 DeclContext::decl_iterator N = D; 1913 if (++N == DEnd) 1914 continue; 1915 if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) { 1916 if (++N != DEnd) 1917 if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) { 1918 // prefer typedef-follows-enum to enum-follows-typedef pattern. 1919 if (migrateNSEnumDecl(Ctx, ED, TDF)) { 1920 ++D; ++D; 1921 CacheObjCNSIntegerTypedefed(TD); 1922 continue; 1923 } 1924 } 1925 if (migrateNSEnumDecl(Ctx, ED, TD)) { 1926 ++D; 1927 continue; 1928 } 1929 } 1930 CacheObjCNSIntegerTypedefed(TD); 1931 } 1932 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) { 1933 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 1934 canModify(FD)) 1935 migrateCFAnnotation(Ctx, FD); 1936 } 1937 1938 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) { 1939 bool CanModify = canModify(CDecl); 1940 // migrate methods which can have instancetype as their result type. 1941 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) && 1942 CanModify) 1943 migrateAllMethodInstaceType(Ctx, CDecl); 1944 // annotate methods with CF annotations. 1945 if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 1946 CanModify) 1947 migrateARCSafeAnnotation(Ctx, CDecl); 1948 } 1949 1950 if (const ObjCImplementationDecl * 1951 ImplD = dyn_cast<ObjCImplementationDecl>(*D)) { 1952 if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) && 1953 canModify(ImplD)) 1954 inferDesignatedInitializers(Ctx, ImplD); 1955 } 1956 } 1957 if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1958 AnnotateImplicitBridging(Ctx); 1959 } 1960 1961 if (IsOutputFile) { 1962 std::error_code EC; 1963 llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::F_None); 1964 if (EC) { 1965 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 1966 Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 1967 << EC.message(); 1968 return; 1969 } 1970 1971 JSONEditWriter Writer(Ctx.getSourceManager(), OS); 1972 Editor->applyRewrites(Writer); 1973 return; 1974 } 1975 1976 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 1977 RewritesReceiver Rec(rewriter); 1978 Editor->applyRewrites(Rec); 1979 1980 for (Rewriter::buffer_iterator 1981 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 1982 FileID FID = I->first; 1983 RewriteBuffer &buf = I->second; 1984 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 1985 assert(file); 1986 SmallString<512> newText; 1987 llvm::raw_svector_ostream vecOS(newText); 1988 buf.write(vecOS); 1989 vecOS.flush(); 1990 std::unique_ptr<llvm::MemoryBuffer> memBuf( 1991 llvm::MemoryBuffer::getMemBufferCopy( 1992 StringRef(newText.data(), newText.size()), file->getName())); 1993 SmallString<64> filePath(file->getName()); 1994 FileMgr.FixupRelativePath(filePath); 1995 Remapper.remap(filePath.str(), std::move(memBuf)); 1996 } 1997 1998 if (IsOutputFile) { 1999 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 2000 } else { 2001 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 2002 } 2003} 2004 2005bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 2006 CI.getDiagnostics().setIgnoreAllWarnings(true); 2007 return true; 2008} 2009 2010static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) { 2011 using namespace llvm::sys::fs; 2012 using namespace llvm::sys::path; 2013 2014 std::vector<std::string> Filenames; 2015 if (DirPath.empty() || !is_directory(DirPath)) 2016 return Filenames; 2017 2018 std::error_code EC; 2019 directory_iterator DI = directory_iterator(DirPath, EC); 2020 directory_iterator DE; 2021 for (; !EC && DI != DE; DI = DI.increment(EC)) { 2022 if (is_regular_file(DI->path())) 2023 Filenames.push_back(filename(DI->path())); 2024 } 2025 2026 return Filenames; 2027} 2028 2029std::unique_ptr<ASTConsumer> 2030MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 2031 PPConditionalDirectiveRecord * 2032 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 2033 unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction; 2034 unsigned ObjCMTOpts = ObjCMTAction; 2035 // These are companion flags, they do not enable transformations. 2036 ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty | 2037 FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty); 2038 if (ObjCMTOpts == FrontendOptions::ObjCMT_None) { 2039 // If no specific option was given, enable literals+subscripting transforms 2040 // by default. 2041 ObjCMTAction |= FrontendOptions::ObjCMT_Literals | 2042 FrontendOptions::ObjCMT_Subscripting; 2043 } 2044 CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 2045 std::vector<std::string> WhiteList = 2046 getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath); 2047 return llvm::make_unique<ObjCMigrateASTConsumer>( 2048 CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper, 2049 CI.getFileManager(), PPRec, CI.getPreprocessor(), 2050 /*isOutputFile=*/true, WhiteList); 2051} 2052 2053namespace { 2054struct EditEntry { 2055 const FileEntry *File; 2056 unsigned Offset; 2057 unsigned RemoveLen; 2058 std::string Text; 2059 2060 EditEntry() : File(), Offset(), RemoveLen() {} 2061}; 2062} 2063 2064namespace llvm { 2065template<> struct DenseMapInfo<EditEntry> { 2066 static inline EditEntry getEmptyKey() { 2067 EditEntry Entry; 2068 Entry.Offset = unsigned(-1); 2069 return Entry; 2070 } 2071 static inline EditEntry getTombstoneKey() { 2072 EditEntry Entry; 2073 Entry.Offset = unsigned(-2); 2074 return Entry; 2075 } 2076 static unsigned getHashValue(const EditEntry& Val) { 2077 llvm::FoldingSetNodeID ID; 2078 ID.AddPointer(Val.File); 2079 ID.AddInteger(Val.Offset); 2080 ID.AddInteger(Val.RemoveLen); 2081 ID.AddString(Val.Text); 2082 return ID.ComputeHash(); 2083 } 2084 static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) { 2085 return LHS.File == RHS.File && 2086 LHS.Offset == RHS.Offset && 2087 LHS.RemoveLen == RHS.RemoveLen && 2088 LHS.Text == RHS.Text; 2089 } 2090}; 2091} 2092 2093namespace { 2094class RemapFileParser { 2095 FileManager &FileMgr; 2096 2097public: 2098 RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { } 2099 2100 bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) { 2101 using namespace llvm::yaml; 2102 2103 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = 2104 llvm::MemoryBuffer::getFile(File); 2105 if (!FileBufOrErr) 2106 return true; 2107 2108 llvm::SourceMgr SM; 2109 Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM); 2110 document_iterator I = YAMLStream.begin(); 2111 if (I == YAMLStream.end()) 2112 return true; 2113 Node *Root = I->getRoot(); 2114 if (!Root) 2115 return true; 2116 2117 SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root); 2118 if (!SeqNode) 2119 return true; 2120 2121 for (SequenceNode::iterator 2122 AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) { 2123 MappingNode *MapNode = dyn_cast<MappingNode>(&*AI); 2124 if (!MapNode) 2125 continue; 2126 parseEdit(MapNode, Entries); 2127 } 2128 2129 return false; 2130 } 2131 2132private: 2133 void parseEdit(llvm::yaml::MappingNode *Node, 2134 SmallVectorImpl<EditEntry> &Entries) { 2135 using namespace llvm::yaml; 2136 EditEntry Entry; 2137 bool Ignore = false; 2138 2139 for (MappingNode::iterator 2140 KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) { 2141 ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey()); 2142 if (!KeyString) 2143 continue; 2144 SmallString<10> KeyStorage; 2145 StringRef Key = KeyString->getValue(KeyStorage); 2146 2147 ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue()); 2148 if (!ValueString) 2149 continue; 2150 SmallString<64> ValueStorage; 2151 StringRef Val = ValueString->getValue(ValueStorage); 2152 2153 if (Key == "file") { 2154 const FileEntry *FE = FileMgr.getFile(Val); 2155 if (!FE) 2156 Ignore = true; 2157 Entry.File = FE; 2158 } else if (Key == "offset") { 2159 if (Val.getAsInteger(10, Entry.Offset)) 2160 Ignore = true; 2161 } else if (Key == "remove") { 2162 if (Val.getAsInteger(10, Entry.RemoveLen)) 2163 Ignore = true; 2164 } else if (Key == "text") { 2165 Entry.Text = Val; 2166 } 2167 } 2168 2169 if (!Ignore) 2170 Entries.push_back(Entry); 2171 } 2172}; 2173} 2174 2175static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) { 2176 Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 2177 << Err.str(); 2178 return true; 2179} 2180 2181static std::string applyEditsToTemp(const FileEntry *FE, 2182 ArrayRef<EditEntry> Edits, 2183 FileManager &FileMgr, 2184 DiagnosticsEngine &Diag) { 2185 using namespace llvm::sys; 2186 2187 SourceManager SM(Diag, FileMgr); 2188 FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); 2189 LangOptions LangOpts; 2190 edit::EditedSource Editor(SM, LangOpts); 2191 for (ArrayRef<EditEntry>::iterator 2192 I = Edits.begin(), E = Edits.end(); I != E; ++I) { 2193 const EditEntry &Entry = *I; 2194 assert(Entry.File == FE); 2195 SourceLocation Loc = 2196 SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset); 2197 CharSourceRange Range; 2198 if (Entry.RemoveLen != 0) { 2199 Range = CharSourceRange::getCharRange(Loc, 2200 Loc.getLocWithOffset(Entry.RemoveLen)); 2201 } 2202 2203 edit::Commit commit(Editor); 2204 if (Range.isInvalid()) { 2205 commit.insert(Loc, Entry.Text); 2206 } else if (Entry.Text.empty()) { 2207 commit.remove(Range); 2208 } else { 2209 commit.replace(Range, Entry.Text); 2210 } 2211 Editor.commit(commit); 2212 } 2213 2214 Rewriter rewriter(SM, LangOpts); 2215 RewritesReceiver Rec(rewriter); 2216 Editor.applyRewrites(Rec); 2217 2218 const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID); 2219 SmallString<512> NewText; 2220 llvm::raw_svector_ostream OS(NewText); 2221 Buf->write(OS); 2222 OS.flush(); 2223 2224 SmallString<64> TempPath; 2225 int FD; 2226 if (fs::createTemporaryFile(path::filename(FE->getName()), 2227 path::extension(FE->getName()), FD, 2228 TempPath)) { 2229 reportDiag("Could not create file: " + TempPath.str(), Diag); 2230 return std::string(); 2231 } 2232 2233 llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true); 2234 TmpOut.write(NewText.data(), NewText.size()); 2235 TmpOut.close(); 2236 2237 return TempPath.str(); 2238} 2239 2240bool arcmt::getFileRemappingsFromFileList( 2241 std::vector<std::pair<std::string,std::string> > &remap, 2242 ArrayRef<StringRef> remapFiles, 2243 DiagnosticConsumer *DiagClient) { 2244 bool hasErrorOccurred = false; 2245 2246 FileSystemOptions FSOpts; 2247 FileManager FileMgr(FSOpts); 2248 RemapFileParser Parser(FileMgr); 2249 2250 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 2251 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 2252 new DiagnosticsEngine(DiagID, new DiagnosticOptions, 2253 DiagClient, /*ShouldOwnClient=*/false)); 2254 2255 typedef llvm::DenseMap<const FileEntry *, std::vector<EditEntry> > 2256 FileEditEntriesTy; 2257 FileEditEntriesTy FileEditEntries; 2258 2259 llvm::DenseSet<EditEntry> EntriesSet; 2260 2261 for (ArrayRef<StringRef>::iterator 2262 I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { 2263 SmallVector<EditEntry, 16> Entries; 2264 if (Parser.parse(*I, Entries)) 2265 continue; 2266 2267 for (SmallVectorImpl<EditEntry>::iterator 2268 EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) { 2269 EditEntry &Entry = *EI; 2270 if (!Entry.File) 2271 continue; 2272 std::pair<llvm::DenseSet<EditEntry>::iterator, bool> 2273 Insert = EntriesSet.insert(Entry); 2274 if (!Insert.second) 2275 continue; 2276 2277 FileEditEntries[Entry.File].push_back(Entry); 2278 } 2279 } 2280 2281 for (FileEditEntriesTy::iterator 2282 I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) { 2283 std::string TempFile = applyEditsToTemp(I->first, I->second, 2284 FileMgr, *Diags); 2285 if (TempFile.empty()) { 2286 hasErrorOccurred = true; 2287 continue; 2288 } 2289 2290 remap.push_back(std::make_pair(I->first->getName(), TempFile)); 2291 } 2292 2293 return hasErrorOccurred; 2294} 2295