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 // We emit deprecation warning for deprecated specializations 540 // when their instantiation stacks originate outside 541 // of a system header, even if the diagnostics is suppresed at the 542 // point of definition. 543 SourceLocation InstantiationLoc = 544 S.getTopMostPointOfInstantiation(ReferringDecl); 545 bool ShouldAllowWarningInSystemHeader = 546 InstantiationLoc != Loc && 547 !S.getSourceManager().isInSystemHeader(InstantiationLoc); 548 struct AllowWarningInSystemHeaders { 549 AllowWarningInSystemHeaders(DiagnosticsEngine &E, 550 bool AllowWarningInSystemHeaders) 551 : Engine(E), Prev(E.getSuppressSystemWarnings()) { 552 E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders); 553 } 554 ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); } 555 556 private: 557 DiagnosticsEngine &Engine; 558 bool Prev; 559 } SystemWarningOverrideRAII(S.getDiagnostics(), 560 ShouldAllowWarningInSystemHeader); 561 562 if (!Message.empty()) { 563 S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; 564 if (ObjCProperty) 565 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 566 << ObjCProperty->getDeclName() << property_note_select; 567 } else if (!UnknownObjCClass) { 568 S.Diag(Loc, diag) << ReferringDecl << FixIts; 569 if (ObjCProperty) 570 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 571 << ObjCProperty->getDeclName() << property_note_select; 572 } else { 573 S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; 574 S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); 575 } 576 577 S.Diag(NoteLocation, diag_available_here) 578 << OffendingDecl << available_here_select_kind; 579 } 580 581 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { 582 assert(DD.Kind == DelayedDiagnostic::Availability && 583 "Expected an availability diagnostic here"); 584 585 DD.Triggered = true; 586 DoEmitAvailabilityWarning( 587 *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), 588 DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), 589 DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), 590 DD.getObjCProperty(), false); 591 } 592 593 static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, 594 const NamedDecl *ReferringDecl, 595 const NamedDecl *OffendingDecl, 596 StringRef Message, 597 ArrayRef<SourceLocation> Locs, 598 const ObjCInterfaceDecl *UnknownObjCClass, 599 const ObjCPropertyDecl *ObjCProperty, 600 bool ObjCPropertyAccess) { 601 // Delay if we're currently parsing a declaration. 602 if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { 603 S.DelayedDiagnostics.add( 604 DelayedDiagnostic::makeAvailability( 605 AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, 606 ObjCProperty, Message, ObjCPropertyAccess)); 607 return; 608 } 609 610 Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); 611 DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, 612 Message, Locs, UnknownObjCClass, ObjCProperty, 613 ObjCPropertyAccess); 614 } 615 616 namespace { 617 618 /// Returns true if the given statement can be a body-like child of \p Parent. 619 bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { 620 switch (Parent->getStmtClass()) { 621 case Stmt::IfStmtClass: 622 return cast<IfStmt>(Parent)->getThen() == S || 623 cast<IfStmt>(Parent)->getElse() == S; 624 case Stmt::WhileStmtClass: 625 return cast<WhileStmt>(Parent)->getBody() == S; 626 case Stmt::DoStmtClass: 627 return cast<DoStmt>(Parent)->getBody() == S; 628 case Stmt::ForStmtClass: 629 return cast<ForStmt>(Parent)->getBody() == S; 630 case Stmt::CXXForRangeStmtClass: 631 return cast<CXXForRangeStmt>(Parent)->getBody() == S; 632 case Stmt::ObjCForCollectionStmtClass: 633 return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; 634 case Stmt::CaseStmtClass: 635 case Stmt::DefaultStmtClass: 636 return cast<SwitchCase>(Parent)->getSubStmt() == S; 637 default: 638 return false; 639 } 640 } 641 642 class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> { 643 const Stmt *Target; 644 645 public: 646 bool VisitStmt(Stmt *S) { return S != Target; } 647 648 /// Returns true if the given statement is present in the given declaration. 649 static bool isContained(const Stmt *Target, const Decl *D) { 650 StmtUSEFinder Visitor; 651 Visitor.Target = Target; 652 return !Visitor.TraverseDecl(const_cast<Decl *>(D)); 653 } 654 }; 655 656 /// Traverses the AST and finds the last statement that used a given 657 /// declaration. 658 class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> { 659 const Decl *D; 660 661 public: 662 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 663 if (DRE->getDecl() == D) 664 return false; 665 return true; 666 } 667 668 static const Stmt *findLastStmtThatUsesDecl(const Decl *D, 669 const CompoundStmt *Scope) { 670 LastDeclUSEFinder Visitor; 671 Visitor.D = D; 672 for (const Stmt *S : llvm::reverse(Scope->body())) { 673 if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) 674 return S; 675 } 676 return nullptr; 677 } 678 }; 679 680 /// This class implements -Wunguarded-availability. 681 /// 682 /// This is done with a traversal of the AST of a function that makes reference 683 /// to a partially available declaration. Whenever we encounter an \c if of the 684 /// form: \c if(@available(...)), we use the version from the condition to visit 685 /// the then statement. 686 class DiagnoseUnguardedAvailability 687 : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> { 688 typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base; 689 690 Sema &SemaRef; 691 Decl *Ctx; 692 693 /// Stack of potentially nested 'if (@available(...))'s. 694 SmallVector<VersionTuple, 8> AvailabilityStack; 695 SmallVector<const Stmt *, 16> StmtStack; 696 697 void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, 698 ObjCInterfaceDecl *ClassReceiver = nullptr); 699 700 public: 701 DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) 702 : SemaRef(SemaRef), Ctx(Ctx) { 703 AvailabilityStack.push_back( 704 SemaRef.Context.getTargetInfo().getPlatformMinVersion()); 705 } 706 707 bool TraverseStmt(Stmt *S) { 708 if (!S) 709 return true; 710 StmtStack.push_back(S); 711 bool Result = Base::TraverseStmt(S); 712 StmtStack.pop_back(); 713 return Result; 714 } 715 716 void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } 717 718 bool TraverseIfStmt(IfStmt *If); 719 720 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead 721 // to any useful diagnostics. 722 bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } 723 724 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; } 725 726 bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { 727 if (ObjCMethodDecl *D = Msg->getMethodDecl()) { 728 ObjCInterfaceDecl *ID = nullptr; 729 QualType ReceiverTy = Msg->getClassReceiver(); 730 if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) 731 ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); 732 733 DiagnoseDeclAvailability( 734 D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); 735 } 736 return true; 737 } 738 739 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 740 DiagnoseDeclAvailability(DRE->getDecl(), 741 SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); 742 return true; 743 } 744 745 bool VisitMemberExpr(MemberExpr *ME) { 746 DiagnoseDeclAvailability(ME->getMemberDecl(), 747 SourceRange(ME->getBeginLoc(), ME->getEndLoc())); 748 return true; 749 } 750 751 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { 752 SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) 753 << (!SemaRef.getLangOpts().ObjC); 754 return true; 755 } 756 757 bool VisitTypeLoc(TypeLoc Ty); 758 }; 759 760 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( 761 NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { 762 AvailabilityResult Result; 763 const NamedDecl *OffendingDecl; 764 std::tie(Result, OffendingDecl) = 765 ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); 766 if (Result != AR_Available) { 767 // All other diagnostic kinds have already been handled in 768 // DiagnoseAvailabilityOfDecl. 769 if (Result != AR_NotYetIntroduced) 770 return; 771 772 const AvailabilityAttr *AA = 773 getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); 774 VersionTuple Introduced = AA->getIntroduced(); 775 776 if (AvailabilityStack.back() >= Introduced) 777 return; 778 779 // If the context of this function is less available than D, we should not 780 // emit a diagnostic. 781 if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx, 782 OffendingDecl)) 783 return; 784 785 // We would like to emit the diagnostic even if -Wunguarded-availability is 786 // not specified for deployment targets >= to iOS 11 or equivalent or 787 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 788 // later. 789 unsigned DiagKind = 790 shouldDiagnoseAvailabilityByDefault( 791 SemaRef.Context, 792 SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced) 793 ? diag::warn_unguarded_availability_new 794 : diag::warn_unguarded_availability; 795 796 std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( 797 SemaRef.getASTContext().getTargetInfo().getPlatformName())); 798 799 SemaRef.Diag(Range.getBegin(), DiagKind) 800 << Range << D << PlatformName << Introduced.getAsString(); 801 802 SemaRef.Diag(OffendingDecl->getLocation(), 803 diag::note_partial_availability_specified_here) 804 << OffendingDecl << PlatformName << Introduced.getAsString() 805 << SemaRef.Context.getTargetInfo() 806 .getPlatformMinVersion() 807 .getAsString(); 808 809 auto FixitDiag = 810 SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) 811 << Range << D 812 << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 813 : /*__builtin_available*/ 1); 814 815 // Find the statement which should be enclosed in the if @available check. 816 if (StmtStack.empty()) 817 return; 818 const Stmt *StmtOfUse = StmtStack.back(); 819 const CompoundStmt *Scope = nullptr; 820 for (const Stmt *S : llvm::reverse(StmtStack)) { 821 if (const auto *CS = dyn_cast<CompoundStmt>(S)) { 822 Scope = CS; 823 break; 824 } 825 if (isBodyLikeChildStmt(StmtOfUse, S)) { 826 // The declaration won't be seen outside of the statement, so we don't 827 // have to wrap the uses of any declared variables in if (@available). 828 // Therefore we can avoid setting Scope here. 829 break; 830 } 831 StmtOfUse = S; 832 } 833 const Stmt *LastStmtOfUse = nullptr; 834 if (isa<DeclStmt>(StmtOfUse) && Scope) { 835 for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { 836 if (StmtUSEFinder::isContained(StmtStack.back(), D)) { 837 LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); 838 break; 839 } 840 } 841 } 842 843 const SourceManager &SM = SemaRef.getSourceManager(); 844 SourceLocation IfInsertionLoc = 845 SM.getExpansionLoc(StmtOfUse->getBeginLoc()); 846 SourceLocation StmtEndLoc = 847 SM.getExpansionRange( 848 (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) 849 .getEnd(); 850 if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) 851 return; 852 853 StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); 854 const char *ExtraIndentation = " "; 855 std::string FixItString; 856 llvm::raw_string_ostream FixItOS(FixItString); 857 FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" 858 : "__builtin_available") 859 << "(" 860 << AvailabilityAttr::getPlatformNameSourceSpelling( 861 SemaRef.getASTContext().getTargetInfo().getPlatformName()) 862 << " " << Introduced.getAsString() << ", *)) {\n" 863 << Indentation << ExtraIndentation; 864 FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); 865 SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( 866 StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), 867 /*SkipTrailingWhitespaceAndNewLine=*/false); 868 if (ElseInsertionLoc.isInvalid()) 869 ElseInsertionLoc = 870 Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); 871 FixItOS.str().clear(); 872 FixItOS << "\n" 873 << Indentation << "} else {\n" 874 << Indentation << ExtraIndentation 875 << "// Fallback on earlier versions\n" 876 << Indentation << "}"; 877 FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); 878 } 879 } 880 881 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { 882 const Type *TyPtr = Ty.getTypePtr(); 883 SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; 884 885 if (Range.isInvalid()) 886 return true; 887 888 if (const auto *TT = dyn_cast<TagType>(TyPtr)) { 889 TagDecl *TD = TT->getDecl(); 890 DiagnoseDeclAvailability(TD, Range); 891 892 } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { 893 TypedefNameDecl *D = TD->getDecl(); 894 DiagnoseDeclAvailability(D, Range); 895 896 } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { 897 if (NamedDecl *D = ObjCO->getInterface()) 898 DiagnoseDeclAvailability(D, Range); 899 } 900 901 return true; 902 } 903 904 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { 905 VersionTuple CondVersion; 906 if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) { 907 CondVersion = E->getVersion(); 908 909 // If we're using the '*' case here or if this check is redundant, then we 910 // use the enclosing version to check both branches. 911 if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) 912 return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); 913 } else { 914 // This isn't an availability checking 'if', we can just continue. 915 return Base::TraverseIfStmt(If); 916 } 917 918 AvailabilityStack.push_back(CondVersion); 919 bool ShouldContinue = TraverseStmt(If->getThen()); 920 AvailabilityStack.pop_back(); 921 922 return ShouldContinue && TraverseStmt(If->getElse()); 923 } 924 925 } // end anonymous namespace 926 927 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { 928 Stmt *Body = nullptr; 929 930 if (auto *FD = D->getAsFunction()) { 931 // FIXME: We only examine the pattern decl for availability violations now, 932 // but we should also examine instantiated templates. 933 if (FD->isTemplateInstantiation()) 934 return; 935 936 Body = FD->getBody(); 937 938 if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) 939 for (const CXXCtorInitializer *CI : CD->inits()) 940 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit()); 941 942 } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) 943 Body = MD->getBody(); 944 else if (auto *BD = dyn_cast<BlockDecl>(D)) 945 Body = BD->getBody(); 946 947 assert(Body && "Need a body here!"); 948 949 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); 950 } 951 952 FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { 953 if (FunctionScopes.empty()) 954 return nullptr; 955 956 // Conservatively search the entire current function scope context for 957 // availability violations. This ensures we always correctly analyze nested 958 // classes, blocks, lambdas, etc. that may or may not be inside if(@available) 959 // checks themselves. 960 return FunctionScopes.front(); 961 } 962 963 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, 964 ArrayRef<SourceLocation> Locs, 965 const ObjCInterfaceDecl *UnknownObjCClass, 966 bool ObjCPropertyAccess, 967 bool AvoidPartialAvailabilityChecks, 968 ObjCInterfaceDecl *ClassReceiver) { 969 std::string Message; 970 AvailabilityResult Result; 971 const NamedDecl* OffendingDecl; 972 // See if this declaration is unavailable, deprecated, or partial. 973 std::tie(Result, OffendingDecl) = 974 ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); 975 if (Result == AR_Available) 976 return; 977 978 if (Result == AR_NotYetIntroduced) { 979 if (AvoidPartialAvailabilityChecks) 980 return; 981 982 // We need to know the @available context in the current function to 983 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that 984 // when we're done parsing the current function. 985 if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { 986 Context->HasPotentialAvailabilityViolations = true; 987 return; 988 } 989 } 990 991 const ObjCPropertyDecl *ObjCPDecl = nullptr; 992 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 993 if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { 994 AvailabilityResult PDeclResult = PD->getAvailability(nullptr); 995 if (PDeclResult == Result) 996 ObjCPDecl = PD; 997 } 998 } 999 1000 EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, 1001 UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); 1002 } 1003