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