1 //===--- SemaAvailability.cpp - Availability attribute handling -----------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file processes the availability attribute. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/AST/Attr.h" 14 #include "clang/AST/Decl.h" 15 #include "clang/AST/RecursiveASTVisitor.h" 16 #include "clang/Basic/DiagnosticSema.h" 17 #include "clang/Basic/TargetInfo.h" 18 #include "clang/Lex/Preprocessor.h" 19 #include "clang/Sema/DelayedDiagnostic.h" 20 #include "clang/Sema/ScopeInfo.h" 21 #include "clang/Sema/Sema.h" 22 23 using namespace clang; 24 using namespace sema; 25 26 static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, 27 const Decl *D) { 28 // Check each AvailabilityAttr to find the one for this platform. 29 for (const auto *A : D->attrs()) { 30 if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { 31 // FIXME: this is copied from CheckAvailability. We should try to 32 // de-duplicate. 33 34 // Check if this is an App Extension "platform", and if so chop off 35 // the suffix for matching with the actual platform. 36 StringRef ActualPlatform = Avail->getPlatform()->getName(); 37 StringRef RealizedPlatform = ActualPlatform; 38 if (Context.getLangOpts().AppExt) { 39 size_t suffix = RealizedPlatform.rfind("_app_extension"); 40 if (suffix != StringRef::npos) 41 RealizedPlatform = RealizedPlatform.slice(0, suffix); 42 } 43 44 StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); 45 46 // Match the platform name. 47 if (RealizedPlatform == TargetPlatform) 48 return Avail; 49 } 50 } 51 return nullptr; 52 } 53 54 /// The diagnostic we should emit for \c D, and the declaration that 55 /// originated it, or \c AR_Available. 56 /// 57 /// \param D The declaration to check. 58 /// \param Message If non-null, this will be populated with the message from 59 /// the availability attribute that is selected. 60 /// \param ClassReceiver If we're checking the the method of a class message 61 /// send, the class. Otherwise nullptr. 62 static std::pair<AvailabilityResult, const NamedDecl *> 63 ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, 64 std::string *Message, 65 ObjCInterfaceDecl *ClassReceiver) { 66 AvailabilityResult Result = D->getAvailability(Message); 67 68 // For typedefs, if the typedef declaration appears available look 69 // to the underlying type to see if it is more restrictive. 70 while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { 71 if (Result == AR_Available) { 72 if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) { 73 D = TT->getDecl(); 74 Result = D->getAvailability(Message); 75 continue; 76 } 77 } 78 break; 79 } 80 81 // Forward class declarations get their attributes from their definition. 82 if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { 83 if (IDecl->getDefinition()) { 84 D = IDecl->getDefinition(); 85 Result = D->getAvailability(Message); 86 } 87 } 88 89 if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) 90 if (Result == AR_Available) { 91 const DeclContext *DC = ECD->getDeclContext(); 92 if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { 93 Result = TheEnumDecl->getAvailability(Message); 94 D = TheEnumDecl; 95 } 96 } 97 98 // For +new, infer availability from -init. 99 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 100 if (S.NSAPIObj && ClassReceiver) { 101 ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( 102 S.NSAPIObj->getInitSelector()); 103 if (Init && Result == AR_Available && MD->isClassMethod() && 104 MD->getSelector() == S.NSAPIObj->getNewSelector() && 105 MD->definedInNSObject(S.getASTContext())) { 106 Result = Init->getAvailability(Message); 107 D = Init; 108 } 109 } 110 } 111 112 return {Result, D}; 113 } 114 115 116 /// whether we should emit a diagnostic for \c K and \c DeclVersion in 117 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic 118 /// in a deprecated context, but not the other way around. 119 static bool 120 ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, 121 VersionTuple DeclVersion, Decl *Ctx, 122 const NamedDecl *OffendingDecl) { 123 assert(K != AR_Available && "Expected an unavailable declaration here!"); 124 125 // Checks if we should emit the availability diagnostic in the context of C. 126 auto CheckContext = [&](const Decl *C) { 127 if (K == AR_NotYetIntroduced) { 128 if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) 129 if (AA->getIntroduced() >= DeclVersion) 130 return true; 131 } else if (K == AR_Deprecated) { 132 if (C->isDeprecated()) 133 return true; 134 } else if (K == AR_Unavailable) { 135 // It is perfectly fine to refer to an 'unavailable' Objective-C method 136 // when it is referenced from within the @implementation itself. In this 137 // context, we interpret unavailable as a form of access control. 138 if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) { 139 if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) { 140 if (MD->getClassInterface() == Impl->getClassInterface()) 141 return true; 142 } 143 } 144 } 145 146 if (C->isUnavailable()) 147 return true; 148 return false; 149 }; 150 151 do { 152 if (CheckContext(Ctx)) 153 return false; 154 155 // An implementation implicitly has the availability of the interface. 156 // Unless it is "+load" method. 157 if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx)) 158 if (MethodD->isClassMethod() && 159 MethodD->getSelector().getAsString() == "load") 160 return true; 161 162 if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) { 163 if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) 164 if (CheckContext(Interface)) 165 return false; 166 } 167 // A category implicitly has the availability of the interface. 168 else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx)) 169 if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) 170 if (CheckContext(Interface)) 171 return false; 172 } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext()))); 173 174 return true; 175 } 176 177 static bool 178 shouldDiagnoseAvailabilityByDefault(const ASTContext &Context, 179 const VersionTuple &DeploymentVersion, 180 const VersionTuple &DeclVersion) { 181 const auto &Triple = Context.getTargetInfo().getTriple(); 182 VersionTuple ForceAvailabilityFromVersion; 183 switch (Triple.getOS()) { 184 case llvm::Triple::IOS: 185 case llvm::Triple::TvOS: 186 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); 187 break; 188 case llvm::Triple::WatchOS: 189 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); 190 break; 191 case llvm::Triple::Darwin: 192 case llvm::Triple::MacOSX: 193 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); 194 break; 195 default: 196 // New targets should always warn about availability. 197 return Triple.getVendor() == llvm::Triple::Apple; 198 } 199 return DeploymentVersion >= ForceAvailabilityFromVersion || 200 DeclVersion >= ForceAvailabilityFromVersion; 201 } 202 203 static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { 204 for (Decl *Ctx = OrigCtx; Ctx; 205 Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { 206 if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) 207 return cast<NamedDecl>(Ctx); 208 if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { 209 if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) 210 return Imp->getClassInterface(); 211 return CD; 212 } 213 } 214 215 return dyn_cast<NamedDecl>(OrigCtx); 216 } 217 218 namespace { 219 220 struct AttributeInsertion { 221 StringRef Prefix; 222 SourceLocation Loc; 223 StringRef Suffix; 224 225 static AttributeInsertion createInsertionAfter(const NamedDecl *D) { 226 return {" ", D->getEndLoc(), ""}; 227 } 228 static AttributeInsertion createInsertionAfter(SourceLocation Loc) { 229 return {" ", Loc, ""}; 230 } 231 static AttributeInsertion createInsertionBefore(const NamedDecl *D) { 232 return {"", D->getBeginLoc(), "\n"}; 233 } 234 }; 235 236 } // end anonymous namespace 237 238 /// Tries to parse a string as ObjC method name. 239 /// 240 /// \param Name The string to parse. Expected to originate from availability 241 /// attribute argument. 242 /// \param SlotNames The vector that will be populated with slot names. In case 243 /// of unsuccessful parsing can contain invalid data. 244 /// \returns A number of method parameters if parsing was successful, None 245 /// otherwise. 246 static Optional<unsigned> 247 tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames, 248 const LangOptions &LangOpts) { 249 // Accept replacements starting with - or + as valid ObjC method names. 250 if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) 251 Name = Name.drop_front(1); 252 if (Name.empty()) 253 return None; 254 Name.split(SlotNames, ':'); 255 unsigned NumParams; 256 if (Name.back() == ':') { 257 // Remove an empty string at the end that doesn't represent any slot. 258 SlotNames.pop_back(); 259 NumParams = SlotNames.size(); 260 } else { 261 if (SlotNames.size() != 1) 262 // Not a valid method name, just a colon-separated string. 263 return None; 264 NumParams = 0; 265 } 266 // Verify all slot names are valid. 267 bool AllowDollar = LangOpts.DollarIdents; 268 for (StringRef S : SlotNames) { 269 if (S.empty()) 270 continue; 271 if (!isValidIdentifier(S, AllowDollar)) 272 return None; 273 } 274 return NumParams; 275 } 276 277 /// Returns a source location in which it's appropriate to insert a new 278 /// attribute for the given declaration \D. 279 static Optional<AttributeInsertion> 280 createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, 281 const LangOptions &LangOpts) { 282 if (isa<ObjCPropertyDecl>(D)) 283 return AttributeInsertion::createInsertionAfter(D); 284 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 285 if (MD->hasBody()) 286 return None; 287 return AttributeInsertion::createInsertionAfter(D); 288 } 289 if (const auto *TD = dyn_cast<TagDecl>(D)) { 290 SourceLocation Loc = 291 Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts); 292 if (Loc.isInvalid()) 293 return None; 294 // Insert after the 'struct'/whatever keyword. 295 return AttributeInsertion::createInsertionAfter(Loc); 296 } 297 return AttributeInsertion::createInsertionBefore(D); 298 } 299 300 /// Actually emit an availability diagnostic for a reference to an unavailable 301 /// decl. 302 /// 303 /// \param Ctx The context that the reference occurred in 304 /// \param ReferringDecl The exact declaration that was referenced. 305 /// \param OffendingDecl A related decl to \c ReferringDecl that has an 306 /// availability attribute corresponding to \c K attached to it. Note that this 307 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and 308 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl 309 /// and OffendingDecl is the EnumDecl. 310 static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, 311 Decl *Ctx, const NamedDecl *ReferringDecl, 312 const NamedDecl *OffendingDecl, 313 StringRef Message, 314 ArrayRef<SourceLocation> Locs, 315 const ObjCInterfaceDecl *UnknownObjCClass, 316 const ObjCPropertyDecl *ObjCProperty, 317 bool ObjCPropertyAccess) { 318 // Diagnostics for deprecated or unavailable. 319 unsigned diag, diag_message, diag_fwdclass_message; 320 unsigned diag_available_here = diag::note_availability_specified_here; 321 SourceLocation NoteLocation = OffendingDecl->getLocation(); 322 323 // Matches 'diag::note_property_attribute' options. 324 unsigned property_note_select; 325 326 // Matches diag::note_availability_specified_here. 327 unsigned available_here_select_kind; 328 329 VersionTuple DeclVersion; 330 if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl)) 331 DeclVersion = AA->getIntroduced(); 332 333 if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx, 334 OffendingDecl)) 335 return; 336 337 SourceLocation Loc = Locs.front(); 338 339 // The declaration can have multiple availability attributes, we are looking 340 // at one of them. 341 const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); 342 if (A && A->isInherited()) { 343 for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; 344 Redecl = Redecl->getPreviousDecl()) { 345 const AvailabilityAttr *AForRedecl = 346 getAttrForPlatform(S.Context, Redecl); 347 if (AForRedecl && !AForRedecl->isInherited()) { 348 // If D is a declaration with inherited attributes, the note should 349 // point to the declaration with actual attributes. 350 NoteLocation = Redecl->getLocation(); 351 break; 352 } 353 } 354 } 355 356 switch (K) { 357 case AR_NotYetIntroduced: { 358 // We would like to emit the diagnostic even if -Wunguarded-availability is 359 // not specified for deployment targets >= to iOS 11 or equivalent or 360 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 361 // later. 362 const AvailabilityAttr *AA = 363 getAttrForPlatform(S.getASTContext(), OffendingDecl); 364 VersionTuple Introduced = AA->getIntroduced(); 365 366 bool UseNewWarning = shouldDiagnoseAvailabilityByDefault( 367 S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), 368 Introduced); 369 unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new 370 : diag::warn_unguarded_availability; 371 372 std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( 373 S.getASTContext().getTargetInfo().getPlatformName())); 374 375 S.Diag(Loc, Warning) << OffendingDecl << PlatformName 376 << Introduced.getAsString(); 377 378 S.Diag(OffendingDecl->getLocation(), 379 diag::note_partial_availability_specified_here) 380 << OffendingDecl << PlatformName << Introduced.getAsString() 381 << S.Context.getTargetInfo().getPlatformMinVersion().getAsString(); 382 383 if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { 384 if (const auto *TD = dyn_cast<TagDecl>(Enclosing)) 385 if (TD->getDeclName().isEmpty()) { 386 S.Diag(TD->getLocation(), 387 diag::note_decl_unguarded_availability_silence) 388 << /*Anonymous*/ 1 << TD->getKindName(); 389 return; 390 } 391 auto FixitNoteDiag = 392 S.Diag(Enclosing->getLocation(), 393 diag::note_decl_unguarded_availability_silence) 394 << /*Named*/ 0 << Enclosing; 395 // Don't offer a fixit for declarations with availability attributes. 396 if (Enclosing->hasAttr<AvailabilityAttr>()) 397 return; 398 if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE")) 399 return; 400 Optional<AttributeInsertion> Insertion = createAttributeInsertion( 401 Enclosing, S.getSourceManager(), S.getLangOpts()); 402 if (!Insertion) 403 return; 404 std::string PlatformName = 405 AvailabilityAttr::getPlatformNameSourceSpelling( 406 S.getASTContext().getTargetInfo().getPlatformName()) 407 .lower(); 408 std::string Introduced = 409 OffendingDecl->getVersionIntroduced().getAsString(); 410 FixitNoteDiag << FixItHint::CreateInsertion( 411 Insertion->Loc, 412 (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName + 413 "(" + Introduced + "))" + Insertion->Suffix) 414 .str()); 415 } 416 return; 417 } 418 case AR_Deprecated: 419 diag = !ObjCPropertyAccess ? diag::warn_deprecated 420 : diag::warn_property_method_deprecated; 421 diag_message = diag::warn_deprecated_message; 422 diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; 423 property_note_select = /* deprecated */ 0; 424 available_here_select_kind = /* deprecated */ 2; 425 if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>()) 426 NoteLocation = AL->getLocation(); 427 break; 428 429 case AR_Unavailable: 430 diag = !ObjCPropertyAccess ? diag::err_unavailable 431 : diag::err_property_method_unavailable; 432 diag_message = diag::err_unavailable_message; 433 diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; 434 property_note_select = /* unavailable */ 1; 435 available_here_select_kind = /* unavailable */ 0; 436 437 if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) { 438 if (AL->isImplicit() && AL->getImplicitReason()) { 439 // Most of these failures are due to extra restrictions in ARC; 440 // reflect that in the primary diagnostic when applicable. 441 auto flagARCError = [&] { 442 if (S.getLangOpts().ObjCAutoRefCount && 443 S.getSourceManager().isInSystemHeader( 444 OffendingDecl->getLocation())) 445 diag = diag::err_unavailable_in_arc; 446 }; 447 448 switch (AL->getImplicitReason()) { 449 case UnavailableAttr::IR_None: break; 450 451 case UnavailableAttr::IR_ARCForbiddenType: 452 flagARCError(); 453 diag_available_here = diag::note_arc_forbidden_type; 454 break; 455 456 case UnavailableAttr::IR_ForbiddenWeak: 457 if (S.getLangOpts().ObjCWeakRuntime) 458 diag_available_here = diag::note_arc_weak_disabled; 459 else 460 diag_available_here = diag::note_arc_weak_no_runtime; 461 break; 462 463 case UnavailableAttr::IR_ARCForbiddenConversion: 464 flagARCError(); 465 diag_available_here = diag::note_performs_forbidden_arc_conversion; 466 break; 467 468 case UnavailableAttr::IR_ARCInitReturnsUnrelated: 469 flagARCError(); 470 diag_available_here = diag::note_arc_init_returns_unrelated; 471 break; 472 473 case UnavailableAttr::IR_ARCFieldWithOwnership: 474 flagARCError(); 475 diag_available_here = diag::note_arc_field_with_ownership; 476 break; 477 } 478 } 479 } 480 break; 481 482 case AR_Available: 483 llvm_unreachable("Warning for availability of available declaration?"); 484 } 485 486 SmallVector<FixItHint, 12> FixIts; 487 if (K == AR_Deprecated) { 488 StringRef Replacement; 489 if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>()) 490 Replacement = AL->getReplacement(); 491 if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) 492 Replacement = AL->getReplacement(); 493 494 CharSourceRange UseRange; 495 if (!Replacement.empty()) 496 UseRange = 497 CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); 498 if (UseRange.isValid()) { 499 if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) { 500 Selector Sel = MethodDecl->getSelector(); 501 SmallVector<StringRef, 12> SelectorSlotNames; 502 Optional<unsigned> NumParams = tryParseObjCMethodName( 503 Replacement, SelectorSlotNames, S.getLangOpts()); 504 if (NumParams && NumParams.getValue() == Sel.getNumArgs()) { 505 assert(SelectorSlotNames.size() == Locs.size()); 506 for (unsigned I = 0; I < Locs.size(); ++I) { 507 if (!Sel.getNameForSlot(I).empty()) { 508 CharSourceRange NameRange = CharSourceRange::getCharRange( 509 Locs[I], S.getLocForEndOfToken(Locs[I])); 510 FixIts.push_back(FixItHint::CreateReplacement( 511 NameRange, SelectorSlotNames[I])); 512 } else 513 FixIts.push_back( 514 FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I])); 515 } 516 } else 517 FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 518 } else 519 FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 520 } 521 } 522 523 if (!Message.empty()) { 524 S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; 525 if (ObjCProperty) 526 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 527 << ObjCProperty->getDeclName() << property_note_select; 528 } else if (!UnknownObjCClass) { 529 S.Diag(Loc, diag) << ReferringDecl << FixIts; 530 if (ObjCProperty) 531 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 532 << ObjCProperty->getDeclName() << property_note_select; 533 } else { 534 S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; 535 S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); 536 } 537 538 S.Diag(NoteLocation, diag_available_here) 539 << OffendingDecl << available_here_select_kind; 540 } 541 542 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { 543 assert(DD.Kind == DelayedDiagnostic::Availability && 544 "Expected an availability diagnostic here"); 545 546 DD.Triggered = true; 547 DoEmitAvailabilityWarning( 548 *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), 549 DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), 550 DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), 551 DD.getObjCProperty(), false); 552 } 553 554 static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, 555 const NamedDecl *ReferringDecl, 556 const NamedDecl *OffendingDecl, 557 StringRef Message, 558 ArrayRef<SourceLocation> Locs, 559 const ObjCInterfaceDecl *UnknownObjCClass, 560 const ObjCPropertyDecl *ObjCProperty, 561 bool ObjCPropertyAccess) { 562 // Delay if we're currently parsing a declaration. 563 if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { 564 S.DelayedDiagnostics.add( 565 DelayedDiagnostic::makeAvailability( 566 AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, 567 ObjCProperty, Message, ObjCPropertyAccess)); 568 return; 569 } 570 571 Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); 572 DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, 573 Message, Locs, UnknownObjCClass, ObjCProperty, 574 ObjCPropertyAccess); 575 } 576 577 namespace { 578 579 /// Returns true if the given statement can be a body-like child of \p Parent. 580 bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { 581 switch (Parent->getStmtClass()) { 582 case Stmt::IfStmtClass: 583 return cast<IfStmt>(Parent)->getThen() == S || 584 cast<IfStmt>(Parent)->getElse() == S; 585 case Stmt::WhileStmtClass: 586 return cast<WhileStmt>(Parent)->getBody() == S; 587 case Stmt::DoStmtClass: 588 return cast<DoStmt>(Parent)->getBody() == S; 589 case Stmt::ForStmtClass: 590 return cast<ForStmt>(Parent)->getBody() == S; 591 case Stmt::CXXForRangeStmtClass: 592 return cast<CXXForRangeStmt>(Parent)->getBody() == S; 593 case Stmt::ObjCForCollectionStmtClass: 594 return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; 595 case Stmt::CaseStmtClass: 596 case Stmt::DefaultStmtClass: 597 return cast<SwitchCase>(Parent)->getSubStmt() == S; 598 default: 599 return false; 600 } 601 } 602 603 class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> { 604 const Stmt *Target; 605 606 public: 607 bool VisitStmt(Stmt *S) { return S != Target; } 608 609 /// Returns true if the given statement is present in the given declaration. 610 static bool isContained(const Stmt *Target, const Decl *D) { 611 StmtUSEFinder Visitor; 612 Visitor.Target = Target; 613 return !Visitor.TraverseDecl(const_cast<Decl *>(D)); 614 } 615 }; 616 617 /// Traverses the AST and finds the last statement that used a given 618 /// declaration. 619 class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> { 620 const Decl *D; 621 622 public: 623 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 624 if (DRE->getDecl() == D) 625 return false; 626 return true; 627 } 628 629 static const Stmt *findLastStmtThatUsesDecl(const Decl *D, 630 const CompoundStmt *Scope) { 631 LastDeclUSEFinder Visitor; 632 Visitor.D = D; 633 for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; ++I) { 634 const Stmt *S = *I; 635 if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) 636 return S; 637 } 638 return nullptr; 639 } 640 }; 641 642 /// This class implements -Wunguarded-availability. 643 /// 644 /// This is done with a traversal of the AST of a function that makes reference 645 /// to a partially available declaration. Whenever we encounter an \c if of the 646 /// form: \c if(@available(...)), we use the version from the condition to visit 647 /// the then statement. 648 class DiagnoseUnguardedAvailability 649 : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> { 650 typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base; 651 652 Sema &SemaRef; 653 Decl *Ctx; 654 655 /// Stack of potentially nested 'if (@available(...))'s. 656 SmallVector<VersionTuple, 8> AvailabilityStack; 657 SmallVector<const Stmt *, 16> StmtStack; 658 659 void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, 660 ObjCInterfaceDecl *ClassReceiver = nullptr); 661 662 public: 663 DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) 664 : SemaRef(SemaRef), Ctx(Ctx) { 665 AvailabilityStack.push_back( 666 SemaRef.Context.getTargetInfo().getPlatformMinVersion()); 667 } 668 669 bool TraverseDecl(Decl *D) { 670 // Avoid visiting nested functions to prevent duplicate warnings. 671 if (!D || isa<FunctionDecl>(D)) 672 return true; 673 return Base::TraverseDecl(D); 674 } 675 676 bool TraverseStmt(Stmt *S) { 677 if (!S) 678 return true; 679 StmtStack.push_back(S); 680 bool Result = Base::TraverseStmt(S); 681 StmtStack.pop_back(); 682 return Result; 683 } 684 685 void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } 686 687 bool TraverseIfStmt(IfStmt *If); 688 689 bool TraverseLambdaExpr(LambdaExpr *E) { return true; } 690 691 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead 692 // to any useful diagnostics. 693 bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } 694 695 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { 696 if (PRE->isClassReceiver()) 697 DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation()); 698 return true; 699 } 700 701 bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { 702 if (ObjCMethodDecl *D = Msg->getMethodDecl()) { 703 ObjCInterfaceDecl *ID = nullptr; 704 QualType ReceiverTy = Msg->getClassReceiver(); 705 if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) 706 ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); 707 708 DiagnoseDeclAvailability( 709 D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); 710 } 711 return true; 712 } 713 714 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 715 DiagnoseDeclAvailability(DRE->getDecl(), 716 SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); 717 return true; 718 } 719 720 bool VisitMemberExpr(MemberExpr *ME) { 721 DiagnoseDeclAvailability(ME->getMemberDecl(), 722 SourceRange(ME->getBeginLoc(), ME->getEndLoc())); 723 return true; 724 } 725 726 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { 727 SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) 728 << (!SemaRef.getLangOpts().ObjC); 729 return true; 730 } 731 732 bool VisitTypeLoc(TypeLoc Ty); 733 }; 734 735 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( 736 NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { 737 AvailabilityResult Result; 738 const NamedDecl *OffendingDecl; 739 std::tie(Result, OffendingDecl) = 740 ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); 741 if (Result != AR_Available) { 742 // All other diagnostic kinds have already been handled in 743 // DiagnoseAvailabilityOfDecl. 744 if (Result != AR_NotYetIntroduced) 745 return; 746 747 const AvailabilityAttr *AA = 748 getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); 749 VersionTuple Introduced = AA->getIntroduced(); 750 751 if (AvailabilityStack.back() >= Introduced) 752 return; 753 754 // If the context of this function is less available than D, we should not 755 // emit a diagnostic. 756 if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx, 757 OffendingDecl)) 758 return; 759 760 // We would like to emit the diagnostic even if -Wunguarded-availability is 761 // not specified for deployment targets >= to iOS 11 or equivalent or 762 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 763 // later. 764 unsigned DiagKind = 765 shouldDiagnoseAvailabilityByDefault( 766 SemaRef.Context, 767 SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced) 768 ? diag::warn_unguarded_availability_new 769 : diag::warn_unguarded_availability; 770 771 std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( 772 SemaRef.getASTContext().getTargetInfo().getPlatformName())); 773 774 SemaRef.Diag(Range.getBegin(), DiagKind) 775 << Range << D << PlatformName << Introduced.getAsString(); 776 777 SemaRef.Diag(OffendingDecl->getLocation(), 778 diag::note_partial_availability_specified_here) 779 << OffendingDecl << PlatformName << Introduced.getAsString() 780 << SemaRef.Context.getTargetInfo() 781 .getPlatformMinVersion() 782 .getAsString(); 783 784 auto FixitDiag = 785 SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) 786 << Range << D 787 << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 788 : /*__builtin_available*/ 1); 789 790 // Find the statement which should be enclosed in the if @available check. 791 if (StmtStack.empty()) 792 return; 793 const Stmt *StmtOfUse = StmtStack.back(); 794 const CompoundStmt *Scope = nullptr; 795 for (const Stmt *S : llvm::reverse(StmtStack)) { 796 if (const auto *CS = dyn_cast<CompoundStmt>(S)) { 797 Scope = CS; 798 break; 799 } 800 if (isBodyLikeChildStmt(StmtOfUse, S)) { 801 // The declaration won't be seen outside of the statement, so we don't 802 // have to wrap the uses of any declared variables in if (@available). 803 // Therefore we can avoid setting Scope here. 804 break; 805 } 806 StmtOfUse = S; 807 } 808 const Stmt *LastStmtOfUse = nullptr; 809 if (isa<DeclStmt>(StmtOfUse) && Scope) { 810 for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { 811 if (StmtUSEFinder::isContained(StmtStack.back(), D)) { 812 LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); 813 break; 814 } 815 } 816 } 817 818 const SourceManager &SM = SemaRef.getSourceManager(); 819 SourceLocation IfInsertionLoc = 820 SM.getExpansionLoc(StmtOfUse->getBeginLoc()); 821 SourceLocation StmtEndLoc = 822 SM.getExpansionRange( 823 (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) 824 .getEnd(); 825 if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) 826 return; 827 828 StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); 829 const char *ExtraIndentation = " "; 830 std::string FixItString; 831 llvm::raw_string_ostream FixItOS(FixItString); 832 FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" 833 : "__builtin_available") 834 << "(" 835 << AvailabilityAttr::getPlatformNameSourceSpelling( 836 SemaRef.getASTContext().getTargetInfo().getPlatformName()) 837 << " " << Introduced.getAsString() << ", *)) {\n" 838 << Indentation << ExtraIndentation; 839 FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); 840 SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( 841 StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), 842 /*SkipTrailingWhitespaceAndNewLine=*/false); 843 if (ElseInsertionLoc.isInvalid()) 844 ElseInsertionLoc = 845 Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); 846 FixItOS.str().clear(); 847 FixItOS << "\n" 848 << Indentation << "} else {\n" 849 << Indentation << ExtraIndentation 850 << "// Fallback on earlier versions\n" 851 << Indentation << "}"; 852 FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); 853 } 854 } 855 856 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { 857 const Type *TyPtr = Ty.getTypePtr(); 858 SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; 859 860 if (Range.isInvalid()) 861 return true; 862 863 if (const auto *TT = dyn_cast<TagType>(TyPtr)) { 864 TagDecl *TD = TT->getDecl(); 865 DiagnoseDeclAvailability(TD, Range); 866 867 } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { 868 TypedefNameDecl *D = TD->getDecl(); 869 DiagnoseDeclAvailability(D, Range); 870 871 } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { 872 if (NamedDecl *D = ObjCO->getInterface()) 873 DiagnoseDeclAvailability(D, Range); 874 } 875 876 return true; 877 } 878 879 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { 880 VersionTuple CondVersion; 881 if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) { 882 CondVersion = E->getVersion(); 883 884 // If we're using the '*' case here or if this check is redundant, then we 885 // use the enclosing version to check both branches. 886 if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) 887 return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); 888 } else { 889 // This isn't an availability checking 'if', we can just continue. 890 return Base::TraverseIfStmt(If); 891 } 892 893 AvailabilityStack.push_back(CondVersion); 894 bool ShouldContinue = TraverseStmt(If->getThen()); 895 AvailabilityStack.pop_back(); 896 897 return ShouldContinue && TraverseStmt(If->getElse()); 898 } 899 900 } // end anonymous namespace 901 902 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { 903 Stmt *Body = nullptr; 904 905 if (auto *FD = D->getAsFunction()) { 906 // FIXME: We only examine the pattern decl for availability violations now, 907 // but we should also examine instantiated templates. 908 if (FD->isTemplateInstantiation()) 909 return; 910 911 Body = FD->getBody(); 912 } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) 913 Body = MD->getBody(); 914 else if (auto *BD = dyn_cast<BlockDecl>(D)) 915 Body = BD->getBody(); 916 917 assert(Body && "Need a body here!"); 918 919 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); 920 } 921 922 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, 923 ArrayRef<SourceLocation> Locs, 924 const ObjCInterfaceDecl *UnknownObjCClass, 925 bool ObjCPropertyAccess, 926 bool AvoidPartialAvailabilityChecks, 927 ObjCInterfaceDecl *ClassReceiver) { 928 std::string Message; 929 AvailabilityResult Result; 930 const NamedDecl* OffendingDecl; 931 // See if this declaration is unavailable, deprecated, or partial. 932 std::tie(Result, OffendingDecl) = 933 ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); 934 if (Result == AR_Available) 935 return; 936 937 if (Result == AR_NotYetIntroduced) { 938 if (AvoidPartialAvailabilityChecks) 939 return; 940 941 // We need to know the @available context in the current function to 942 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that 943 // when we're done parsing the current function. 944 if (getCurFunctionOrMethodDecl()) { 945 getEnclosingFunction()->HasPotentialAvailabilityViolations = true; 946 return; 947 } else if (getCurBlock() || getCurLambda()) { 948 getCurFunction()->HasPotentialAvailabilityViolations = true; 949 return; 950 } 951 } 952 953 const ObjCPropertyDecl *ObjCPDecl = nullptr; 954 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 955 if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { 956 AvailabilityResult PDeclResult = PD->getAvailability(nullptr); 957 if (PDeclResult == Result) 958 ObjCPDecl = PD; 959 } 960 } 961 962 EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, 963 UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); 964 } 965