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