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