Path: blob/main/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp
35233 views
//===--- SemaAvailability.cpp - Availability attribute handling -----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file processes the availability attribute.9//10//===----------------------------------------------------------------------===//1112#include "clang/AST/Attr.h"13#include "clang/AST/Decl.h"14#include "clang/AST/DeclTemplate.h"15#include "clang/AST/RecursiveASTVisitor.h"16#include "clang/Basic/DiagnosticSema.h"17#include "clang/Basic/IdentifierTable.h"18#include "clang/Basic/LangOptions.h"19#include "clang/Basic/TargetInfo.h"20#include "clang/Lex/Preprocessor.h"21#include "clang/Sema/DelayedDiagnostic.h"22#include "clang/Sema/ScopeInfo.h"23#include "clang/Sema/Sema.h"24#include "clang/Sema/SemaObjC.h"25#include "llvm/ADT/StringRef.h"26#include <optional>2728using namespace clang;29using namespace sema;3031static bool hasMatchingEnvironmentOrNone(const ASTContext &Context,32const AvailabilityAttr *AA) {33IdentifierInfo *IIEnvironment = AA->getEnvironment();34auto Environment = Context.getTargetInfo().getTriple().getEnvironment();35if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment)36return true;3738llvm::Triple::EnvironmentType ET =39AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());40return Environment == ET;41}4243static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,44const Decl *D) {45AvailabilityAttr const *PartialMatch = nullptr;46// Check each AvailabilityAttr to find the one for this platform.47// For multiple attributes with the same platform try to find one for this48// environment.49// The attribute is always on the FunctionDecl, not on the50// FunctionTemplateDecl.51if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))52D = FTD->getTemplatedDecl();53for (const auto *A : D->attrs()) {54if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {55// FIXME: this is copied from CheckAvailability. We should try to56// de-duplicate.5758// Check if this is an App Extension "platform", and if so chop off59// the suffix for matching with the actual platform.60StringRef ActualPlatform = Avail->getPlatform()->getName();61StringRef RealizedPlatform = ActualPlatform;62if (Context.getLangOpts().AppExt) {63size_t suffix = RealizedPlatform.rfind("_app_extension");64if (suffix != StringRef::npos)65RealizedPlatform = RealizedPlatform.slice(0, suffix);66}6768StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();6970// Match the platform name.71if (RealizedPlatform == TargetPlatform) {72// Find the best matching attribute for this environment73if (hasMatchingEnvironmentOrNone(Context, Avail))74return Avail;75PartialMatch = Avail;76}77}78}79return PartialMatch;80}8182/// The diagnostic we should emit for \c D, and the declaration that83/// originated it, or \c AR_Available.84///85/// \param D The declaration to check.86/// \param Message If non-null, this will be populated with the message from87/// the availability attribute that is selected.88/// \param ClassReceiver If we're checking the method of a class message89/// send, the class. Otherwise nullptr.90static std::pair<AvailabilityResult, const NamedDecl *>91ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,92std::string *Message,93ObjCInterfaceDecl *ClassReceiver) {94AvailabilityResult Result = D->getAvailability(Message);9596// For typedefs, if the typedef declaration appears available look97// to the underlying type to see if it is more restrictive.98while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {99if (Result == AR_Available) {100if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {101D = TT->getDecl();102Result = D->getAvailability(Message);103continue;104}105}106break;107}108109// For alias templates, get the underlying declaration.110if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) {111D = ADecl->getTemplatedDecl();112Result = D->getAvailability(Message);113}114115// Forward class declarations get their attributes from their definition.116if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {117if (IDecl->getDefinition()) {118D = IDecl->getDefinition();119Result = D->getAvailability(Message);120}121}122123if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))124if (Result == AR_Available) {125const DeclContext *DC = ECD->getDeclContext();126if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {127Result = TheEnumDecl->getAvailability(Message);128D = TheEnumDecl;129}130}131132// For +new, infer availability from -init.133if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {134if (S.ObjC().NSAPIObj && ClassReceiver) {135ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(136S.ObjC().NSAPIObj->getInitSelector());137if (Init && Result == AR_Available && MD->isClassMethod() &&138MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() &&139MD->definedInNSObject(S.getASTContext())) {140Result = Init->getAvailability(Message);141D = Init;142}143}144}145146return {Result, D};147}148149150/// whether we should emit a diagnostic for \c K and \c DeclVersion in151/// the context of \c Ctx. For example, we should emit an unavailable diagnostic152/// in a deprecated context, but not the other way around.153static bool ShouldDiagnoseAvailabilityInContext(154Sema &S, AvailabilityResult K, VersionTuple DeclVersion,155const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {156assert(K != AR_Available && "Expected an unavailable declaration here!");157158// If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.159auto DeclLoc = Ctx->getBeginLoc();160// This is only a problem in Foundation's C++ implementation for CF_OPTIONS.161if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus &&162isa<TypedefDecl>(OffendingDecl)) {163StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc);164if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" ||165MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") {166return false;167}168}169170// In HLSL, skip emitting diagnostic if the diagnostic mode is not set to171// strict (-fhlsl-strict-availability), or if the target is library and the172// availability is restricted to a specific environment/shader stage.173// For libraries the availability will be checked later in174// DiagnoseHLSLAvailability class once where the specific environment/shader175// stage of the caller is known.176if (S.getLangOpts().HLSL) {177if (!S.getLangOpts().HLSLStrictAvailability ||178(DeclEnv != nullptr &&179S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==180llvm::Triple::EnvironmentType::Library))181return false;182}183184// Checks if we should emit the availability diagnostic in the context of C.185auto CheckContext = [&](const Decl *C) {186if (K == AR_NotYetIntroduced) {187if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))188if (AA->getIntroduced() >= DeclVersion &&189AA->getEnvironment() == DeclEnv)190return true;191} else if (K == AR_Deprecated) {192if (C->isDeprecated())193return true;194} else if (K == AR_Unavailable) {195// It is perfectly fine to refer to an 'unavailable' Objective-C method196// when it is referenced from within the @implementation itself. In this197// context, we interpret unavailable as a form of access control.198if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {199if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {200if (MD->getClassInterface() == Impl->getClassInterface())201return true;202}203}204}205206if (C->isUnavailable())207return true;208return false;209};210211do {212if (CheckContext(Ctx))213return false;214215// An implementation implicitly has the availability of the interface.216// Unless it is "+load" method.217if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))218if (MethodD->isClassMethod() &&219MethodD->getSelector().getAsString() == "load")220return true;221222if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {223if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())224if (CheckContext(Interface))225return false;226}227// A category implicitly has the availability of the interface.228else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))229if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())230if (CheckContext(Interface))231return false;232} while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));233234return true;235}236237static unsigned getAvailabilityDiagnosticKind(238const ASTContext &Context, const VersionTuple &DeploymentVersion,239const VersionTuple &DeclVersion, bool HasMatchingEnv) {240const auto &Triple = Context.getTargetInfo().getTriple();241VersionTuple ForceAvailabilityFromVersion;242switch (Triple.getOS()) {243// For iOS, emit the diagnostic even if -Wunguarded-availability is244// not specified for deployment targets >= to iOS 11 or equivalent or245// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or246// later.247case llvm::Triple::IOS:248case llvm::Triple::TvOS:249ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);250break;251case llvm::Triple::WatchOS:252ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);253break;254case llvm::Triple::Darwin:255case llvm::Triple::MacOSX:256ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);257break;258// For HLSL, use diagnostic from HLSLAvailability group which259// are reported as errors by default and in strict diagnostic mode260// (-fhlsl-strict-availability) and as warnings in relaxed diagnostic261// mode (-Wno-error=hlsl-availability)262case llvm::Triple::ShaderModel:263return HasMatchingEnv ? diag::warn_hlsl_availability264: diag::warn_hlsl_availability_unavailable;265default:266// New Apple targets should always warn about availability.267ForceAvailabilityFromVersion =268(Triple.getVendor() == llvm::Triple::Apple)269? VersionTuple(/*Major=*/0, 0)270: VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);271}272if (DeploymentVersion >= ForceAvailabilityFromVersion ||273DeclVersion >= ForceAvailabilityFromVersion)274return HasMatchingEnv ? diag::warn_unguarded_availability_new275: diag::warn_unguarded_availability_unavailable_new;276return HasMatchingEnv ? diag::warn_unguarded_availability277: diag::warn_unguarded_availability_unavailable;278}279280static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {281for (Decl *Ctx = OrigCtx; Ctx;282Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {283if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))284return cast<NamedDecl>(Ctx);285if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {286if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))287return Imp->getClassInterface();288return CD;289}290}291292return dyn_cast<NamedDecl>(OrigCtx);293}294295namespace {296297struct AttributeInsertion {298StringRef Prefix;299SourceLocation Loc;300StringRef Suffix;301302static AttributeInsertion createInsertionAfter(const NamedDecl *D) {303return {" ", D->getEndLoc(), ""};304}305static AttributeInsertion createInsertionAfter(SourceLocation Loc) {306return {" ", Loc, ""};307}308static AttributeInsertion createInsertionBefore(const NamedDecl *D) {309return {"", D->getBeginLoc(), "\n"};310}311};312313} // end anonymous namespace314315/// Tries to parse a string as ObjC method name.316///317/// \param Name The string to parse. Expected to originate from availability318/// attribute argument.319/// \param SlotNames The vector that will be populated with slot names. In case320/// of unsuccessful parsing can contain invalid data.321/// \returns A number of method parameters if parsing was successful,322/// std::nullopt otherwise.323static std::optional<unsigned>324tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,325const LangOptions &LangOpts) {326// Accept replacements starting with - or + as valid ObjC method names.327if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))328Name = Name.drop_front(1);329if (Name.empty())330return std::nullopt;331Name.split(SlotNames, ':');332unsigned NumParams;333if (Name.back() == ':') {334// Remove an empty string at the end that doesn't represent any slot.335SlotNames.pop_back();336NumParams = SlotNames.size();337} else {338if (SlotNames.size() != 1)339// Not a valid method name, just a colon-separated string.340return std::nullopt;341NumParams = 0;342}343// Verify all slot names are valid.344bool AllowDollar = LangOpts.DollarIdents;345for (StringRef S : SlotNames) {346if (S.empty())347continue;348if (!isValidAsciiIdentifier(S, AllowDollar))349return std::nullopt;350}351return NumParams;352}353354/// Returns a source location in which it's appropriate to insert a new355/// attribute for the given declaration \D.356static std::optional<AttributeInsertion>357createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,358const LangOptions &LangOpts) {359if (isa<ObjCPropertyDecl>(D))360return AttributeInsertion::createInsertionAfter(D);361if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {362if (MD->hasBody())363return std::nullopt;364return AttributeInsertion::createInsertionAfter(D);365}366if (const auto *TD = dyn_cast<TagDecl>(D)) {367SourceLocation Loc =368Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);369if (Loc.isInvalid())370return std::nullopt;371// Insert after the 'struct'/whatever keyword.372return AttributeInsertion::createInsertionAfter(Loc);373}374return AttributeInsertion::createInsertionBefore(D);375}376377/// Actually emit an availability diagnostic for a reference to an unavailable378/// decl.379///380/// \param Ctx The context that the reference occurred in381/// \param ReferringDecl The exact declaration that was referenced.382/// \param OffendingDecl A related decl to \c ReferringDecl that has an383/// availability attribute corresponding to \c K attached to it. Note that this384/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and385/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl386/// and OffendingDecl is the EnumDecl.387static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,388Decl *Ctx, const NamedDecl *ReferringDecl,389const NamedDecl *OffendingDecl,390StringRef Message,391ArrayRef<SourceLocation> Locs,392const ObjCInterfaceDecl *UnknownObjCClass,393const ObjCPropertyDecl *ObjCProperty,394bool ObjCPropertyAccess) {395// Diagnostics for deprecated or unavailable.396unsigned diag, diag_message, diag_fwdclass_message;397unsigned diag_available_here = diag::note_availability_specified_here;398SourceLocation NoteLocation = OffendingDecl->getLocation();399400// Matches 'diag::note_property_attribute' options.401unsigned property_note_select;402403// Matches diag::note_availability_specified_here.404unsigned available_here_select_kind;405406VersionTuple DeclVersion;407const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);408const IdentifierInfo *IIEnv = nullptr;409if (AA) {410DeclVersion = AA->getIntroduced();411IIEnv = AA->getEnvironment();412}413414if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,415OffendingDecl))416return;417418SourceLocation Loc = Locs.front();419420// The declaration can have multiple availability attributes, we are looking421// at one of them.422if (AA && AA->isInherited()) {423for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;424Redecl = Redecl->getPreviousDecl()) {425const AvailabilityAttr *AForRedecl =426getAttrForPlatform(S.Context, Redecl);427if (AForRedecl && !AForRedecl->isInherited()) {428// If D is a declaration with inherited attributes, the note should429// point to the declaration with actual attributes.430NoteLocation = Redecl->getLocation();431break;432}433}434}435436switch (K) {437case AR_NotYetIntroduced: {438// We would like to emit the diagnostic even if -Wunguarded-availability is439// not specified for deployment targets >= to iOS 11 or equivalent or440// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or441// later.442assert(AA != nullptr && "expecting valid availability attribute");443VersionTuple Introduced = AA->getIntroduced();444bool EnvironmentMatchesOrNone =445hasMatchingEnvironmentOrNone(S.getASTContext(), AA);446447const TargetInfo &TI = S.getASTContext().getTargetInfo();448std::string PlatformName(449AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));450llvm::StringRef TargetEnvironment(451llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));452llvm::StringRef AttrEnvironment =453AA->getEnvironment() ? AA->getEnvironment()->getName() : "";454bool UseEnvironment =455(!AttrEnvironment.empty() && !TargetEnvironment.empty());456457unsigned DiagKind = getAvailabilityDiagnosticKind(458S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),459Introduced, EnvironmentMatchesOrNone);460461S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName462<< Introduced.getAsString() << UseEnvironment463<< TargetEnvironment;464465S.Diag(OffendingDecl->getLocation(),466diag::note_partial_availability_specified_here)467<< OffendingDecl << PlatformName << Introduced.getAsString()468<< S.Context.getTargetInfo().getPlatformMinVersion().getAsString()469<< UseEnvironment << AttrEnvironment << TargetEnvironment;470471// Do not offer to silence the warning or fixits for HLSL472if (S.getLangOpts().HLSL)473return;474475if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {476if (const auto *TD = dyn_cast<TagDecl>(Enclosing))477if (TD->getDeclName().isEmpty()) {478S.Diag(TD->getLocation(),479diag::note_decl_unguarded_availability_silence)480<< /*Anonymous*/ 1 << TD->getKindName();481return;482}483auto FixitNoteDiag =484S.Diag(Enclosing->getLocation(),485diag::note_decl_unguarded_availability_silence)486<< /*Named*/ 0 << Enclosing;487// Don't offer a fixit for declarations with availability attributes.488if (Enclosing->hasAttr<AvailabilityAttr>())489return;490if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))491return;492std::optional<AttributeInsertion> Insertion = createAttributeInsertion(493Enclosing, S.getSourceManager(), S.getLangOpts());494if (!Insertion)495return;496std::string PlatformName =497AvailabilityAttr::getPlatformNameSourceSpelling(498S.getASTContext().getTargetInfo().getPlatformName())499.lower();500std::string Introduced =501OffendingDecl->getVersionIntroduced().getAsString();502FixitNoteDiag << FixItHint::CreateInsertion(503Insertion->Loc,504(llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +505"(" + Introduced + "))" + Insertion->Suffix)506.str());507}508return;509}510case AR_Deprecated:511diag = !ObjCPropertyAccess ? diag::warn_deprecated512: diag::warn_property_method_deprecated;513diag_message = diag::warn_deprecated_message;514diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;515property_note_select = /* deprecated */ 0;516available_here_select_kind = /* deprecated */ 2;517if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())518NoteLocation = AL->getLocation();519break;520521case AR_Unavailable:522diag = !ObjCPropertyAccess ? diag::err_unavailable523: diag::err_property_method_unavailable;524diag_message = diag::err_unavailable_message;525diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;526property_note_select = /* unavailable */ 1;527available_here_select_kind = /* unavailable */ 0;528529if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {530if (AL->isImplicit() && AL->getImplicitReason()) {531// Most of these failures are due to extra restrictions in ARC;532// reflect that in the primary diagnostic when applicable.533auto flagARCError = [&] {534if (S.getLangOpts().ObjCAutoRefCount &&535S.getSourceManager().isInSystemHeader(536OffendingDecl->getLocation()))537diag = diag::err_unavailable_in_arc;538};539540switch (AL->getImplicitReason()) {541case UnavailableAttr::IR_None: break;542543case UnavailableAttr::IR_ARCForbiddenType:544flagARCError();545diag_available_here = diag::note_arc_forbidden_type;546break;547548case UnavailableAttr::IR_ForbiddenWeak:549if (S.getLangOpts().ObjCWeakRuntime)550diag_available_here = diag::note_arc_weak_disabled;551else552diag_available_here = diag::note_arc_weak_no_runtime;553break;554555case UnavailableAttr::IR_ARCForbiddenConversion:556flagARCError();557diag_available_here = diag::note_performs_forbidden_arc_conversion;558break;559560case UnavailableAttr::IR_ARCInitReturnsUnrelated:561flagARCError();562diag_available_here = diag::note_arc_init_returns_unrelated;563break;564565case UnavailableAttr::IR_ARCFieldWithOwnership:566flagARCError();567diag_available_here = diag::note_arc_field_with_ownership;568break;569}570}571}572break;573574case AR_Available:575llvm_unreachable("Warning for availability of available declaration?");576}577578SmallVector<FixItHint, 12> FixIts;579if (K == AR_Deprecated) {580StringRef Replacement;581if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())582Replacement = AL->getReplacement();583if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))584Replacement = AL->getReplacement();585586CharSourceRange UseRange;587if (!Replacement.empty())588UseRange =589CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));590if (UseRange.isValid()) {591if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {592Selector Sel = MethodDecl->getSelector();593SmallVector<StringRef, 12> SelectorSlotNames;594std::optional<unsigned> NumParams = tryParseObjCMethodName(595Replacement, SelectorSlotNames, S.getLangOpts());596if (NumParams && *NumParams == Sel.getNumArgs()) {597assert(SelectorSlotNames.size() == Locs.size());598for (unsigned I = 0; I < Locs.size(); ++I) {599if (!Sel.getNameForSlot(I).empty()) {600CharSourceRange NameRange = CharSourceRange::getCharRange(601Locs[I], S.getLocForEndOfToken(Locs[I]));602FixIts.push_back(FixItHint::CreateReplacement(603NameRange, SelectorSlotNames[I]));604} else605FixIts.push_back(606FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));607}608} else609FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));610} else611FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));612}613}614615// We emit deprecation warning for deprecated specializations616// when their instantiation stacks originate outside617// of a system header, even if the diagnostics is suppresed at the618// point of definition.619SourceLocation InstantiationLoc =620S.getTopMostPointOfInstantiation(ReferringDecl);621bool ShouldAllowWarningInSystemHeader =622InstantiationLoc != Loc &&623!S.getSourceManager().isInSystemHeader(InstantiationLoc);624struct AllowWarningInSystemHeaders {625AllowWarningInSystemHeaders(DiagnosticsEngine &E,626bool AllowWarningInSystemHeaders)627: Engine(E), Prev(E.getSuppressSystemWarnings()) {628E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders);629}630~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); }631632private:633DiagnosticsEngine &Engine;634bool Prev;635} SystemWarningOverrideRAII(S.getDiagnostics(),636ShouldAllowWarningInSystemHeader);637638if (!Message.empty()) {639S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;640if (ObjCProperty)641S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)642<< ObjCProperty->getDeclName() << property_note_select;643} else if (!UnknownObjCClass) {644S.Diag(Loc, diag) << ReferringDecl << FixIts;645if (ObjCProperty)646S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)647<< ObjCProperty->getDeclName() << property_note_select;648} else {649S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;650S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);651}652653S.Diag(NoteLocation, diag_available_here)654<< OffendingDecl << available_here_select_kind;655}656657void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {658assert(DD.Kind == DelayedDiagnostic::Availability &&659"Expected an availability diagnostic here");660661DD.Triggered = true;662DoEmitAvailabilityWarning(663*this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),664DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),665DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),666DD.getObjCProperty(), false);667}668669static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,670const NamedDecl *ReferringDecl,671const NamedDecl *OffendingDecl,672StringRef Message,673ArrayRef<SourceLocation> Locs,674const ObjCInterfaceDecl *UnknownObjCClass,675const ObjCPropertyDecl *ObjCProperty,676bool ObjCPropertyAccess) {677// Delay if we're currently parsing a declaration.678if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {679S.DelayedDiagnostics.add(680DelayedDiagnostic::makeAvailability(681AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,682ObjCProperty, Message, ObjCPropertyAccess));683return;684}685686Decl *Ctx = cast<Decl>(S.getCurLexicalContext());687DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,688Message, Locs, UnknownObjCClass, ObjCProperty,689ObjCPropertyAccess);690}691692namespace {693694/// Returns true if the given statement can be a body-like child of \p Parent.695bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {696switch (Parent->getStmtClass()) {697case Stmt::IfStmtClass:698return cast<IfStmt>(Parent)->getThen() == S ||699cast<IfStmt>(Parent)->getElse() == S;700case Stmt::WhileStmtClass:701return cast<WhileStmt>(Parent)->getBody() == S;702case Stmt::DoStmtClass:703return cast<DoStmt>(Parent)->getBody() == S;704case Stmt::ForStmtClass:705return cast<ForStmt>(Parent)->getBody() == S;706case Stmt::CXXForRangeStmtClass:707return cast<CXXForRangeStmt>(Parent)->getBody() == S;708case Stmt::ObjCForCollectionStmtClass:709return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;710case Stmt::CaseStmtClass:711case Stmt::DefaultStmtClass:712return cast<SwitchCase>(Parent)->getSubStmt() == S;713default:714return false;715}716}717718class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> {719const Stmt *Target;720721public:722bool VisitStmt(Stmt *S) { return S != Target; }723724/// Returns true if the given statement is present in the given declaration.725static bool isContained(const Stmt *Target, const Decl *D) {726StmtUSEFinder Visitor;727Visitor.Target = Target;728return !Visitor.TraverseDecl(const_cast<Decl *>(D));729}730};731732/// Traverses the AST and finds the last statement that used a given733/// declaration.734class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> {735const Decl *D;736737public:738bool VisitDeclRefExpr(DeclRefExpr *DRE) {739if (DRE->getDecl() == D)740return false;741return true;742}743744static const Stmt *findLastStmtThatUsesDecl(const Decl *D,745const CompoundStmt *Scope) {746LastDeclUSEFinder Visitor;747Visitor.D = D;748for (const Stmt *S : llvm::reverse(Scope->body())) {749if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))750return S;751}752return nullptr;753}754};755756/// This class implements -Wunguarded-availability.757///758/// This is done with a traversal of the AST of a function that makes reference759/// to a partially available declaration. Whenever we encounter an \c if of the760/// form: \c if(@available(...)), we use the version from the condition to visit761/// the then statement.762class DiagnoseUnguardedAvailability763: public RecursiveASTVisitor<DiagnoseUnguardedAvailability> {764typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;765766Sema &SemaRef;767Decl *Ctx;768769/// Stack of potentially nested 'if (@available(...))'s.770SmallVector<VersionTuple, 8> AvailabilityStack;771SmallVector<const Stmt *, 16> StmtStack;772773void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,774ObjCInterfaceDecl *ClassReceiver = nullptr);775776public:777DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)778: SemaRef(SemaRef), Ctx(Ctx) {779AvailabilityStack.push_back(780SemaRef.Context.getTargetInfo().getPlatformMinVersion());781}782783bool TraverseStmt(Stmt *S) {784if (!S)785return true;786StmtStack.push_back(S);787bool Result = Base::TraverseStmt(S);788StmtStack.pop_back();789return Result;790}791792void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }793794bool TraverseIfStmt(IfStmt *If);795796// for 'case X:' statements, don't bother looking at the 'X'; it can't lead797// to any useful diagnostics.798bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }799800bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; }801802bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {803if (ObjCMethodDecl *D = Msg->getMethodDecl()) {804ObjCInterfaceDecl *ID = nullptr;805QualType ReceiverTy = Msg->getClassReceiver();806if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())807ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();808809DiagnoseDeclAvailability(810D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);811}812return true;813}814815bool VisitDeclRefExpr(DeclRefExpr *DRE) {816DiagnoseDeclAvailability(DRE->getDecl(),817SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));818return true;819}820821bool VisitMemberExpr(MemberExpr *ME) {822DiagnoseDeclAvailability(ME->getMemberDecl(),823SourceRange(ME->getBeginLoc(), ME->getEndLoc()));824return true;825}826827bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {828SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)829<< (!SemaRef.getLangOpts().ObjC);830return true;831}832833bool VisitTypeLoc(TypeLoc Ty);834};835836void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(837NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {838AvailabilityResult Result;839const NamedDecl *OffendingDecl;840std::tie(Result, OffendingDecl) =841ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);842if (Result != AR_Available) {843// All other diagnostic kinds have already been handled in844// DiagnoseAvailabilityOfDecl.845if (Result != AR_NotYetIntroduced)846return;847848const AvailabilityAttr *AA =849getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);850assert(AA != nullptr && "expecting valid availability attribute");851bool EnvironmentMatchesOrNone =852hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);853VersionTuple Introduced = AA->getIntroduced();854855if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)856return;857858// If the context of this function is less available than D, we should not859// emit a diagnostic.860if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,861AA->getEnvironment(), Ctx,862OffendingDecl))863return;864865const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();866std::string PlatformName(867AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));868llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());869llvm::StringRef AttrEnvironment =870AA->getEnvironment() ? AA->getEnvironment()->getName() : "";871bool UseEnvironment =872(!AttrEnvironment.empty() && !TargetEnvironment.empty());873874unsigned DiagKind = getAvailabilityDiagnosticKind(875SemaRef.Context,876SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,877EnvironmentMatchesOrNone);878879SemaRef.Diag(Range.getBegin(), DiagKind)880<< Range << D << PlatformName << Introduced.getAsString()881<< UseEnvironment << TargetEnvironment;882883SemaRef.Diag(OffendingDecl->getLocation(),884diag::note_partial_availability_specified_here)885<< OffendingDecl << PlatformName << Introduced.getAsString()886<< SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()887<< UseEnvironment << AttrEnvironment << TargetEnvironment;888889// Do not offer to silence the warning or fixits for HLSL890if (SemaRef.getLangOpts().HLSL)891return;892893auto FixitDiag =894SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)895<< Range << D896<< (SemaRef.getLangOpts().ObjC ? /*@available*/ 0897: /*__builtin_available*/ 1);898899// Find the statement which should be enclosed in the if @available check.900if (StmtStack.empty())901return;902const Stmt *StmtOfUse = StmtStack.back();903const CompoundStmt *Scope = nullptr;904for (const Stmt *S : llvm::reverse(StmtStack)) {905if (const auto *CS = dyn_cast<CompoundStmt>(S)) {906Scope = CS;907break;908}909if (isBodyLikeChildStmt(StmtOfUse, S)) {910// The declaration won't be seen outside of the statement, so we don't911// have to wrap the uses of any declared variables in if (@available).912// Therefore we can avoid setting Scope here.913break;914}915StmtOfUse = S;916}917const Stmt *LastStmtOfUse = nullptr;918if (isa<DeclStmt>(StmtOfUse) && Scope) {919for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {920if (StmtUSEFinder::isContained(StmtStack.back(), D)) {921LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);922break;923}924}925}926927const SourceManager &SM = SemaRef.getSourceManager();928SourceLocation IfInsertionLoc =929SM.getExpansionLoc(StmtOfUse->getBeginLoc());930SourceLocation StmtEndLoc =931SM.getExpansionRange(932(LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())933.getEnd();934if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))935return;936937StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);938const char *ExtraIndentation = " ";939std::string FixItString;940llvm::raw_string_ostream FixItOS(FixItString);941FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"942: "__builtin_available")943<< "("944<< AvailabilityAttr::getPlatformNameSourceSpelling(945SemaRef.getASTContext().getTargetInfo().getPlatformName())946<< " " << Introduced.getAsString() << ", *)) {\n"947<< Indentation << ExtraIndentation;948FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());949SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(950StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),951/*SkipTrailingWhitespaceAndNewLine=*/false);952if (ElseInsertionLoc.isInvalid())953ElseInsertionLoc =954Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());955FixItOS.str().clear();956FixItOS << "\n"957<< Indentation << "} else {\n"958<< Indentation << ExtraIndentation959<< "// Fallback on earlier versions\n"960<< Indentation << "}";961FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());962}963}964965bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {966const Type *TyPtr = Ty.getTypePtr();967SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};968969if (Range.isInvalid())970return true;971972if (const auto *TT = dyn_cast<TagType>(TyPtr)) {973TagDecl *TD = TT->getDecl();974DiagnoseDeclAvailability(TD, Range);975976} else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {977TypedefNameDecl *D = TD->getDecl();978DiagnoseDeclAvailability(D, Range);979980} else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {981if (NamedDecl *D = ObjCO->getInterface())982DiagnoseDeclAvailability(D, Range);983}984985return true;986}987988bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {989VersionTuple CondVersion;990if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {991CondVersion = E->getVersion();992993// If we're using the '*' case here or if this check is redundant, then we994// use the enclosing version to check both branches.995if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())996return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());997} else {998// This isn't an availability checking 'if', we can just continue.999return Base::TraverseIfStmt(If);1000}10011002AvailabilityStack.push_back(CondVersion);1003bool ShouldContinue = TraverseStmt(If->getThen());1004AvailabilityStack.pop_back();10051006return ShouldContinue && TraverseStmt(If->getElse());1007}10081009} // end anonymous namespace10101011void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {1012Stmt *Body = nullptr;10131014if (auto *FD = D->getAsFunction()) {1015Body = FD->getBody();10161017if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))1018for (const CXXCtorInitializer *CI : CD->inits())1019DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit());10201021} else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))1022Body = MD->getBody();1023else if (auto *BD = dyn_cast<BlockDecl>(D))1024Body = BD->getBody();10251026assert(Body && "Need a body here!");10271028DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);1029}10301031FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {1032if (FunctionScopes.empty())1033return nullptr;10341035// Conservatively search the entire current function scope context for1036// availability violations. This ensures we always correctly analyze nested1037// classes, blocks, lambdas, etc. that may or may not be inside if(@available)1038// checks themselves.1039return FunctionScopes.front();1040}10411042void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,1043ArrayRef<SourceLocation> Locs,1044const ObjCInterfaceDecl *UnknownObjCClass,1045bool ObjCPropertyAccess,1046bool AvoidPartialAvailabilityChecks,1047ObjCInterfaceDecl *ClassReceiver) {1048std::string Message;1049AvailabilityResult Result;1050const NamedDecl* OffendingDecl;1051// See if this declaration is unavailable, deprecated, or partial.1052std::tie(Result, OffendingDecl) =1053ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);1054if (Result == AR_Available)1055return;10561057if (Result == AR_NotYetIntroduced) {1058if (AvoidPartialAvailabilityChecks)1059return;10601061// We need to know the @available context in the current function to1062// diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that1063// when we're done parsing the current function.1064if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {1065Context->HasPotentialAvailabilityViolations = true;1066return;1067}1068}10691070const ObjCPropertyDecl *ObjCPDecl = nullptr;1071if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {1072if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {1073AvailabilityResult PDeclResult = PD->getAvailability(nullptr);1074if (PDeclResult == Result)1075ObjCPDecl = PD;1076}1077}10781079EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,1080UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);1081}108210831084