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 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 (!isValidAsciiIdentifier(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 == 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 (const Stmt *S : llvm::reverse(Scope->body())) { 634 if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) 635 return S; 636 } 637 return nullptr; 638 } 639 }; 640 641 /// This class implements -Wunguarded-availability. 642 /// 643 /// This is done with a traversal of the AST of a function that makes reference 644 /// to a partially available declaration. Whenever we encounter an \c if of the 645 /// form: \c if(@available(...)), we use the version from the condition to visit 646 /// the then statement. 647 class DiagnoseUnguardedAvailability 648 : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> { 649 typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base; 650 651 Sema &SemaRef; 652 Decl *Ctx; 653 654 /// Stack of potentially nested 'if (@available(...))'s. 655 SmallVector<VersionTuple, 8> AvailabilityStack; 656 SmallVector<const Stmt *, 16> StmtStack; 657 658 void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, 659 ObjCInterfaceDecl *ClassReceiver = nullptr); 660 661 public: 662 DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) 663 : SemaRef(SemaRef), Ctx(Ctx) { 664 AvailabilityStack.push_back( 665 SemaRef.Context.getTargetInfo().getPlatformMinVersion()); 666 } 667 668 bool TraverseStmt(Stmt *S) { 669 if (!S) 670 return true; 671 StmtStack.push_back(S); 672 bool Result = Base::TraverseStmt(S); 673 StmtStack.pop_back(); 674 return Result; 675 } 676 677 void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } 678 679 bool TraverseIfStmt(IfStmt *If); 680 681 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead 682 // to any useful diagnostics. 683 bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } 684 685 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; } 686 687 bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { 688 if (ObjCMethodDecl *D = Msg->getMethodDecl()) { 689 ObjCInterfaceDecl *ID = nullptr; 690 QualType ReceiverTy = Msg->getClassReceiver(); 691 if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) 692 ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); 693 694 DiagnoseDeclAvailability( 695 D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); 696 } 697 return true; 698 } 699 700 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 701 DiagnoseDeclAvailability(DRE->getDecl(), 702 SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); 703 return true; 704 } 705 706 bool VisitMemberExpr(MemberExpr *ME) { 707 DiagnoseDeclAvailability(ME->getMemberDecl(), 708 SourceRange(ME->getBeginLoc(), ME->getEndLoc())); 709 return true; 710 } 711 712 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { 713 SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) 714 << (!SemaRef.getLangOpts().ObjC); 715 return true; 716 } 717 718 bool VisitTypeLoc(TypeLoc Ty); 719 }; 720 721 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( 722 NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { 723 AvailabilityResult Result; 724 const NamedDecl *OffendingDecl; 725 std::tie(Result, OffendingDecl) = 726 ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); 727 if (Result != AR_Available) { 728 // All other diagnostic kinds have already been handled in 729 // DiagnoseAvailabilityOfDecl. 730 if (Result != AR_NotYetIntroduced) 731 return; 732 733 const AvailabilityAttr *AA = 734 getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); 735 VersionTuple Introduced = AA->getIntroduced(); 736 737 if (AvailabilityStack.back() >= Introduced) 738 return; 739 740 // If the context of this function is less available than D, we should not 741 // emit a diagnostic. 742 if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx, 743 OffendingDecl)) 744 return; 745 746 // We would like to emit the diagnostic even if -Wunguarded-availability is 747 // not specified for deployment targets >= to iOS 11 or equivalent or 748 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 749 // later. 750 unsigned DiagKind = 751 shouldDiagnoseAvailabilityByDefault( 752 SemaRef.Context, 753 SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced) 754 ? diag::warn_unguarded_availability_new 755 : diag::warn_unguarded_availability; 756 757 std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( 758 SemaRef.getASTContext().getTargetInfo().getPlatformName())); 759 760 SemaRef.Diag(Range.getBegin(), DiagKind) 761 << Range << D << PlatformName << Introduced.getAsString(); 762 763 SemaRef.Diag(OffendingDecl->getLocation(), 764 diag::note_partial_availability_specified_here) 765 << OffendingDecl << PlatformName << Introduced.getAsString() 766 << SemaRef.Context.getTargetInfo() 767 .getPlatformMinVersion() 768 .getAsString(); 769 770 auto FixitDiag = 771 SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) 772 << Range << D 773 << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 774 : /*__builtin_available*/ 1); 775 776 // Find the statement which should be enclosed in the if @available check. 777 if (StmtStack.empty()) 778 return; 779 const Stmt *StmtOfUse = StmtStack.back(); 780 const CompoundStmt *Scope = nullptr; 781 for (const Stmt *S : llvm::reverse(StmtStack)) { 782 if (const auto *CS = dyn_cast<CompoundStmt>(S)) { 783 Scope = CS; 784 break; 785 } 786 if (isBodyLikeChildStmt(StmtOfUse, S)) { 787 // The declaration won't be seen outside of the statement, so we don't 788 // have to wrap the uses of any declared variables in if (@available). 789 // Therefore we can avoid setting Scope here. 790 break; 791 } 792 StmtOfUse = S; 793 } 794 const Stmt *LastStmtOfUse = nullptr; 795 if (isa<DeclStmt>(StmtOfUse) && Scope) { 796 for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { 797 if (StmtUSEFinder::isContained(StmtStack.back(), D)) { 798 LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); 799 break; 800 } 801 } 802 } 803 804 const SourceManager &SM = SemaRef.getSourceManager(); 805 SourceLocation IfInsertionLoc = 806 SM.getExpansionLoc(StmtOfUse->getBeginLoc()); 807 SourceLocation StmtEndLoc = 808 SM.getExpansionRange( 809 (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) 810 .getEnd(); 811 if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) 812 return; 813 814 StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); 815 const char *ExtraIndentation = " "; 816 std::string FixItString; 817 llvm::raw_string_ostream FixItOS(FixItString); 818 FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" 819 : "__builtin_available") 820 << "(" 821 << AvailabilityAttr::getPlatformNameSourceSpelling( 822 SemaRef.getASTContext().getTargetInfo().getPlatformName()) 823 << " " << Introduced.getAsString() << ", *)) {\n" 824 << Indentation << ExtraIndentation; 825 FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); 826 SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( 827 StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), 828 /*SkipTrailingWhitespaceAndNewLine=*/false); 829 if (ElseInsertionLoc.isInvalid()) 830 ElseInsertionLoc = 831 Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); 832 FixItOS.str().clear(); 833 FixItOS << "\n" 834 << Indentation << "} else {\n" 835 << Indentation << ExtraIndentation 836 << "// Fallback on earlier versions\n" 837 << Indentation << "}"; 838 FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); 839 } 840 } 841 842 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { 843 const Type *TyPtr = Ty.getTypePtr(); 844 SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; 845 846 if (Range.isInvalid()) 847 return true; 848 849 if (const auto *TT = dyn_cast<TagType>(TyPtr)) { 850 TagDecl *TD = TT->getDecl(); 851 DiagnoseDeclAvailability(TD, Range); 852 853 } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { 854 TypedefNameDecl *D = TD->getDecl(); 855 DiagnoseDeclAvailability(D, Range); 856 857 } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { 858 if (NamedDecl *D = ObjCO->getInterface()) 859 DiagnoseDeclAvailability(D, Range); 860 } 861 862 return true; 863 } 864 865 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { 866 VersionTuple CondVersion; 867 if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) { 868 CondVersion = E->getVersion(); 869 870 // If we're using the '*' case here or if this check is redundant, then we 871 // use the enclosing version to check both branches. 872 if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) 873 return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); 874 } else { 875 // This isn't an availability checking 'if', we can just continue. 876 return Base::TraverseIfStmt(If); 877 } 878 879 AvailabilityStack.push_back(CondVersion); 880 bool ShouldContinue = TraverseStmt(If->getThen()); 881 AvailabilityStack.pop_back(); 882 883 return ShouldContinue && TraverseStmt(If->getElse()); 884 } 885 886 } // end anonymous namespace 887 888 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { 889 Stmt *Body = nullptr; 890 891 if (auto *FD = D->getAsFunction()) { 892 // FIXME: We only examine the pattern decl for availability violations now, 893 // but we should also examine instantiated templates. 894 if (FD->isTemplateInstantiation()) 895 return; 896 897 Body = FD->getBody(); 898 } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) 899 Body = MD->getBody(); 900 else if (auto *BD = dyn_cast<BlockDecl>(D)) 901 Body = BD->getBody(); 902 903 assert(Body && "Need a body here!"); 904 905 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); 906 } 907 908 FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { 909 if (FunctionScopes.empty()) 910 return nullptr; 911 912 // Conservatively search the entire current function scope context for 913 // availability violations. This ensures we always correctly analyze nested 914 // classes, blocks, lambdas, etc. that may or may not be inside if(@available) 915 // checks themselves. 916 return FunctionScopes.front(); 917 } 918 919 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, 920 ArrayRef<SourceLocation> Locs, 921 const ObjCInterfaceDecl *UnknownObjCClass, 922 bool ObjCPropertyAccess, 923 bool AvoidPartialAvailabilityChecks, 924 ObjCInterfaceDecl *ClassReceiver) { 925 std::string Message; 926 AvailabilityResult Result; 927 const NamedDecl* OffendingDecl; 928 // See if this declaration is unavailable, deprecated, or partial. 929 std::tie(Result, OffendingDecl) = 930 ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); 931 if (Result == AR_Available) 932 return; 933 934 if (Result == AR_NotYetIntroduced) { 935 if (AvoidPartialAvailabilityChecks) 936 return; 937 938 // We need to know the @available context in the current function to 939 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that 940 // when we're done parsing the current function. 941 if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { 942 Context->HasPotentialAvailabilityViolations = true; 943 return; 944 } 945 } 946 947 const ObjCPropertyDecl *ObjCPDecl = nullptr; 948 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 949 if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { 950 AvailabilityResult PDeclResult = PD->getAvailability(nullptr); 951 if (PDeclResult == Result) 952 ObjCPDecl = PD; 953 } 954 } 955 956 EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, 957 UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); 958 } 959