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