diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 2a674d375b5d94..f3a10c281f4248 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -250,7 +250,7 @@ SymbolTags computeSymbolTags(const NamedDecl &ND) { SymbolTags Result = 0; const auto IsDef = isUniqueDefinition(&ND); - if (ND.isDeprecated()) + if (ND.isDeprecatedInAnyTargetPlatform()) Result |= toSymbolTagBitmask(SymbolTag::Deprecated); if (isConst(&ND)) @@ -563,7 +563,7 @@ std::optional declToSym(ASTContext &Ctx, const NamedDecl &ND) { DocumentSymbol SI; SI.name = getSymbolName(Ctx, ND); SI.kind = SK; - SI.deprecated = ND.isDeprecated(); + SI.deprecated = ND.isDeprecatedInAnyTargetPlatform(); SI.range = Range{sourceLocToPosition(SM, SymbolRange->getBegin()), sourceLocToPosition(SM, SymbolRange->getEnd())}; SI.detail = getSymbolDetail(Ctx, ND); diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 86528d806eab34..bfc440302696ce 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1865,7 +1865,7 @@ static std::optional declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) { auto Result = declToHierarchyItem(ND, TUPath); if (Result) { - Result->deprecated = ND.isDeprecated(); + Result->deprecated = ND.isDeprecatedInAnyTargetPlatform(); // Compute the SymbolID and store it in the 'data' field. // This allows typeHierarchy/resolve to be used to // resolve children of items returned in a previous request @@ -1880,7 +1880,7 @@ declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) { auto Result = declToHierarchyItem(ND, TUPath); if (!Result) return Result; - if (ND.isDeprecated()) + if (ND.isDeprecatedInAnyTargetPlatform()) Result->tags.push_back(SymbolTag::Deprecated); if (auto ID = getSymbolID(&ND)) Result->data = ID.str(); diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 9d233be282dbb8..2cf8a792c9df85 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -758,6 +758,13 @@ class alignas(8) Decl { VersionTuple EnclosingVersion = VersionTuple(), StringRef *RealizedPlatform = nullptr) const; + /// Determine the availability of the given declaration for the given + /// target platform and its minimum version. + AvailabilityResult + getAvailability(StringRef Platform, const VersionTuple &PlatformMinVersion, + std::string *Message = nullptr, + StringRef *RealizedPlatform = nullptr) const; + /// Retrieve the version of the target platform in which this /// declaration was introduced. /// @@ -766,14 +773,13 @@ class alignas(8) Decl { /// attribute otherwise. VersionTuple getVersionIntroduced() const; - /// Determine whether this declaration is marked 'deprecated'. + /// \brief Determine whether this declaration is marked 'deprecated' in any + /// target platform that we're compiling for. /// /// \param Message If non-NULL and the declaration is deprecated, /// this will be set to the message describing why the declaration /// was deprecated (which may be empty). - bool isDeprecated(std::string *Message = nullptr) const { - return getAvailability(Message) == AR_Deprecated; - } + bool isDeprecatedInAnyTargetPlatform(std::string *Message = nullptr) const; /// Determine whether this declaration is marked 'unavailable'. /// @@ -784,6 +790,10 @@ class alignas(8) Decl { return getAvailability(Message) == AR_Unavailable; } + /// \brief Determine whether this declaration is marked 'deprecated' for + /// all target platforms that we're compiling for. + bool isUnavailabledForAllTargetPlatforms() const; + /// Determine whether this is a weak-imported symbol. /// /// Weak-imported symbols are typically marked with the diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index b424530c8447cf..39f22c961ce964 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -1736,13 +1736,18 @@ class ObjCAvailabilityCheckExpr : public Expr { friend class ASTStmtReader; VersionTuple VersionToCheck; + VersionTuple VariantVersionToCheck; SourceLocation AtLoc, RParen; public: - ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc, - SourceLocation RParen, QualType Ty) + ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, + VersionTuple VariantVersionToCheck, + SourceLocation AtLoc, SourceLocation RParen, + QualType Ty) : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary), - VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) { + VersionToCheck(VersionToCheck), + VariantVersionToCheck(VariantVersionToCheck), AtLoc(AtLoc), + RParen(RParen) { setDependence(ExprDependence::None); } @@ -1757,6 +1762,10 @@ class ObjCAvailabilityCheckExpr : public Expr { bool hasVersion() const { return !VersionToCheck.empty(); } VersionTuple getVersion() const { return VersionToCheck; } + bool hasVariantVersion() const { return !VariantVersionToCheck.empty(); } + + VersionTuple getVariantVersion() const { return VariantVersionToCheck; } + child_range children() { return child_range(child_iterator(), child_iterator()); } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cfb2ee33682013..c6b237c883a2b6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4300,6 +4300,9 @@ def warn_unguarded_availability_new : def warn_unguarded_availability_unavailable_new : Warning, InGroup; +def warn_zippered_unguarded_availability : + Warning<"%0 is only available on %1 %2 and %3 %4 or newer">, + InGroup; def note_decl_unguarded_availability_silence : Note< "annotate %select{%1|anonymous %1}0 with an availability attribute to silence this warning">; def note_unguarded_available_silence : Note< diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 5a541aaf797fe4..66a7ba2bd096f5 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -260,6 +260,8 @@ class TargetInfo : public TransferrableTargetInfo, mutable StringRef PlatformName; mutable VersionTuple PlatformMinVersion; + mutable StringRef TargetVariantPlatform; + mutable VersionTuple TargetVariantPlatformMinVersion; LLVM_PREFERRED_TYPE(bool) unsigned HasAlignMac68kSupport : 1; @@ -1737,6 +1739,28 @@ class TargetInfo : public TransferrableTargetInfo, /// which the program should be compiled. VersionTuple getPlatformMinVersion() const { return PlatformMinVersion; } + /// Returns true when are building for an auxillary target variant platform. + bool hasTargetVariantPlatform() const { + return !TargetVariantPlatform.empty(); + } + + /// Retrieve the name of the target variant platform as it is used in the + /// availability attribute. + StringRef getTargetVariantPlatform() const { + assert(hasTargetVariantPlatform() && + "no target variant platform specified"); + return TargetVariantPlatform; + } + + /// Retrieve the minimum desired deployment version of the target variant + /// platform, + /// for which the program should be compiled. + VersionTuple getTargetVariantPlatformMinVersion() const { + assert(hasTargetVariantPlatform() && + "no target variant platform specified"); + return TargetVariantPlatformMinVersion; + } + bool isBigEndian() const { return BigEndian; } bool isLittleEndian() const { return !BigEndian; } diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h index 0105089a393f17..5e45c3459f1513 100644 --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -134,14 +134,13 @@ class DelayedDiagnostic { void Destroy(); - static DelayedDiagnostic makeAvailability(AvailabilityResult AR, - ArrayRef Locs, - const NamedDecl *ReferringDecl, - const NamedDecl *OffendingDecl, - const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, - StringRef Msg, - bool ObjCPropertyAccess); + static DelayedDiagnostic + makeAvailability(AvailabilityResult AR, ArrayRef Locs, + const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, + const ObjCInterfaceDecl *UnknownObjCClass, + const ObjCPropertyDecl *ObjCProperty, StringRef Msg, + bool ObjCPropertyAccess, bool IsTargetVariantPlatform); static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { @@ -232,6 +231,10 @@ class DelayedDiagnostic { return AvailabilityData.ObjCPropertyAccess; } + bool isTargetVariantPlatform() const { + return AvailabilityData.IsTargetVariantPlatform; + } + private: struct AD { const NamedDecl *ReferringDecl; @@ -244,6 +247,7 @@ class DelayedDiagnostic { size_t NumSelectorLocs; AvailabilityResult AR; bool ObjCPropertyAccess; + bool IsTargetVariantPlatform; }; struct FTD { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index dcf112fd8eaa45..42858681fba88a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2466,6 +2466,11 @@ class Sema final : public SemaBase { void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx); + void + handleZipperedDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, + sema::DelayedDiagnostic &VariantDD, + Decl *Ctx); + /// Retrieve the current function, if any, that should be analyzed for /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); @@ -2479,7 +2484,9 @@ class Sema final : public SemaBase { void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs); std::pair - ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, + ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, StringRef Platform, + const VersionTuple &PlatformVersion, + std::string *Message, ObjCInterfaceDecl *ClassReceiver); ///@} diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 70f61fa57a682a..aff04541c67812 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -674,15 +674,12 @@ static StringRef getRealizedPlatform(const AvailabilityAttr *A, static AvailabilityResult CheckAvailability(ASTContext &Context, const AvailabilityAttr *A, std::string *Message, + StringRef TargetPlatform, VersionTuple EnclosingVersion) { - if (EnclosingVersion.empty()) - EnclosingVersion = Context.getTargetInfo().getPlatformMinVersion(); - if (EnclosingVersion.empty()) return AR_Available; StringRef ActualPlatform = A->getPlatform()->getName(); - StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); // Match the platform name. if (getRealizedPlatform(A, Context) != TargetPlatform) @@ -779,9 +776,20 @@ static AvailabilityResult CheckAvailability(ASTContext &Context, AvailabilityResult Decl::getAvailability(std::string *Message, VersionTuple EnclosingVersion, StringRef *RealizedPlatform) const { + const TargetInfo &TI = getASTContext().getTargetInfo(); + return getAvailability(TI.getPlatformName(), + EnclosingVersion.empty() ? TI.getPlatformMinVersion() + : EnclosingVersion, + Message, RealizedPlatform); +} + +AvailabilityResult Decl::getAvailability(StringRef Platform, + const VersionTuple &PlatformMinVersion, + std::string *Message, + StringRef *RealizedPlatform) const { if (auto *FTD = dyn_cast(this)) - return FTD->getTemplatedDecl()->getAvailability(Message, EnclosingVersion, - RealizedPlatform); + return FTD->getTemplatedDecl()->getAvailability( + Platform, PlatformMinVersion, Message, RealizedPlatform); AvailabilityResult Result = AR_Available; std::string ResultMessage; @@ -806,8 +814,8 @@ AvailabilityResult Decl::getAvailability(std::string *Message, if (const auto *Availability = dyn_cast(A)) { Availability = Availability->getEffectiveAttr(); - AvailabilityResult AR = CheckAvailability(getASTContext(), Availability, - Message, EnclosingVersion); + AvailabilityResult AR = CheckAvailability( + getASTContext(), Availability, Message, Platform, PlatformMinVersion); if (AR == AR_Unavailable) { if (RealizedPlatform) @@ -844,6 +852,26 @@ VersionTuple Decl::getVersionIntroduced() const { return {}; } +bool Decl::isDeprecatedInAnyTargetPlatform(std::string *Message) const { + const TargetInfo &TI = getASTContext().getTargetInfo(); + return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) == + AR_Deprecated || + (TI.hasTargetVariantPlatform() && + getAvailability(TI.getTargetVariantPlatform(), + TI.getTargetVariantPlatformMinVersion()) == + AR_Deprecated); +} + +bool Decl::isUnavailabledForAllTargetPlatforms() const { + const TargetInfo &TI = getASTContext().getTargetInfo(); + return getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()) == + AR_Unavailable && + (!getASTContext().getTargetInfo().hasTargetVariantPlatform() || + getAvailability(TI.getTargetVariantPlatform(), + TI.getTargetVariantPlatformMinVersion()) == + AR_Unavailable); +} + bool Decl::canBeWeakImported(bool &IsDefinition) const { IsDefinition = false; @@ -878,6 +906,7 @@ bool Decl::isWeakImported() const { if (!canBeWeakImported(IsDefinition)) return false; + const TargetInfo &TI = getASTContext().getTargetInfo(); for (const auto *A : getMostRecentDecl()->attrs()) { if (isa(A)) return true; @@ -885,7 +914,13 @@ bool Decl::isWeakImported() const { if (const auto *Availability = dyn_cast(A)) { Availability = Availability->getEffectiveAttr(); if (CheckAvailability(getASTContext(), Availability, nullptr, - VersionTuple()) == AR_NotYetIntroduced) + TI.getPlatformName(), TI.getPlatformMinVersion()) == + AR_NotYetIntroduced || + (TI.hasTargetVariantPlatform() && + CheckAvailability(getASTContext(), Availability, nullptr, + TI.getTargetVariantPlatform(), + TI.getTargetVariantPlatformMinVersion()) == + AR_NotYetIntroduced)) return true; } } diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index 8d6e8d6990e806..0cd4b705bbcb96 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -1558,5 +1558,7 @@ DarwinARMTargetInfo::DarwinARMTargetInfo(const llvm::Triple &Triple, void DarwinARMTargetInfo::getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion); + getDarwinDefines(Builder, Opts, Triple, /*TargetVariantTriple=*/"", + PlatformName, PlatformMinVersion, TargetVariantPlatform, + TargetVariantPlatformMinVersion); } diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index 04d9e13d3c9a6b..91f98595dbbf58 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -55,8 +55,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts, } void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, - const llvm::Triple &Triple, StringRef &PlatformName, - VersionTuple &PlatformMinVersion) { + const llvm::Triple &Triple, StringRef TargetVariantTriple, + StringRef &PlatformName, VersionTuple &PlatformMinVersion, + StringRef &TargetVariantPlatformName, + VersionTuple &TargetVariantPlatformMinVersion) { getAppleMachODefines(Builder, Opts, Triple); // Darwin's libc doesn't have threads.h @@ -67,6 +69,14 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, if (Triple.isMacOSX()) { Triple.getMacOSXVersion(OsVersion); PlatformName = "macos"; + if (!TargetVariantTriple.empty()) { + llvm::Triple TVT(TargetVariantTriple); + if (TVT.getOS() == llvm::Triple::IOS && + TVT.getEnvironment() == llvm::Triple::MacABI) { + TargetVariantPlatformName = "maccatalyst"; + TargetVariantPlatformMinVersion = TVT.getiOSVersion(); + } + } } else { OsVersion = Triple.getOSVersion(); PlatformName = llvm::Triple::getOSTypeName(Triple.getOS()); diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h index fb5c17cefc004e..724406f82b7256 100644 --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/clang/lib/Basic/Targets/OSTargets.h @@ -38,8 +38,10 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts, const llvm::Triple &Triple); void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, - const llvm::Triple &Triple, StringRef &PlatformName, - VersionTuple &PlatformMinVersion); + const llvm::Triple &Triple, StringRef TargetVariantTriple, + StringRef &PlatformName, VersionTuple &PlatformMinVersion, + StringRef &TargetVariantPlatformName, + VersionTuple &TargetVariantPlatform); template class LLVM_LIBRARY_VISIBILITY AppleMachOTargetInfo @@ -77,8 +79,10 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo protected: void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const override { - getDarwinDefines(Builder, Opts, Triple, this->PlatformName, - this->PlatformMinVersion); + getDarwinDefines(Builder, Opts, Triple, TargetVariantTriple, + this->PlatformName, this->PlatformMinVersion, + this->TargetVariantPlatform, + this->TargetVariantPlatformMinVersion); } public: @@ -87,6 +91,7 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo // By default, no TLS, and we list permitted architecture/OS // combinations. this->TLSSupported = false; + TargetVariantTriple = Opts.DarwinTargetVariantTriple; if (Triple.isMacOSX()) this->TLSSupported = !Triple.isMacOSXVersionLT(10, 7); @@ -185,6 +190,14 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo bool areDefaultedSMFStillPOD(const LangOptions &) const override { return false; } + + /// Darwin does not support protected visibility. Darwin's "default" + /// is very similar to ELF's "protected"; Darwin requires a "weak" + /// attribute on declarations that can be dynamically replaced. + bool hasProtectedVisibility() const override { return false; } + +private: + std::string TargetVariantTriple; }; // DragonFlyBSD Target diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 173931213ce68b..5be65e13a34f92 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -635,13 +635,20 @@ class ScalarExprEmitter Value *VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { VersionTuple Version = E->getVersion(); + VersionTuple VariantVersion = E->getVariantVersion(); // If we're checking for a platform older than our minimum deployment // target, we can fold the check away. - if (Version <= CGF.CGM.getTarget().getPlatformMinVersion()) + if ((Version.empty() && VariantVersion.empty()) || + (!Version.empty() && + Version <= CGF.CGM.getTarget().getPlatformMinVersion() && + (!CGF.CGM.getTarget().hasTargetVariantPlatform() || + (!VariantVersion.empty() && + VariantVersion <= + CGF.CGM.getTarget().getTargetVariantPlatformMinVersion())))) return llvm::ConstantInt::get(Builder.getInt1Ty(), 1); - return CGF.EmitBuiltinAvailable(Version); + return CGF.EmitBuiltinAvailable(Version, VariantVersion); } Value *VisitArraySubscriptExpr(ArraySubscriptExpr *E); diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index b9cbf593fb1c4b..73b1cdba095148 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -4049,9 +4049,11 @@ static llvm::Value *emitIsPlatformVersionAtLeast(CodeGenFunction &CGF, } llvm::Value * -CodeGenFunction::EmitBuiltinAvailable(const VersionTuple &Version) { +CodeGenFunction::EmitBuiltinAvailable(const VersionTuple &Version, + const VersionTuple &VariantVersion) { // Darwin uses the new __isPlatformVersionAtLeast family of routines. - if (CGM.getTarget().getTriple().isOSDarwin()) + if (CGM.getTarget().getTriple().isOSDarwin() && + !CGM.getTarget().hasTargetVariantPlatform()) return emitIsPlatformVersionAtLeast(*this, Version); if (!CGM.IsOSVersionAtLeastFn) { @@ -4059,19 +4061,50 @@ CodeGenFunction::EmitBuiltinAvailable(const VersionTuple &Version) { llvm::FunctionType::get(Int32Ty, {Int32Ty, Int32Ty, Int32Ty}, false); CGM.IsOSVersionAtLeastFn = CGM.CreateRuntimeFunction(FTy, "__isOSVersionAtLeast"); + llvm::FunctionType *FTy2 = llvm::FunctionType::get(Int32Ty, {}, false); + CGM.IsTargetPlatformNativeFn = + CGM.CreateRuntimeFunction(FTy2, "__isTargetPlatformNative"); + CGM.IsTargetVariantOSVersionAtLeastFn = + CGM.CreateRuntimeFunction(FTy, "__isTargetVariantOSVersionAtLeast"); } - std::optional Min = Version.getMinor(), - SMin = Version.getSubminor(); - llvm::Value *Args[] = { - llvm::ConstantInt::get(CGM.Int32Ty, Version.getMajor()), - llvm::ConstantInt::get(CGM.Int32Ty, Min.value_or(0)), - llvm::ConstantInt::get(CGM.Int32Ty, SMin.value_or(0))}; + auto EmitVersionArgs = [&](const VersionTuple &V, + llvm::SmallVectorImpl &Args) { + if (V.empty()) + return; + std::optional Min = V.getMinor(), SMin = V.getSubminor(); + Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, V.getMajor())); + Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, Min.value_or(0))); + Args.push_back(llvm::ConstantInt::get(CGM.Int32Ty, SMin.value_or(0))); + }; - llvm::Value *CallRes = - EmitNounwindRuntimeCall(CGM.IsOSVersionAtLeastFn, Args); + llvm::SmallVector Args; + llvm::SmallVector VariantArgs; + EmitVersionArgs(Version, Args); + EmitVersionArgs(VariantVersion, VariantArgs); + + llvm::Value *Check = nullptr; + if (!Args.empty()) { + Check = EmitNounwindRuntimeCall(CGM.IsOSVersionAtLeastFn, Args); + } + llvm::Value *VariantCheck = nullptr; + if (!VariantArgs.empty()) { + VariantCheck = EmitNounwindRuntimeCall( + CGM.IsTargetVariantOSVersionAtLeastFn, VariantArgs); + } + llvm::Value *IsNativeCheck = nullptr; + if (CGM.getTarget().hasTargetVariantPlatform()) { + IsNativeCheck = EmitNounwindRuntimeCall(CGM.IsTargetPlatformNativeFn); + IsNativeCheck = Builder.CreateICmpNE(IsNativeCheck, + llvm::Constant::getNullValue(Int32Ty)); + if (!Check) + Check = llvm::Constant::getNullValue(Int32Ty); + if (!VariantCheck) + VariantCheck = llvm::Constant::getNullValue(Int32Ty); + Check = Builder.CreateSelect(IsNativeCheck, Check, VariantCheck); + } - return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty)); + return Builder.CreateICmpNE(Check, llvm::Constant::getNullValue(Int32Ty)); } static bool isFoundationNeededForDarwinAvailabilityCheck( diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 7bdc79d86ea0a7..3bf62755ac2700 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5028,7 +5028,8 @@ class CodeGenFunction : public CodeGenTypeCache { enum class MSVCIntrin; llvm::Value *EmitMSVCBuiltinExpr(MSVCIntrin BuiltinID, const CallExpr *E); - llvm::Value *EmitBuiltinAvailable(const VersionTuple &Version); + llvm::Value *EmitBuiltinAvailable(const VersionTuple &Version, + const VersionTuple &VariantVersion); llvm::Value *EmitObjCProtocolExpr(const ObjCProtocolExpr *E); llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 1f5ecf734c5288..ffca028f657090 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -832,6 +832,8 @@ class CodeGenModule : public CodeGenTypeCache { llvm::FunctionCallee IsOSVersionAtLeastFn = nullptr; // i32 @__isPlatformVersionAtLeast(i32, i32, i32, i32) llvm::FunctionCallee IsPlatformVersionAtLeastFn = nullptr; + llvm::FunctionCallee IsTargetPlatformNativeFn = nullptr; + llvm::FunctionCallee IsTargetVariantOSVersionAtLeastFn = nullptr; InstrProfStats &getPGOStats() { return PGOStats; } llvm::IndexedInstrProfReader *getPGOReader() const { return PGOReader.get(); } diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index e8d4660fd36b22..fb3d1fef764b01 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -163,7 +163,7 @@ static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { - if (!MD->isUnavailable()) + if (!MD->isUnavailabledForAllTargetPlatforms()) return true; } return false; diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp index 50a552272f421d..750a0f00ecff9a 100644 --- a/clang/lib/Sema/CodeCompleteConsumer.cpp +++ b/clang/lib/Sema/CodeCompleteConsumer.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/StringExtras.h" @@ -765,9 +766,16 @@ void PrintingCodeCompleteConsumer::ProcessOverloadCandidates( /// Retrieve the effective availability of the given declaration. static AvailabilityResult getDeclAvailability(const Decl *D) { - AvailabilityResult AR = D->getAvailability(); + // FIXME: Take min of target and target-variant AR for better Xcode + // experience? + const TargetInfo &TI = + D->getTranslationUnitDecl()->getASTContext().getTargetInfo(); + AvailabilityResult AR = + D->getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion()); if (isa(D)) - AR = std::max(AR, cast(D->getDeclContext())->getAvailability()); + AR = std::max(AR, cast(D->getDeclContext()) + ->getAvailability(TI.getPlatformName(), + TI.getPlatformMinVersion())); return AR; } diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp index cb2721b92090ec..e825abaa5bc719 100644 --- a/clang/lib/Sema/DelayedDiagnostic.cpp +++ b/clang/lib/Sema/DelayedDiagnostic.cpp @@ -20,15 +20,12 @@ using namespace clang; using namespace sema; -DelayedDiagnostic -DelayedDiagnostic::makeAvailability(AvailabilityResult AR, - ArrayRef Locs, - const NamedDecl *ReferringDecl, - const NamedDecl *OffendingDecl, - const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, - StringRef Msg, - bool ObjCPropertyAccess) { +DelayedDiagnostic DelayedDiagnostic::makeAvailability( + AvailabilityResult AR, ArrayRef Locs, + const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl, + const ObjCInterfaceDecl *UnknownObjCClass, + const ObjCPropertyDecl *ObjCProperty, StringRef Msg, + bool ObjCPropertyAccess, bool IsTargetVariantPlatform) { assert(!Locs.empty()); DelayedDiagnostic DD; DD.Kind = Availability; @@ -53,6 +50,7 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR, DD.AvailabilityData.AR = AR; DD.AvailabilityData.ObjCPropertyAccess = ObjCPropertyAccess; + DD.AvailabilityData.IsTargetVariantPlatform = IsTargetVariantPlatform; return DD; } diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index 28a4b760dbd4d4..2c736015c20ca5 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -44,6 +44,7 @@ static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, } static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, + StringRef TargetPlatform, const Decl *D) { AvailabilityAttr const *PartialMatch = nullptr; // Check each AvailabilityAttr to find the one for this platform. @@ -73,8 +74,6 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, RealizedPlatform = RealizedPlatform.slice(0, suffix); } - StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); - // Match the platform name. if (RealizedPlatform == TargetPlatform) { // Find the best matching attribute for this environment @@ -96,9 +95,12 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, /// \param ClassReceiver If we're checking the method of a class message /// send, the class. Otherwise nullptr. std::pair -Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, +Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, StringRef Platform, + const VersionTuple &PlatformVersion, + std::string *Message, ObjCInterfaceDecl *ClassReceiver) { - AvailabilityResult Result = D->getAvailability(Message); + AvailabilityResult Result = + D->getAvailability(Platform, PlatformVersion, Message); // For typedefs, if the typedef declaration appears available look // to the underlying type to see if it is more restrictive. @@ -121,7 +123,9 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, T = NextT; continue; } - Result = D->getAvailability(Message); + /* TO_UPSTREAM(iosmac) ON*/ + Result = D->getAvailability(Platform, PlatformVersion, Message); + /* TO_UPSTREAM(iosmac) OFF*/ break; } } @@ -130,7 +134,7 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, if (const auto *IDecl = dyn_cast(D)) { if (IDecl->getDefinition()) { D = IDecl->getDefinition(); - Result = D->getAvailability(Message); + Result = D->getAvailability(Platform, PlatformVersion, Message); } } @@ -138,7 +142,8 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, if (Result == AR_Available) { const DeclContext *DC = ECD->getDeclContext(); if (const auto *TheEnumDecl = dyn_cast(DC)) { - Result = TheEnumDecl->getAvailability(Message); + Result = + TheEnumDecl->getAvailability(Platform, PlatformVersion, Message); D = TheEnumDecl; } } @@ -151,7 +156,7 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, if (Init && Result == AR_Available && MD->isClassMethod() && MD->getSelector() == ObjC().NSAPIObj->getNewSelector() && MD->definedInNSObject(getASTContext())) { - Result = Init->getAvailability(Message); + Result = Init->getAvailability(Platform, PlatformVersion, Message); D = Init; } } @@ -165,7 +170,8 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, /// in a deprecated context, but not the other way around. static bool ShouldDiagnoseAvailabilityInContext( Sema &S, AvailabilityResult K, VersionTuple DeclVersion, - const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { + const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl, + StringRef TargetPlatform, const VersionTuple &TargetPlatformMinVersion) { assert(K != AR_Available && "Expected an unavailable declaration here!"); // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. @@ -198,19 +204,21 @@ static bool ShouldDiagnoseAvailabilityInContext( if (K == AR_Deprecated) { if (const auto *VD = dyn_cast(OffendingDecl)) - if (VD->isLocalVarDeclOrParm() && VD->isDeprecated()) + if (VD->isLocalVarDeclOrParm() && VD->isDeprecatedInAnyTargetPlatform()) return true; } // Checks if we should emit the availability diagnostic in the context of C. auto CheckContext = [&](const Decl *C) { if (K == AR_NotYetIntroduced) { - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) + if (const AvailabilityAttr *AA = + getAttrForPlatform(S.Context, TargetPlatform, C)) if (AA->getEffectiveIntroduced() >= DeclVersion && AA->getEffectiveEnvironment() == DeclEnv) return true; } else if (K == AR_Deprecated) { - if (C->isDeprecated()) + if (C->getAvailability(TargetPlatform, TargetPlatformMinVersion) == + AR_Deprecated) return true; } else if (K == AR_Unavailable) { // It is perfectly fine to refer to an 'unavailable' Objective-C method @@ -224,7 +232,8 @@ static bool ShouldDiagnoseAvailabilityInContext( } } - if (C->isUnavailable()) + if (C->getAvailability(TargetPlatform, TargetPlatformMinVersion) == + AR_Unavailable) return true; return false; }; @@ -257,8 +266,12 @@ static bool ShouldDiagnoseAvailabilityInContext( static unsigned getAvailabilityDiagnosticKind( const ASTContext &Context, const VersionTuple &DeploymentVersion, - const VersionTuple &DeclVersion, bool HasMatchingEnv) { - const auto &Triple = Context.getTargetInfo().getTriple(); + const VersionTuple &DeclVersion, bool HasMatchingEnv, + bool IsTargetVariantCheck = false) { + const auto &Triple = + IsTargetVariantCheck + ? *Context.getTargetInfo().getDarwinTargetVariantTriple() + : Context.getTargetInfo().getTriple(); VersionTuple ForceAvailabilityFromVersion; switch (Triple.getOS()) { // For iOS, emit the diagnostic even if -Wunguarded-availability is @@ -395,129 +408,198 @@ createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, return AttributeInsertion::createInsertionBefore(D); } -/// Actually emit an availability diagnostic for a reference to an unavailable -/// decl. -/// -/// \param Ctx The context that the reference occurred in -/// \param ReferringDecl The exact declaration that was referenced. -/// \param OffendingDecl A related decl to \c ReferringDecl that has an -/// availability attribute corresponding to \c K attached to it. Note that this -/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and -/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl -/// and OffendingDecl is the EnumDecl. -static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, - Decl *Ctx, const NamedDecl *ReferringDecl, - const NamedDecl *OffendingDecl, - StringRef Message, - ArrayRef Locs, - const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, - bool ObjCPropertyAccess) { - // Diagnostics for deprecated or unavailable. - unsigned diag, diag_message, diag_fwdclass_message; - unsigned diag_available_here = diag::note_availability_specified_here; - SourceLocation NoteLocation = OffendingDecl->getLocation(); +/// Target-independent information about an availability diagnostic. +struct PlatformAgnosticAvailabilityDiagInfo { + const NamedDecl *ReferringDecl; + ArrayRef Locs; + const ObjCInterfaceDecl *UnknownObjCClass; + bool ObjCPropertyAccess; + + PlatformAgnosticAvailabilityDiagInfo( + const NamedDecl *ReferringDecl, ArrayRef Locs, + const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess) + : ReferringDecl(ReferringDecl), Locs(Locs), + UnknownObjCClass(UnknownObjCClass), + ObjCPropertyAccess(ObjCPropertyAccess) {} +}; - // Matches 'diag::note_property_attribute' options. - unsigned property_note_select; +// Target-specific information about an availability diagnostic. +struct PlatformSpecificAvailabilityDiag { + AvailabilityResult AR; + const NamedDecl *OffendingDecl; + std::string Message; + const ObjCPropertyDecl *ObjCProperty; + bool IsTargetVariantPlatform; + + PlatformSpecificAvailabilityDiag(AvailabilityResult AR, + const NamedDecl *OffendingDecl, + std::string Message, + const ObjCPropertyDecl *ObjCProperty, + bool IsTargetVariantPlatform) + : AR(AR), OffendingDecl(OffendingDecl), Message(std::move(Message)), + ObjCProperty(ObjCProperty), + IsTargetVariantPlatform(IsTargetVariantPlatform) {} + + PlatformSpecificAvailabilityDiag(AvailabilityResult AR, + const NamedDecl *OffendingDecl, + bool IsTargetVariantPlatform) + : AR(AR), OffendingDecl(OffendingDecl), ObjCProperty(nullptr), + IsTargetVariantPlatform(IsTargetVariantPlatform) {} + + /// Returns the location of the note which points to the availability + /// attribute. + SourceLocation getNoteLocation(Sema &S) const { + const TargetInfo &TI = S.getASTContext().getTargetInfo(); + StringRef Platform = IsTargetVariantPlatform ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + // The declaration can have multiple availability attributes, we are looking + // at one of them. + const AvailabilityAttr *A = + getAttrForPlatform(S.Context, Platform, OffendingDecl); + if (A && A->isInherited()) { + for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; + Redecl = Redecl->getPreviousDecl()) { + const AvailabilityAttr *AForRedecl = + getAttrForPlatform(S.Context, Platform, Redecl); + if (AForRedecl && !AForRedecl->isInherited()) { + // If D is a declaration with inherited attributes, the note should + // point to the declaration with actual attributes. + return Redecl->getLocation(); + } + } + } + return OffendingDecl->getLocation(); + } +}; - // Matches diag::note_availability_specified_here. - unsigned available_here_select_kind; +// Check if we have to emit an availability diagnostic for a particular target. +static bool ShouldEmitAvailabilityWarning( + Sema &S, Decl *Ctx, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo) { + const TargetInfo &TI = S.getASTContext().getTargetInfo(); + StringRef Platform = PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + VersionTuple PlatformMinVersion = + PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion(); VersionTuple DeclVersion; - const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); const IdentifierInfo *IIEnv = nullptr; - if (AA) { + if (const AvailabilityAttr *AA = + getAttrForPlatform(S.Context, Platform, PlatformInfo.OffendingDecl)) { DeclVersion = AA->getEffectiveIntroduced(); IIEnv = AA->getEffectiveEnvironment(); } - if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx, - OffendingDecl)) - return; - - SourceLocation Loc = Locs.front(); - - // The declaration can have multiple availability attributes, we are looking - // at one of them. - if (AA && AA->isInherited()) { - for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; - Redecl = Redecl->getPreviousDecl()) { - const AvailabilityAttr *AForRedecl = - getAttrForPlatform(S.Context, Redecl); - if (AForRedecl && !AForRedecl->isInherited()) { - // If D is a declaration with inherited attributes, the note should - // point to the declaration with actual attributes. - NoteLocation = Redecl->getLocation(); - break; - } - } - } + return ShouldDiagnoseAvailabilityInContext( + S, PlatformInfo.AR, DeclVersion, IIEnv, Ctx, PlatformInfo.OffendingDecl, + Platform, PlatformMinVersion); +} - switch (K) { - case AR_NotYetIntroduced: { +/// Emits an availability diagnostic for a not yet introduced declaration. +static void EmitNotIntroducedAvailabilityWarning( + Sema &S, Decl *Ctx, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const PlatformSpecificAvailabilityDiag *VariantPlatformInfo = nullptr) { + assert(PlatformInfo.AR == AR_NotYetIntroduced && "unexpected AR"); + const TargetInfo &TI = S.Context.getTargetInfo(); + StringRef TargetPlatform = PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + const AvailabilityAttr *AA = getAttrForPlatform( + S.getASTContext(), TargetPlatform, PlatformInfo.OffendingDecl); + assert(AA != nullptr && "expecting valid availability attribute"); + VersionTuple Introduced = AA->getEffectiveIntroduced(); + bool EnvironmentMatchesOrNone = + hasMatchingEnvironmentOrNone(S.getASTContext(), AA->getEffectiveAttr()); + + std::string PlatformName( + AvailabilityAttr::getPrettyPlatformName(TargetPlatform)); + llvm::StringRef TargetEnvironment( + llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); + llvm::StringRef AttrEnvironment = + AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; + bool UseEnvironment = + (!AttrEnvironment.empty() && !TargetEnvironment.empty()); + unsigned DiagKind; + if (VariantPlatformInfo) { + DiagKind = diag::warn_zippered_unguarded_availability; + } else { // We would like to emit the diagnostic even if -Wunguarded-availability is // not specified for deployment targets >= to iOS 11 or equivalent or // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or // later. - assert(AA != nullptr && "expecting valid availability attribute"); - VersionTuple Introduced = AA->getEffectiveIntroduced(); - bool EnvironmentMatchesOrNone = - hasMatchingEnvironmentOrNone(S.getASTContext(), AA->getEffectiveAttr()); - - const TargetInfo &TI = S.getASTContext().getTargetInfo(); - std::string PlatformName( - AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); - llvm::StringRef TargetEnvironment( - llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); - llvm::StringRef AttrEnvironment = - AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; - bool UseEnvironment = - (!AttrEnvironment.empty() && !TargetEnvironment.empty()); - - unsigned DiagKind = getAvailabilityDiagnosticKind( + // FIXME: Should we look at the target-variant platform here too + // potentially? + DiagKind = getAvailabilityDiagnosticKind( S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), Introduced, EnvironmentMatchesOrNone); + } - S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName - << Introduced.getAsString() << UseEnvironment - << TargetEnvironment; + VersionTuple VariantIntroduced; + if (VariantPlatformInfo) + VariantIntroduced = + getAttrForPlatform(S.getASTContext(), TI.getTargetVariantPlatform(), + VariantPlatformInfo->OffendingDecl) + ->getIntroduced(); + { + auto Diag = S.Diag(Info.Locs[0], DiagKind); + Diag << Info.ReferringDecl << PlatformName << Introduced.getAsString(); + if (!VariantPlatformInfo) + Diag << UseEnvironment << TargetEnvironment; + else + Diag << AvailabilityAttr::getPrettyPlatformName( + TI.getTargetVariantPlatform()) + << VariantIntroduced.getAsString(); + } - S.Diag(OffendingDecl->getLocation(), + S.Diag(PlatformInfo.OffendingDecl->getLocation(), + diag::note_partial_availability_specified_here) + << PlatformInfo.OffendingDecl << PlatformName << Introduced.getAsString() + << (PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion()) + .getAsString() + << UseEnvironment << AttrEnvironment << TargetEnvironment; + if (VariantPlatformInfo && + VariantPlatformInfo->OffendingDecl != PlatformInfo.OffendingDecl) + S.Diag(VariantPlatformInfo->OffendingDecl->getLocation(), diag::note_partial_availability_specified_here) - << OffendingDecl << PlatformName << Introduced.getAsString() - << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() - << UseEnvironment << AttrEnvironment << TargetEnvironment; - - // Do not offer to silence the warning or fixits for HLSL - if (S.getLangOpts().HLSL) - return; + << VariantPlatformInfo->OffendingDecl + << AvailabilityAttr::getPrettyPlatformName( + TI.getTargetVariantPlatform()) + << VariantIntroduced.getAsString() + << TI.getTargetVariantPlatformMinVersion().getAsString(); + + // Do not offer to silence the warning or fixits for HLSL + if (S.getLangOpts().HLSL) + return; - if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { - if (const auto *TD = dyn_cast(Enclosing)) - if (TD->getDeclName().isEmpty()) { - S.Diag(TD->getLocation(), - diag::note_decl_unguarded_availability_silence) - << /*Anonymous*/ 1 << TD->getKindName(); - return; - } - auto FixitNoteDiag = - S.Diag(Enclosing->getLocation(), - diag::note_decl_unguarded_availability_silence) - << /*Named*/ 0 << Enclosing; - // Don't offer a fixit for declarations with availability attributes. - if (Enclosing->hasAttr()) - return; - Preprocessor &PP = S.getPreprocessor(); - if (!PP.isMacroDefined("API_AVAILABLE")) + if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { + if (const auto *TD = dyn_cast(Enclosing)) + if (TD->getDeclName().isEmpty()) { + S.Diag(TD->getLocation(), + diag::note_decl_unguarded_availability_silence) + << /*Anonymous*/ 1 << TD->getKindName(); return; - std::optional Insertion = createAttributeInsertion( - Enclosing, S.getSourceManager(), S.getLangOpts()); - if (!Insertion) - return; - StringRef PlatformName = - S.getASTContext().getTargetInfo().getPlatformName(); + } + auto FixitNoteDiag = S.Diag(Enclosing->getLocation(), + diag::note_decl_unguarded_availability_silence) + << /*Named*/ 0 << Enclosing; + // Don't offer a fixit for declarations with availability attributes. + if (Enclosing->hasAttr()) + return; + Preprocessor &PP = S.getPreprocessor(); + if (!PP.isMacroDefined("API_AVAILABLE")) + return; + std::optional Insertion = createAttributeInsertion( + Enclosing, S.getSourceManager(), S.getLangOpts()); + if (!Insertion) + return; + auto GetAvailablePlatform = [&](StringRef PlatformName) -> StringRef { // Apple's API_AVAILABLE macro expands roughly like this. // API_AVAILABLE(ios(17.0)) // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0))) @@ -536,60 +618,120 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, return PP.isMacroDefined((MacroPrefix + EquivalentPlatform).str()); }); if (AvailablePlatform == EquivalentPlatforms.end()) - return; - std::string Introduced = - OffendingDecl->getVersionIntroduced().getAsString(); - FixitNoteDiag << FixItHint::CreateInsertion( - Insertion->Loc, - (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + - *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix) - .str()); + return {}; + return *AvailablePlatform; + }; + + StringRef PlatformName = GetAvailablePlatform(TargetPlatform); + if (PlatformName.empty()) + return; + + std::string FixItText; + llvm::raw_string_ostream OS(FixItText); + OS << Insertion->Prefix << "API_AVAILABLE(" << PlatformName << '(' + << Introduced.getAsString() << ')'; + if (VariantPlatformInfo) { + StringRef VariantPlatformName = + GetAvailablePlatform(TI.getTargetVariantPlatform()); + if (!VariantPlatformName.empty()) + OS << ", " << VariantPlatformName << '(' + << VariantIntroduced.getAsString() << ')'; } - return; + OS << ')' << Insertion->Suffix; + FixitNoteDiag << FixItHint::CreateInsertion(Insertion->Loc, OS.str()); } - case AR_Deprecated: - // Suppress -Wdeprecated-declarations in implicit - // functions. - if (const auto *FD = dyn_cast_or_null(S.getCurFunctionDecl()); - FD && FD->isImplicit()) - return; +} - if (ObjCPropertyAccess) - diag = diag::warn_property_method_deprecated; - else if (S.currentEvaluationContext().IsCaseExpr) - diag = diag::warn_deprecated_switch_case; - else - diag = diag::warn_deprecated; - - diag_message = diag::warn_deprecated_message; - diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; - property_note_select = /* deprecated */ 0; - available_here_select_kind = /* deprecated */ 2; - if (const auto *AL = OffendingDecl->getAttr()) - NoteLocation = AL->getLocation(); - break; +static void EmitZipperedNotIntroducedAvailabilityWarning( + Sema &S, Decl *Ctx, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const PlatformSpecificAvailabilityDiag &VariantPlatformInfo) { + EmitNotIntroducedAvailabilityWarning(S, Ctx, Info, PlatformInfo, + &VariantPlatformInfo); +} + +// Information about the diagnostic for a 'deprecated' or 'unavailable'. +struct UnavailableDeprecatedAvailabilityDiag { + // Diagnostics for deprecated or unavailable. + unsigned diag, diag_message, diag_fwdclass_message; + unsigned diag_available_here = diag::note_availability_specified_here; + + // Matches 'diag::note_property_attribute' options. + unsigned property_note_select; + + // Matches diag::note_availability_specified_here. + unsigned available_here_select_kind; + + // An optional note location for the note. + SourceLocation NoteLocation; - case AR_Unavailable: - diag = !ObjCPropertyAccess ? diag::err_unavailable - : diag::err_property_method_unavailable; + CharSourceRange UseRange; + StringRef Replacement; + + UnavailableDeprecatedAvailabilityDiag( + Sema &S, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo) { + const TargetInfo &TI = S.getASTContext().getTargetInfo(); + StringRef TargetPlatform = PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + Replacement = ""; + UseRange = CharSourceRange(); + if (PlatformInfo.AR == AR_Deprecated) { + if (auto AL = PlatformInfo.OffendingDecl->getAttr()) + Replacement = AL->getReplacement(); + if (auto AL = getAttrForPlatform(S.Context, TargetPlatform, + PlatformInfo.OffendingDecl)) + Replacement = AL->getReplacement(); + + if (!Replacement.empty()) + UseRange = CharSourceRange::getCharRange( + Info.Locs[0], S.getLocForEndOfToken(Info.Locs[0])); + } + + if (PlatformInfo.AR == AR_Deprecated) { + if (Info.ObjCPropertyAccess) + diag = diag::warn_property_method_deprecated; + else if (S.currentEvaluationContext().IsCaseExpr) + diag = diag::warn_deprecated_switch_case; + else + diag = diag::warn_deprecated; + + diag_message = diag::warn_deprecated_message; + diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; + property_note_select = /* deprecated */ 0; + available_here_select_kind = /* deprecated */ 2; + if (const auto *AL = + PlatformInfo.OffendingDecl->getAttr()) + NoteLocation = AL->getLocation(); + else + NoteLocation = PlatformInfo.getNoteLocation(S); + return; + } + assert(PlatformInfo.AR == AR_Unavailable && "unexpected AR"); + + diag = !Info.ObjCPropertyAccess ? diag::err_unavailable + : diag::err_property_method_unavailable; diag_message = diag::err_unavailable_message; diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; property_note_select = /* unavailable */ 1; available_here_select_kind = /* unavailable */ 0; + NoteLocation = PlatformInfo.getNoteLocation(S); - if (auto AL = OffendingDecl->getAttr()) { + if (auto AL = PlatformInfo.OffendingDecl->getAttr()) { if (AL->isImplicit() && AL->getImplicitReason()) { // Most of these failures are due to extra restrictions in ARC; // reflect that in the primary diagnostic when applicable. auto flagARCError = [&] { if (S.getLangOpts().ObjCAutoRefCount && S.getSourceManager().isInSystemHeader( - OffendingDecl->getLocation())) + PlatformInfo.OffendingDecl->getLocation())) diag = diag::err_unavailable_in_arc; }; switch (AL->getImplicitReason()) { - case UnavailableAttr::IR_None: break; + case UnavailableAttr::IR_None: + break; case UnavailableAttr::IR_ARCForbiddenType: flagARCError(); @@ -620,55 +762,60 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } } } - break; + } - case AR_Available: - llvm_unreachable("Warning for availability of available declaration?"); + bool operator==(const UnavailableDeprecatedAvailabilityDiag &Other) { + return diag == Other.diag && diag_message == Other.diag_message && + diag_fwdclass_message == Other.diag_fwdclass_message && + diag_available_here == Other.diag_available_here; } +}; + +static void EmitUnavailableDeprecatedAvailabilityWarning( + Sema &S, StringRef Message, + const UnavailableDeprecatedAvailabilityDiag &Diag, + const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const UnavailableDeprecatedAvailabilityDiag *VariantDiag = nullptr, + const PlatformSpecificAvailabilityDiag *VariantPlatformInfo = nullptr) { + // Create the fix-it only when the replacement on both platforms matches. SmallVector FixIts; - if (K == AR_Deprecated) { - StringRef Replacement; - if (auto AL = OffendingDecl->getAttr()) - Replacement = AL->getReplacement(); - if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) - Replacement = AL->getReplacement(); - - CharSourceRange UseRange; - if (!Replacement.empty()) - UseRange = - CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); - if (UseRange.isValid()) { - if (const auto *MethodDecl = dyn_cast(ReferringDecl)) { - Selector Sel = MethodDecl->getSelector(); - SmallVector SelectorSlotNames; - std::optional NumParams = tryParseObjCMethodName( - Replacement, SelectorSlotNames, S.getLangOpts()); - if (NumParams && *NumParams == Sel.getNumArgs()) { - assert(SelectorSlotNames.size() == Locs.size()); - for (unsigned I = 0; I < Locs.size(); ++I) { - if (!Sel.getNameForSlot(I).empty()) { - CharSourceRange NameRange = CharSourceRange::getCharRange( - Locs[I], S.getLocForEndOfToken(Locs[I])); - FixIts.push_back(FixItHint::CreateReplacement( - NameRange, SelectorSlotNames[I])); - } else - FixIts.push_back( - FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I])); - } - } else - FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); + if (Diag.UseRange.isValid() && + (!VariantDiag || Diag.Replacement == VariantDiag->Replacement)) { + if (const auto *MethodDecl = dyn_cast(Info.ReferringDecl)) { + Selector Sel = MethodDecl->getSelector(); + SmallVector SelectorSlotNames; + std::optional NumParams = tryParseObjCMethodName( + Diag.Replacement, SelectorSlotNames, S.getLangOpts()); + if (NumParams && *NumParams == Sel.getNumArgs()) { + assert(SelectorSlotNames.size() == Info.Locs.size()); + for (unsigned I = 0; I < Info.Locs.size(); ++I) { + if (!Sel.getNameForSlot(I).empty()) { + CharSourceRange NameRange = CharSourceRange::getCharRange( + Info.Locs[I], S.getLocForEndOfToken(Info.Locs[I])); + FixIts.push_back( + FixItHint::CreateReplacement(NameRange, SelectorSlotNames[I])); + } else + FixIts.push_back( + FixItHint::CreateInsertion(Info.Locs[I], SelectorSlotNames[I])); + } } else - FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); - } + FixIts.push_back( + FixItHint::CreateReplacement(Diag.UseRange, Diag.Replacement)); + } else + FixIts.push_back( + FixItHint::CreateReplacement(Diag.UseRange, Diag.Replacement)); } + SourceLocation Loc = Info.Locs[0]; + // We emit deprecation warning for deprecated specializations // when their instantiation stacks originate outside // of a system header, even if the diagnostics is suppresed at the // point of definition. SourceLocation InstantiationLoc = - S.getTopMostPointOfInstantiation(ReferringDecl); + S.getTopMostPointOfInstantiation(Info.ReferringDecl); bool ShouldAllowWarningInSystemHeader = InstantiationLoc != Loc && !S.getSourceManager().isInSystemHeader(InstantiationLoc); @@ -687,23 +834,112 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } SystemWarningOverrideRAII(S.getDiagnostics(), ShouldAllowWarningInSystemHeader); + auto EmitObjCPropNote = [&]() { + if (PlatformInfo.ObjCProperty) + S.Diag(PlatformInfo.ObjCProperty->getLocation(), + diag::note_property_attribute) + << PlatformInfo.ObjCProperty->getDeclName() + << Diag.property_note_select; + if (VariantPlatformInfo && VariantPlatformInfo->ObjCProperty) + S.Diag(VariantPlatformInfo->ObjCProperty->getLocation(), + diag::note_property_attribute) + << VariantPlatformInfo->ObjCProperty->getDeclName() + << VariantDiag->property_note_select; + }; if (!Message.empty()) { - S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; - if (ObjCProperty) - S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) - << ObjCProperty->getDeclName() << property_note_select; - } else if (!UnknownObjCClass) { - S.Diag(Loc, diag) << ReferringDecl << FixIts; - if (ObjCProperty) - S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) - << ObjCProperty->getDeclName() << property_note_select; + S.Diag(Loc, Diag.diag_message) << Info.ReferringDecl << Message << FixIts; + EmitObjCPropNote(); + } else if (!Info.UnknownObjCClass) { + S.Diag(Loc, Diag.diag) << Info.ReferringDecl << FixIts; + EmitObjCPropNote(); } else { - S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; - S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); + S.Diag(Loc, Diag.diag_fwdclass_message) << Info.ReferringDecl << FixIts; + S.Diag(Info.UnknownObjCClass->getLocation(), diag::note_forward_class); } - S.Diag(NoteLocation, diag_available_here) - << OffendingDecl << available_here_select_kind; + S.Diag(Diag.NoteLocation, Diag.diag_available_here) + << PlatformInfo.OffendingDecl << Diag.available_here_select_kind; + if (VariantDiag && VariantDiag->NoteLocation != Diag.NoteLocation) + S.Diag(VariantDiag->NoteLocation, VariantDiag->diag_available_here) + << VariantPlatformInfo->OffendingDecl + << VariantDiag->available_here_select_kind; +} + +/// Actually emit an availability diagnostic for a reference to an unavailable +/// decl. +/// +/// \param Ctx The context that the reference occurred in +/// \param ReferringDecl The exact declaration that was referenced. +/// \param OffendingDecl A related decl to \c ReferringDecl that has an +/// availability attribute corresponding to \c K attached to it. Note that this +/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and +/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl +/// and OffendingDecl is the EnumDecl. +static void DoEmitAvailabilityWarning( + Sema &S, Decl *Ctx, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo) { + if (!ShouldEmitAvailabilityWarning(S, Ctx, Info, PlatformInfo)) + return; + + if (PlatformInfo.AR == AR_NotYetIntroduced) + return EmitNotIntroducedAvailabilityWarning(S, Ctx, Info, PlatformInfo); + assert(PlatformInfo.AR != AR_Available && + "expected an unavailable/deprecated AR"); + + if (PlatformInfo.AR == AR_Deprecated) + // Suppress -Wdeprecated-declarations in implicit + // functions. + if (const auto *FD = dyn_cast_or_null(S.getCurFunctionDecl()); + FD && FD->isImplicit()) + return; + + UnavailableDeprecatedAvailabilityDiag Diag(S, Info, PlatformInfo); + EmitUnavailableDeprecatedAvailabilityWarning(S, PlatformInfo.Message, Diag, + Info, PlatformInfo); +} + +static void DoEmitZipperedAvailabilityWarning( + Sema &S, Decl *Ctx, const PlatformAgnosticAvailabilityDiagInfo &Info, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const PlatformSpecificAvailabilityDiag &VariantPlatformInfo) { + assert(PlatformInfo.IsTargetVariantPlatform == false && + VariantPlatformInfo.IsTargetVariantPlatform == true && + "invalid zippered diag"); + bool EmitP1 = ShouldEmitAvailabilityWarning(S, Ctx, Info, PlatformInfo); + bool EmitP2 = + ShouldEmitAvailabilityWarning(S, Ctx, Info, VariantPlatformInfo); + if (!EmitP1 && !EmitP2) + return; + if (EmitP1 && EmitP2) { + // Check if we can merge the diagnostic into one. + if (PlatformInfo.AR == VariantPlatformInfo.AR) { + if (PlatformInfo.AR == AR_NotYetIntroduced) { + EmitZipperedNotIntroducedAvailabilityWarning(S, Ctx, Info, PlatformInfo, + VariantPlatformInfo); + return; + } + UnavailableDeprecatedAvailabilityDiag D1(S, Info, PlatformInfo); + UnavailableDeprecatedAvailabilityDiag D2(S, Info, VariantPlatformInfo); + if (D1 == D2) { + std::string Message; + llvm::raw_string_ostream OS(Message); + if (!PlatformInfo.Message.empty() && + !VariantPlatformInfo.Message.empty()) + OS << PlatformInfo.Message << " and " << VariantPlatformInfo.Message; + else if (!PlatformInfo.Message.empty()) + OS << PlatformInfo.Message; + else if (!VariantPlatformInfo.Message.empty()) + OS << VariantPlatformInfo.Message; + EmitUnavailableDeprecatedAvailabilityWarning( + S, OS.str(), D1, Info, PlatformInfo, &D2, &VariantPlatformInfo); + return; + } + } + } + if (EmitP1) + DoEmitAvailabilityWarning(S, Ctx, Info, PlatformInfo); + if (EmitP2) + DoEmitAvailabilityWarning(S, Ctx, Info, VariantPlatformInfo); } void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { @@ -711,34 +947,39 @@ void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { "Expected an availability diagnostic here"); DD.Triggered = true; - DoEmitAvailabilityWarning( - *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), - DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), - DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), - DD.getObjCProperty(), false); + PlatformAgnosticAvailabilityDiagInfo Info( + DD.getAvailabilityReferringDecl(), DD.getAvailabilitySelectorLocs(), + DD.getUnknownObjCClass(), DD.getObjCPropertyAccess()); + PlatformSpecificAvailabilityDiag PlatformInfo( + DD.getAvailabilityResult(), DD.getAvailabilityOffendingDecl(), + DD.getAvailabilityMessage().str(), DD.getObjCProperty(), + DD.isTargetVariantPlatform()); + DoEmitAvailabilityWarning(*this, Ctx, Info, PlatformInfo); } -static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, - const NamedDecl *ReferringDecl, - const NamedDecl *OffendingDecl, - StringRef Message, - ArrayRef Locs, - const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, - bool ObjCPropertyAccess) { - // Delay if we're currently parsing a declaration. - if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { - S.DelayedDiagnostics.add( - DelayedDiagnostic::makeAvailability( - AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, - ObjCProperty, Message, ObjCPropertyAccess)); - return; - } +void Sema::handleZipperedDelayedAvailabilityCheck(DelayedDiagnostic &DD, + DelayedDiagnostic &VariantDD, + Decl *Ctx) { + assert(DD.Kind == DelayedDiagnostic::Availability && + VariantDD.Kind == DelayedDiagnostic::Availability && + "Expected an availability diagnostic here"); - Decl *Ctx = cast(S.getCurLexicalContext()); - DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, - Message, Locs, UnknownObjCClass, ObjCProperty, - ObjCPropertyAccess); + DD.Triggered = true; + VariantDD.Triggered = true; + PlatformAgnosticAvailabilityDiagInfo Info( + DD.getAvailabilityReferringDecl(), DD.getAvailabilitySelectorLocs(), + DD.getUnknownObjCClass(), DD.getObjCPropertyAccess()); + PlatformSpecificAvailabilityDiag PlatformInfo( + DD.getAvailabilityResult(), DD.getAvailabilityOffendingDecl(), + DD.getAvailabilityMessage().str(), DD.getObjCProperty(), + DD.isTargetVariantPlatform()); + PlatformSpecificAvailabilityDiag VariantPlatformInfo( + VariantDD.getAvailabilityResult(), + VariantDD.getAvailabilityOffendingDecl(), + VariantDD.getAvailabilityMessage().str(), VariantDD.getObjCProperty(), + VariantDD.isTargetVariantPlatform()); + DoEmitZipperedAvailabilityWarning(*this, Ctx, Info, PlatformInfo, + VariantPlatformInfo); } namespace { @@ -816,17 +1057,58 @@ class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor { Decl *Ctx; /// Stack of potentially nested 'if (@available(...))'s. - SmallVector AvailabilityStack; + struct ZipperedVersionTuple { + std::optional Version; + std::optional VariantVersion; + + static ZipperedVersionTuple make(VersionTuple V) { + return {V, std::nullopt}; + } + static ZipperedVersionTuple makeVariant(VersionTuple V) { + return {std::nullopt, V}; + } + static ZipperedVersionTuple makeZippered(VersionTuple V, + VersionTuple VariantV) { + return {V, VariantV}; + } + }; + SmallVector AvailabilityStack; SmallVector StmtStack; + void EmitNotYetIntroducedDiagnostic( + NamedDecl *D, SourceRange Range, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const PlatformSpecificAvailabilityDiag *VariantPlatformInfo = nullptr); + void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ClassReceiver = nullptr); public: DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) : SemaRef(SemaRef), Ctx(Ctx) { + const TargetInfo &TI = SemaRef.Context.getTargetInfo(); AvailabilityStack.push_back( - SemaRef.Context.getTargetInfo().getPlatformMinVersion()); + TI.hasTargetVariantPlatform() + ? ZipperedVersionTuple::makeZippered( + TI.getPlatformMinVersion(), + TI.getTargetVariantPlatformMinVersion()) + : ZipperedVersionTuple::make(TI.getPlatformMinVersion())); + } + + // Returns the OS version for the native/variant platform that's guarded by + // the @available + // checks. + const VersionTuple &getGuardedVersion(bool IsTargetVariantPlatform) { + if (!SemaRef.Context.getTargetInfo().hasTargetVariantPlatform()) + return *AvailabilityStack.back().Version; + for (const auto &S : llvm::reverse(AvailabilityStack)) { + if (IsTargetVariantPlatform) { + if (S.VariantVersion) + return *S.VariantVersion; + } else if (S.Version) + return *S.Version; + } + llvm_unreachable("missing availability version guard"); } bool TraverseStmt(Stmt *S) override { @@ -882,143 +1164,226 @@ class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor { bool VisitTypeLoc(TypeLoc Ty) override; }; +void DiagnoseUnguardedAvailability::EmitNotYetIntroducedDiagnostic( + NamedDecl *D, SourceRange Range, + const PlatformSpecificAvailabilityDiag &PlatformInfo, + const PlatformSpecificAvailabilityDiag *VariantPlatformInfo) { + // We would like to emit the diagnostic even if -Wunguarded-availability is + // not specified for deployment targets >= to iOS 11 or equivalent or + // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or + // later. + const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); + StringRef PlatformNameStrRef = PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + auto *AA = getAttrForPlatform(SemaRef.getASTContext(), PlatformNameStrRef, + PlatformInfo.OffendingDecl); + VersionTuple Introduced = AA->getEffectiveIntroduced(); + + std::string PlatformName( + AvailabilityAttr::getPrettyPlatformName(PlatformNameStrRef)); + llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); + bool EnvironmentMatchesOrNone = hasMatchingEnvironmentOrNone( + SemaRef.getASTContext(), AA->getEffectiveAttr()); + llvm::StringRef AttrEnvironment = + AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; + bool UseEnvironment = + (!AttrEnvironment.empty() && !TargetEnvironment.empty()); + + unsigned DiagKind = + VariantPlatformInfo + ? diag::warn_zippered_unguarded_availability + : getAvailabilityDiagnosticKind( + SemaRef.Context, + PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion(), + Introduced, EnvironmentMatchesOrNone, + /*IsTargetVariantCheck=*/PlatformInfo.IsTargetVariantPlatform); + + VersionTuple VariantIntroduced; + if (VariantPlatformInfo) + VariantIntroduced = getAttrForPlatform(SemaRef.getASTContext(), + TI.getTargetVariantPlatform(), + VariantPlatformInfo->OffendingDecl) + ->getIntroduced(); + { + auto Diag = SemaRef.Diag(Range.getBegin(), DiagKind) + << Range << D << PlatformName << Introduced.getAsString(); + if (!VariantPlatformInfo) + Diag << UseEnvironment << TargetEnvironment; + else + Diag << AvailabilityAttr::getPrettyPlatformName( + TI.getTargetVariantPlatform()) + << VariantIntroduced.getAsString(); + } + + SemaRef.Diag(PlatformInfo.OffendingDecl->getLocation(), + diag::note_partial_availability_specified_here) + << PlatformInfo.OffendingDecl << PlatformName << Introduced.getAsString() + << (PlatformInfo.IsTargetVariantPlatform + ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion()) + .getAsString() + << UseEnvironment << AttrEnvironment << TargetEnvironment; + if (VariantPlatformInfo && + PlatformInfo.OffendingDecl != VariantPlatformInfo->OffendingDecl) + SemaRef.Diag(VariantPlatformInfo->OffendingDecl->getLocation(), + diag::note_partial_availability_specified_here) + << VariantPlatformInfo->OffendingDecl + << AvailabilityAttr::getPrettyPlatformName( + TI.getTargetVariantPlatform()) + << VariantIntroduced.getAsString() + << TI.getTargetVariantPlatformMinVersion().getAsString(); + + // Do not offer to silence the warning or fixits for HLSL + if (SemaRef.getLangOpts().HLSL) + return; + + auto FixitDiag = + SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) + << Range << D + << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 + : /*__builtin_available*/ 1); + + // Find the statement which should be enclosed in the if @available check. + if (StmtStack.empty()) + return; + const Stmt *StmtOfUse = StmtStack.back(); + const CompoundStmt *Scope = nullptr; + for (const Stmt *S : llvm::reverse(StmtStack)) { + if (const auto *CS = dyn_cast(S)) { + Scope = CS; + break; + } + if (isBodyLikeChildStmt(StmtOfUse, S)) { + // The declaration won't be seen outside of the statement, so we don't + // have to wrap the uses of any declared variables in if (@available). + // Therefore we can avoid setting Scope here. + break; + } + StmtOfUse = S; + } + const Stmt *LastStmtOfUse = nullptr; + if (isa(StmtOfUse) && Scope) { + for (const Decl *D : cast(StmtOfUse)->decls()) { + if (StmtUSEFinder::isContained(StmtStack.back(), D)) { + LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); + break; + } + } + } + + const SourceManager &SM = SemaRef.getSourceManager(); + SourceLocation IfInsertionLoc = SM.getExpansionLoc(StmtOfUse->getBeginLoc()); + SourceLocation StmtEndLoc = + SM.getExpansionRange( + (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) + .getEnd(); + if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) + return; + + StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); + const char *ExtraIndentation = " "; + std::string FixItString; + llvm::raw_string_ostream FixItOS(FixItString); + StringRef FixItPlatformName; + VersionTuple FixItVersion; + + if (AA->getInferredAttr()) { + FixItPlatformName = "anyAppleOS"; + FixItVersion = AA->getIntroduced(); + } else { + FixItPlatformName = + AvailabilityAttr::getPlatformNameSourceSpelling(PlatformName); + FixItVersion = Introduced; + } + FixItOS << "if (" + << (SemaRef.getLangOpts().ObjC ? "@available" : "__builtin_available") + << "(" << FixItPlatformName << " " << FixItVersion.getAsString(); + if (VariantPlatformInfo) + FixItOS << ", " + << AvailabilityAttr::getPlatformNameSourceSpelling( + TI.getTargetVariantPlatform()) + << " " << VariantIntroduced.getAsString(); + FixItOS << ", *)) {\n" << Indentation << ExtraIndentation; + FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); + SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( + StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (ElseInsertionLoc.isInvalid()) + ElseInsertionLoc = + Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); + FixItOS.str().clear(); + FixItOS << "\n" + << Indentation << "} else {\n" + << Indentation << ExtraIndentation + << "// Fallback on earlier versions\n" + << Indentation << "}"; + FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); +} + void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { - AvailabilityResult Result; - const NamedDecl *OffendingDecl; - std::tie(Result, OffendingDecl) = - SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, ReceiverClass); - if (Result != AR_Available) { + const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); + + auto CreatePlatformDiagInfo = [&](bool IsTargetVariantPlatform) + -> std::optional { + AvailabilityResult Result; + const NamedDecl *OffendingDecl; + StringRef PlatformName = IsTargetVariantPlatform + ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + const VersionTuple &PlatformMinVersion = + IsTargetVariantPlatform ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion(); + std::tie(Result, OffendingDecl) = SemaRef.ShouldDiagnoseAvailabilityOfDecl( + D, PlatformName, PlatformMinVersion, nullptr, ReceiverClass); // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. if (Result != AR_NotYetIntroduced) - return; + return std::nullopt; - const AvailabilityAttr *AA = - getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); + const AvailabilityAttr *AA = getAttrForPlatform( + SemaRef.getASTContext(), PlatformName, OffendingDecl); assert(AA != nullptr && "expecting valid availability attribute"); bool EnvironmentMatchesOrNone = hasMatchingEnvironmentOrNone( SemaRef.getASTContext(), AA->getEffectiveAttr()); VersionTuple Introduced = AA->getEffectiveIntroduced(); - if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) - return; + if (EnvironmentMatchesOrNone && + getGuardedVersion(IsTargetVariantPlatform) >= Introduced) + return std::nullopt; // If the context of this function is less available than D, we should not // emit a diagnostic. - if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, - AA->getEffectiveEnvironment(), Ctx, - OffendingDecl)) - return; - - const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); - std::string PlatformName( - AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); - llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); - llvm::StringRef AttrEnvironment = - AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; - bool UseEnvironment = - (!AttrEnvironment.empty() && !TargetEnvironment.empty()); - - unsigned DiagKind = getAvailabilityDiagnosticKind( - SemaRef.Context, - SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced, - EnvironmentMatchesOrNone); - - SemaRef.Diag(Range.getBegin(), DiagKind) - << Range << D << PlatformName << Introduced.getAsString() - << UseEnvironment << TargetEnvironment; - - SemaRef.Diag(OffendingDecl->getLocation(), - diag::note_partial_availability_specified_here) - << OffendingDecl << PlatformName << Introduced.getAsString() - << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() - << UseEnvironment << AttrEnvironment << TargetEnvironment; - - // Do not offer to silence the warning or fixits for HLSL - if (SemaRef.getLangOpts().HLSL) - return; - - auto FixitDiag = - SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) - << Range << D - << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 - : /*__builtin_available*/ 1); - - // Find the statement which should be enclosed in the if @available check. - if (StmtStack.empty()) - return; - const Stmt *StmtOfUse = StmtStack.back(); - const CompoundStmt *Scope = nullptr; - for (const Stmt *S : llvm::reverse(StmtStack)) { - if (const auto *CS = dyn_cast(S)) { - Scope = CS; - break; - } - if (isBodyLikeChildStmt(StmtOfUse, S)) { - // The declaration won't be seen outside of the statement, so we don't - // have to wrap the uses of any declared variables in if (@available). - // Therefore we can avoid setting Scope here. - break; - } - StmtOfUse = S; - } - const Stmt *LastStmtOfUse = nullptr; - if (isa(StmtOfUse) && Scope) { - for (const Decl *D : cast(StmtOfUse)->decls()) { - if (StmtUSEFinder::isContained(StmtStack.back(), D)) { - LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); - break; - } - } - } + if (!ShouldDiagnoseAvailabilityInContext( + SemaRef, Result, Introduced, AA->getEffectiveEnvironment(), Ctx, + OffendingDecl, PlatformName, PlatformMinVersion)) + return std::nullopt; + return PlatformSpecificAvailabilityDiag(Result, OffendingDecl, + IsTargetVariantPlatform); + }; - const SourceManager &SM = SemaRef.getSourceManager(); - SourceLocation IfInsertionLoc = - SM.getExpansionLoc(StmtOfUse->getBeginLoc()); - SourceLocation StmtEndLoc = - SM.getExpansionRange( - (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) - .getEnd(); - if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) - return; + std::optional PlatformInfo = + CreatePlatformDiagInfo(/*IsTargetVariantPlatform=*/false); + std::optional VariantPlatformInfo; + if (TI.hasTargetVariantPlatform()) + VariantPlatformInfo = + CreatePlatformDiagInfo(/*IsTargetVariantPlatform=*/true); + if (!PlatformInfo && !VariantPlatformInfo) + return; - StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); - const char *ExtraIndentation = " "; - std::string FixItString; - llvm::raw_string_ostream FixItOS(FixItString); - StringRef FixItPlatformName; - VersionTuple FixItVersion; - - if (AA->getInferredAttr()) { - FixItPlatformName = "anyAppleOS"; - FixItVersion = AA->getIntroduced(); - } else { - FixItPlatformName = AvailabilityAttr::getPlatformNameSourceSpelling( - SemaRef.getASTContext().getTargetInfo().getPlatformName()); - FixItVersion = AA->getEffectiveIntroduced(); - } - FixItOS << "if (" - << (SemaRef.getLangOpts().ObjC ? "@available" - : "__builtin_available") - << "(" << FixItPlatformName << " " << FixItVersion.getAsString() - << ", *)) {\n" - << Indentation << ExtraIndentation; - FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); - SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( - StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), - /*SkipTrailingWhitespaceAndNewLine=*/false); - if (ElseInsertionLoc.isInvalid()) - ElseInsertionLoc = - Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); - FixItOS.str().clear(); - FixItOS << "\n" - << Indentation << "} else {\n" - << Indentation << ExtraIndentation - << "// Fallback on earlier versions\n" - << Indentation << "}"; - FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); + if (PlatformInfo) { + if (VariantPlatformInfo) + EmitNotYetIntroducedDiagnostic(D, Range, *PlatformInfo, + &*VariantPlatformInfo); + else + EmitNotYetIntroducedDiagnostic(D, Range, *PlatformInfo); + return; } + if (VariantPlatformInfo) + EmitNotYetIntroducedDiagnostic(D, Range, *VariantPlatformInfo); } bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { @@ -1068,16 +1433,34 @@ ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { } bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { + VersionTuple CondVersion; + VersionTuple VariantCondVersion; + if (auto *E = dyn_cast(If->getCond())) { + CondVersion = E->getVersion(); + VariantCondVersion = E->getVariantVersion(); + + bool IsStar = CondVersion.empty() && VariantCondVersion.empty(); + bool IsCondRedundant = + CondVersion <= getGuardedVersion(/*IsTargetVariantPlatform=*/false); + bool IsVariantCondRedundant = + VariantCondVersion.empty() || + VariantCondVersion <= + getGuardedVersion(/*IsTargetVariantPlatform=*/true); + // If we're using the '*' case here or if this check is redundant, then we + // use the enclosing version to check both branches. + if (IsStar || (IsCondRedundant && IsVariantCondRedundant)) + return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); + } + ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); if (!IfCond.E) { // This isn't an availability checking 'if', we can just continue. return DynamicRecursiveASTVisitor::TraverseIfStmt(If); } - - VersionTuple CondVersion = IfCond.E->getVersion(); + CondVersion = IfCond.E->getVersion(); // If we're using the '*' case here or if this check is redundant, then we // use the enclosing version to check both branches. - if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { + if (CondVersion.empty() || CondVersion <= AvailabilityStack.back().Version) { return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); } @@ -1086,14 +1469,18 @@ bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { if (IfCond.isNegated) { std::swap(Guarded, Unguarded); } - - AvailabilityStack.push_back(CondVersion); + AvailabilityStack.push_back(ZipperedVersionTuple{ + CondVersion.empty() ? std::nullopt + : std::optional(CondVersion), + VariantCondVersion.empty() + ? std::nullopt + : std::optional(VariantCondVersion)}); bool ShouldContinue = TraverseStmt(Guarded); + AvailabilityStack.pop_back(); return ShouldContinue && TraverseStmt(Unguarded); } - } // end anonymous namespace void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { @@ -1133,40 +1520,91 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, bool ObjCPropertyAccess, bool AvoidPartialAvailabilityChecks, ObjCInterfaceDecl *ClassReceiver) { + auto CreateAvailabilityDiagnostic = [&](bool IsTargetVariantPlatform) + -> std::optional { + std::string Message; + AvailabilityResult Result; + const NamedDecl *OffendingDecl; + // See if this declaration is unavailable, deprecated, or partial. + const TargetInfo &TI = getASTContext().getTargetInfo(); + StringRef Platform = IsTargetVariantPlatform ? TI.getTargetVariantPlatform() + : TI.getPlatformName(); + VersionTuple VT = IsTargetVariantPlatform + ? TI.getTargetVariantPlatformMinVersion() + : TI.getPlatformMinVersion(); + std::tie(Result, OffendingDecl) = ShouldDiagnoseAvailabilityOfDecl( + D, Platform, VT, &Message, ClassReceiver); + if (Result == AR_Available) + return std::nullopt; - std::string Message; - AvailabilityResult Result; - const NamedDecl* OffendingDecl; - // See if this declaration is unavailable, deprecated, or partial. - std::tie(Result, OffendingDecl) = - ShouldDiagnoseAvailabilityOfDecl(D, &Message, ClassReceiver); - if (Result == AR_Available) - return; + if (Result == AR_NotYetIntroduced) { + if (AvoidPartialAvailabilityChecks) + return std::nullopt; - if (Result == AR_NotYetIntroduced) { - if (AvoidPartialAvailabilityChecks) - return; + // We need to know the @available context in the current function to + // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that + // when we're done parsing the current function. + if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { + Context->HasPotentialAvailabilityViolations = true; + return std::nullopt; + } + } - // We need to know the @available context in the current function to - // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that - // when we're done parsing the current function. - if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { - Context->HasPotentialAvailabilityViolations = true; - return; + const ObjCPropertyDecl *ObjCPDecl = nullptr; + if (const auto *MD = dyn_cast(D)) { + if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { + AvailabilityResult PDeclResult = + PD->getAvailability(Platform, VT, nullptr); + if (PDeclResult == Result) + ObjCPDecl = PD; + } } + + return PlatformSpecificAvailabilityDiag(Result, OffendingDecl, + std::move(Message), ObjCPDecl, + IsTargetVariantPlatform); + }; + std::optional TargetAvailabilityDiag = + CreateAvailabilityDiagnostic(/*IsTargetVariantPlatform=*/false); + std::optional TargetVariantAvailabilityDiag; + if (getASTContext().getTargetInfo().hasTargetVariantPlatform()) + TargetVariantAvailabilityDiag = + CreateAvailabilityDiagnostic(/*IsTargetVariantPlatform=*/true); + if (!TargetAvailabilityDiag && !TargetVariantAvailabilityDiag) + return; + + PlatformAgnosticAvailabilityDiagInfo Info(D, Locs, UnknownObjCClass, + ObjCPropertyAccess); + + // Delay if we're currently parsing a declaration. + if (DelayedDiagnostics.shouldDelayDiagnostics()) { + auto MakeDiag = [&](const PlatformSpecificAvailabilityDiag &PlatformInfo) + -> DelayedDiagnostic { + return DelayedDiagnostic::makeAvailability( + PlatformInfo.AR, Info.Locs, Info.ReferringDecl, + PlatformInfo.OffendingDecl, Info.UnknownObjCClass, + PlatformInfo.ObjCProperty, PlatformInfo.Message, + Info.ObjCPropertyAccess, PlatformInfo.IsTargetVariantPlatform); + }; + if (TargetAvailabilityDiag) + DelayedDiagnostics.add(MakeDiag(*TargetAvailabilityDiag)); + if (TargetVariantAvailabilityDiag) + DelayedDiagnostics.add(MakeDiag(*TargetVariantAvailabilityDiag)); + return; } - const ObjCPropertyDecl *ObjCPDecl = nullptr; - if (const auto *MD = dyn_cast(D)) { - if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { - AvailabilityResult PDeclResult = PD->getAvailability(nullptr); - if (PDeclResult == Result) - ObjCPDecl = PD; + Decl *Ctx = cast(getCurLexicalContext()); + if (TargetAvailabilityDiag) { + if (TargetVariantAvailabilityDiag) { + DoEmitZipperedAvailabilityWarning(*this, Ctx, Info, + *TargetAvailabilityDiag, + *TargetVariantAvailabilityDiag); + return; } + DoEmitAvailabilityWarning(*this, Ctx, Info, *TargetAvailabilityDiag); } - - EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, - UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); + if (TargetVariantAvailabilityDiag) + DoEmitAvailabilityWarning(*this, Ctx, Info, *TargetVariantAvailabilityDiag); } void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a99fcb56d11380..2181617de7a6fa 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17205,7 +17205,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation, }; // Don't issue this warning for unavailable inits or direct subclasses // of NSObject. - if (!MD->isUnavailable() && !superIsNSObject(MD)) { + if (!MD->isUnavailabledForAllTargetPlatforms() && + !superIsNSObject(MD)) { Diag(MD->getLocation(), diag::warn_objc_designated_init_missing_super_call); Diag(InitMethod->getLocation(), @@ -17215,7 +17216,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation, } if (FSI->ObjCWarnForNoInitDelegation) { // Don't issue this warning for unavailable inits. - if (!MD->isUnavailable()) + if (!MD->isUnavailabledForAllTargetPlatforms()) Diag(MD->getLocation(), diag::warn_objc_secondary_init_missing_init_call); FSI->ObjCWarnForNoInitDelegation = false; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a61fc54ade7578..d56f136889d2e5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3012,9 +3012,14 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (NewAttr) D->addAttr(NewAttr); } - } else if (S.Context.getTargetInfo().getTriple().getOS() == - llvm::Triple::IOS && - S.Context.getTargetInfo().getTriple().isMacCatalystEnvironment()) { + } else if ((S.Context.getTargetInfo().getTriple().getOS() == + llvm::Triple::IOS && + S.Context.getTargetInfo() + .getTriple() + .isMacCatalystEnvironment()) || + (S.Context.getTargetInfo().hasTargetVariantPlatform() && + S.Context.getTargetInfo().getTargetVariantPlatform() == + "maccatalyst")) { auto GetSDKInfo = [&]() { return S.getDarwinSDKInfoForAvailabilityChecking(AL.getRange().getBegin(), "macOS"); @@ -9015,8 +9020,20 @@ void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { case DelayedDiagnostic::Availability: // Don't bother giving deprecation/unavailable diagnostics if // the decl is invalid. - if (!decl->isInvalidDecl()) + if (!decl->isInvalidDecl()) { + if (!diag.isTargetVariantPlatform() && (i + 1) != pool->pool_end()) { + DelayedDiagnostic &next = const_cast(*(i + 1)); + if (!next.Triggered && next.isTargetVariantPlatform() && + diag.Loc == next.Loc && + diag.getAvailabilityReferringDecl() == + next.getAvailabilityReferringDecl()) { + handleZipperedDelayedAvailabilityCheck(diag, next, decl); + ++i; + break; + } + } handleDelayedAvailabilityCheck(diag, decl); + } break; case DelayedDiagnostic::Access: diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 04ba528098af48..51da6c624972a4 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -290,10 +290,13 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S, const NamedDecl *ND, if (!ND) return; bool IsCategory = false; + // FIXME: Implement support for TVT. + StringRef Platform = S.getASTContext().getTargetInfo().getPlatformName(); + VersionTuple PlatformVersionMin = + S.getASTContext().getTargetInfo().getPlatformMinVersion(); StringRef RealizedPlatform; AvailabilityResult Availability = ND->getAvailability( - /*Message=*/nullptr, /*EnclosingVersion=*/VersionTuple(), - &RealizedPlatform); + Platform, PlatformVersionMin, /*Message=*/nullptr, &RealizedPlatform); if (Availability != AR_Deprecated) { if (isa(ND)) { if (Availability != AR_Unavailable) @@ -310,7 +313,8 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S, const NamedDecl *ND, return; } if (const auto *CD = dyn_cast(ND)) { - if (!CD->getClassInterface()->isDeprecated()) + if (CD->getClassInterface()->getAvailability( + Platform, PlatformVersionMin) != AR_Deprecated) return; ND = CD->getClassInterface(); IsCategory = true; @@ -2243,16 +2247,20 @@ void SemaObjC::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, Diag(IVI->getLocation(), diag::err_inconsistent_ivar_count); } -static bool shouldWarnUndefinedMethod(const ObjCMethodDecl *M) { +static bool shouldWarnUndefinedMethod(Sema &S, const ObjCMethodDecl *M) { // No point warning no definition of method which is 'unavailable'. - return M->getAvailability() != AR_Unavailable; + // FIXME: We can warn on a definition of partially unavailable method. + return M->getAvailability( + S.getASTContext().getTargetInfo().getPlatformName(), + S.getASTContext().getTargetInfo().getPlatformMinVersion()) != + AR_Unavailable; } static void WarnUndefinedMethod(Sema &S, ObjCImplDecl *Impl, ObjCMethodDecl *method, bool &IncompleteImpl, unsigned DiagID, NamedDecl *NeededFor = nullptr) { - if (!shouldWarnUndefinedMethod(method)) + if (!shouldWarnUndefinedMethod(S, method)) return; // FIXME: For now ignore 'IncompleteImpl'. @@ -3372,13 +3380,18 @@ void SemaObjC::addMethodToGlobalList(ObjCMethodList *List, // For methods with the same declaration, the one that is deprecated // should be put in the front for better diagnostics. - if (Method->isDeprecated() && SameDeclaration && - !ListWithSameDeclaration && !List->getMethod()->isDeprecated()) + if (Method->isDeprecatedInAnyTargetPlatform() && SameDeclaration && + !ListWithSameDeclaration && + !List->getMethod()->isDeprecatedInAnyTargetPlatform()) ListWithSameDeclaration = List; - if (Method->isUnavailable() && SameDeclaration && + // FIXME: What about TVT? + if (Method->isUnavailabledForAllTargetPlatforms() && SameDeclaration && !ListWithSameDeclaration && - List->getMethod()->getAvailability() < AR_Deprecated) + List->getMethod()->getAvailability( + getASTContext().getTargetInfo().getPlatformName(), + getASTContext().getTargetInfo().getPlatformMinVersion()) < + AR_Deprecated) ListWithSameDeclaration = List; continue; } @@ -3398,14 +3411,18 @@ void SemaObjC::addMethodToGlobalList(ObjCMethodList *List, // If a method is deprecated, push it in the global pool. // This is used for better diagnostics. - if (Method->isDeprecated()) { - if (!PrevObjCMethod->isDeprecated()) + if (Method->isDeprecatedInAnyTargetPlatform()) { + if (!PrevObjCMethod->isDeprecatedInAnyTargetPlatform()) List->setMethod(Method); } + // FIXME: What about TVT? // If the new method is unavailable, push it into global pool // unless previous one is deprecated. - if (Method->isUnavailable()) { - if (PrevObjCMethod->getAvailability() < AR_Deprecated) + if (Method->isUnavailabledForAllTargetPlatforms()) { + if (PrevObjCMethod->getAvailability( + getASTContext().getTargetInfo().getPlatformName(), + getASTContext().getTargetInfo().getPlatformMinVersion()) < + AR_Deprecated) List->setMethod(Method); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2b524a956ecc4e..3ed20095993e56 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -100,8 +100,8 @@ bool Sema::CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid) { } // See if this function is unavailable. - if (TreatUnavailableAsInvalid && D->getAvailability() == AR_Unavailable && - cast(CurContext)->getAvailability() != AR_Unavailable) + if (TreatUnavailableAsInvalid && D->isUnavailabledForAllTargetPlatforms() && + !cast(CurContext)->isUnavailabledForAllTargetPlatforms()) return false; if (isa(D)) diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 25cc068ec30bd3..2de5ee4d2598b2 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -5328,13 +5328,23 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr( Context.getTargetInfo().getTriple().getOS())) Version = *MaybeVersion; + VersionTuple VariantVersion; + if (Context.getTargetInfo().hasTargetVariantPlatform()) { + const llvm::Triple *VariantTriple = + Context.getTargetInfo().getDarwinTargetVariantTriple(); + if (auto MaybeVariantVersion = FindSpecVersion( + Context.getTargetInfo().getTargetVariantPlatform(), + VariantTriple ? VariantTriple->getOS() : llvm::Triple::UnknownOS)) + VariantVersion = *MaybeVariantVersion; + } + // The use of `@available` in the enclosing context should be analyzed to // warn when it's used inappropriately (i.e. not if(@available)). if (FunctionScopeInfo *Context = SemaRef.getCurFunctionAvailabilityContext()) Context->HasPotentialAvailabilityViolations = true; - return new (Context) - ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy); + return new (Context) ObjCAvailabilityCheckExpr(Version, VariantVersion, AtLoc, + RParen, Context.BoolTy); } /// Prepare a conversion of the given expression to an ObjC object diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 43129800e98135..e0024e48280958 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4766,7 +4766,7 @@ void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { auto IsDeprecated = [](Decl *D) { while (D) { - if (D->isDeprecated()) + if (D->isDeprecatedInAnyTargetPlatform()) return true; D = llvm::dyn_cast_or_null(D->getDeclContext()); } diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 4d7efed23939e1..73985e6964ed9c 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -1927,7 +1927,7 @@ void SemaObjC::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl *IMPDecl, /* property = */ Prop->getIdentifier(), /* ivar = */ Prop->getDefaultSynthIvarName(Context), Prop->getLocation(), Prop->getQueryKind())); - if (PIDecl && !Prop->isUnavailable()) { + if (PIDecl && !Prop->isUnavailabledForAllTargetPlatforms()) { Diag(Prop->getLocation(), diag::warn_missing_explicit_synthesis); Diag(IMPDecl->getLocation(), diag::note_while_in_implementation); } @@ -2075,8 +2075,7 @@ void SemaObjC::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl *IMPDecl, // Is there a matching property synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || - PropImplMap.count(Prop) || - Prop->getAvailability() == AR_Unavailable) + PropImplMap.count(Prop) || Prop->isUnavailabledForAllTargetPlatforms()) continue; // Diagnose unimplemented getters and setters. @@ -2291,12 +2290,12 @@ void SemaObjC::DiagnoseMissingDesignatedInitOverrides( // marked as unavailable. bool Ignore = false; if (auto *IMD = IFD->getInstanceMethod(MD->getSelector())) { - Ignore = IMD->isUnavailable(); + Ignore = IMD->isUnavailabledForAllTargetPlatforms(); } else { // Check the methods declared in the class extensions too. for (auto *Ext : IFD->visible_extensions()) if (auto *IMD = Ext->getInstanceMethod(MD->getSelector())) { - Ignore = IMD->isUnavailable(); + Ignore = IMD->isUnavailabledForAllTargetPlatforms(); break; } } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 21781f7474e560..b0eac60ce8119b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4172,8 +4172,13 @@ bool Sema::InstantiateClassTemplateSpecialization( if (AlreadyInstantiating) return false; + const TargetInfo &TI = Context.getTargetInfo(); + StringRef PlatformName = TI.getPlatformName(); + const VersionTuple &PlatformMinVersion = TI.getPlatformMinVersion(); + bool HadAvaibilityWarning = - ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, nullptr, nullptr) + ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, PlatformName, + PlatformMinVersion, nullptr, nullptr) .first != AR_Available; ActionResult Pattern = diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 92c555dc427b3d..8fb690d800143c 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1743,6 +1743,7 @@ void ASTStmtReader::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) E->AtLoc = R.getBegin(); E->RParen = R.getEnd(); E->VersionToCheck = Record.readVersionTuple(); + E->VariantVersionToCheck = Record.readVersionTuple(); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 782fecdbd0c805..c1c9cf77feddd6 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1716,6 +1716,7 @@ void ASTStmtWriter::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) VisitExpr(E); Record.AddSourceRange(E->getSourceRange()); Record.AddVersionTuple(E->getVersion()); + Record.AddVersionTuple(E->getVariantVersion()); Code = serialization::EXPR_OBJC_AVAILABILITY_CHECK; } diff --git a/clang/test/CodeGenObjC/availability-check-zippered.m b/clang/test/CodeGenObjC/availability-check-zippered.m new file mode 100644 index 00000000000000..cde108307d96ba --- /dev/null +++ b/clang/test/CodeGenObjC/availability-check-zippered.m @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14 -darwin-target-variant-triple x86_64-apple-ios12-macabi -emit-llvm -o - %s | FileCheck %s + +// XFAIL: * +void use_at_available() { + + // CHECK: call i32 @__isOSVersionAtLeast(i32 10, i32 15, i32 0) + // CHECK-NEXT: call i32 @__isTargetPlatformNative() + // CHECK-NEXT: icmp ne + // CHECK-NEXT: select + // CHECK-NEXT: icmp ne + if (@available(macos 10.15, *)) + ; + + // CHECK: call i32 @__isOSVersionAtLeast(i32 10, i32 15, i32 0) + // CHECK-NEXT: call i32 @__isTargetVariantOSVersionAtLeast(i32 13, i32 0, i32 0) + // CHECK-NEXT: call i32 @__isTargetPlatformNative() + // CHECK-NEXT: icmp ne + // CHECK-NEXT: select + // CHECK-NEXT: icmp ne + if (@available(macos 10.15, iosmac 13, *)) + ; + + // CHECK: call i32 @__isTargetVariantOSVersionAtLeast(i32 13, i32 0, i32 0) + // CHECK-NEXT: call i32 @__isTargetPlatformNative() + // CHECK-NEXT: icmp ne + // CHECK-NEXT: select + // CHECK-NEXT: icmp ne + if (@available(ios 13, *)) + ; + + // This check should be folded: our deployment target is 10.11. + // CHECK-NOT: call i32 @__isOSVersionAtLeast + // CHECK-NOT: call i32 @__isTargetVariantOSVersionAtLeast + // CHECK: br i1 true + if (__builtin_available(macos 10.11, ios 11, *)) + ; +} + +// CHECK: declare i32 @__isOSVersionAtLeast(i32, i32, i32) +// CHECK: declare i32 @__isTargetPlatformNative +// CHECK: declare i32 @__isTargetVariantOSVersionAtLeast(i32, i32, i32) diff --git a/clang/test/FixIt/fixit-availability-zippered.m b/clang/test/FixIt/fixit-availability-zippered.m new file mode 100644 index 00000000000000..82fc8397ff21d1 --- /dev/null +++ b/clang/test/FixIt/fixit-availability-zippered.m @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunguarded-availability -fdiagnostics-parseable-fixits -triple x86_64-apple-macos10.14 -darwin-target-variant-triple x86_64-apple-ios12-macabi %s 2>&1 | FileCheck %s + +// XFAIL: * +__attribute__((availability(macOS, introduced=10.15))) __attribute__((availability(ios, introduced=13))) +int function(void); + +int use() { + function(); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"if (@available(macOS 10.15, iOSMac 13, *)) {\n " +// CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:14-[[@LINE-2]]:14}:"\n } else {\n // Fallback on earlier versions\n }" +} + +#define API_AVAILABLE(...) __attribute__((availability(macOS, introduced=10.15))) __attribute__((availability(ios, introduced=13))) // dummy macro + +API_AVAILABLE(macos(10.15), ios(13)) +@interface NewClass +@end + +@interface OldButOfferFixit +@property(copy) NewClass *prop; +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(macos(10.15), iosmac(13))\n" + +@end diff --git a/clang/test/Sema/attr-availability-zippered.c b/clang/test/Sema/attr-availability-zippered.c new file mode 100644 index 00000000000000..37a3e6c157722e --- /dev/null +++ b/clang/test/Sema/attr-availability-zippered.c @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 "-triple" "x86_64-apple-macos10.14" -darwin-target-variant-triple x86_64-apple-ios12-macabi -fsyntax-only -verify %s + +// XFAIL: * +__attribute__((availability(macos, introduced=10.10, deprecated=10.14), availability(ios, introduced=9, deprecated=12))) +void bothDeprecated() { // expected-note {{'bothDeprecated' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14), availability(ios, introduced=9, deprecated=13))) +void macOSDeprecated() { // expected-note {{'macOSDeprecated' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.15), availability(ios, introduced=9, deprecated=12))) +void iOSDeprecated() { // expected-note {{'iOSDeprecated' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.15), availability(ios, introduced=9, deprecated=13))) +void bothNotDeprecated() { +} + +void checkDeprecated() { + // The compiler may warn here if the deployment target is older than the version introduced + bothDeprecated(); // expected-warning {{'bothDeprecated' is deprecated: first deprecated in macOS 10.14 and first deprecated in iOS (on macOS) 12}} + macOSDeprecated(); // expected-warning {{'macOSDeprecated' is deprecated: first deprecated in macOS 10.14}} + iOSDeprecated(); // expected-warning {{'iOSDeprecated' is deprecated: first deprecated in iOS (on macOS) 12}} + bothNotDeprecated(); +} + +__attribute__((availability(macos, unavailable), availability(ios, unavailable))) +void bothUnavailable() { // expected-note {{'bothUnavailable' has been explicitly marked unavailable here}} +} + +__attribute__((availability(macos, introduced=10.10), availability(ios, unavailable))) +void iosUnavailable() { // expected-note {{'iosUnavailable' has been explicitly marked unavailable here}} +} + +__attribute__((availability(macos, unavailable), availability(ios, introduced=11))) +void macOSUnavailable() { // expected-note {{'macOSUnavailable' has been explicitly marked unavailable here}} +} + +__attribute__((availability(macos, introduced=10.10), availability(ios, introduced=11))) +void bothAvailable() { } + +__attribute__((availability(macos, introduced=10.10, obsoleted=10.14), availability(ios, introduced=9, obsoleted=12))) +void bothObsoleted() { // expected-note {{'bothObsoleted' has been explicitly marked unavailable here}} +} + +void checkUnavailable() { + bothUnavailable(); // expected-error {{'bothUnavailable' is unavailable: not available on macOS and not available on iOS (on macOS)}} + iosUnavailable(); // expected-error {{'iosUnavailable' is unavailable: not available on iOS (on macOS)}} + macOSUnavailable(); // expected-error {{'macOSUnavailable' is unavailable: not available on macOS}} + bothAvailable(); + + bothObsoleted(); // expected-error {{'bothObsoleted' is unavailable: obsoleted in macOS 10.14 and obsoleted in iOS (on macOS) 12}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14))) +typedef struct ZipperedTypedefDifferentOffendingDecl { // expected-note {{'ZipperedTypedefDifferentOffendingDecl' has been explicitly marked deprecated here}} +// expected-warning@-1 {{'ZipperedTypedefDifferentOffendingDecl' is deprecated: first deprecated in iOS (on macOS) 12}} +// expected-note@-2 {{'ZipperedTypedefDifferentOffendingDecl' has been explicitly marked deprecated here}} + int x; +} __attribute__((availability(ios, introduced=9, deprecated=12))) ZipperedTypedefDifferentOffendingDecl_t; // expected-note {{'ZipperedTypedefDifferentOffendingDecl_t' has been explicitly marked deprecated here}} + +void checkZipperDiffOffendingDecl() { + ZipperedTypedefDifferentOffendingDecl_t tt; // expected-warning {{'ZipperedTypedefDifferentOffendingDecl_t' is deprecated: first deprecated in macOS 10.14 and first deprecated in iOS (on macOS) 12}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14), availability(ios, unavailable))) +void mixUnavailableAndDeprecated() { + // expected-note@-1 {{'mixUnavailableAndDeprecated' has been explicitly marked unavailable here}} + // expected-note@-2 {{'mixUnavailableAndDeprecated' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.15), availability(ios, unavailable))) +void iOSUnavailableMacGood() { + // expected-note@-1 {{'iOSUnavailableMacGood' has been explicitly marked unavailable here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14), availability(ios, introduced=11))) +void macOSDeprecatedIOSGood() { + // expected-note@-1 {{'macOSDeprecatedIOSGood' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14, replacement="foo"), availability(ios, introduced=9, deprecated=12, replacement="bar"))) +void bothDeprecatedDiffReplacement() { // expected-note {{'bothDeprecatedDiffReplacement' has been explicitly marked deprecated here}} +} + +__attribute__((availability(macos, unavailable, replacement="bar"), availability(ios, unavailable, replacement="foo"))) +void bothUnavailableSameReplacement() { // expected-note {{'bothUnavailableSameReplacement' has been explicitly marked unavailable here}} +} + +__attribute__((availability(ios, unavailable))) +void justIosUnavailable() { // expected-note {{'justIosUnavailable' has been explicitly marked unavailable here}} +} + +__attribute__((availability(macos, introduced=10.10, deprecated=10.14))) +void justMacOSDeprecated() { // expected-note {{'justMacOSDeprecated' has been explicitly marked deprecated here}} +} + +void checkMix() { + mixUnavailableAndDeprecated(); + // expected-error@-1 {{'mixUnavailableAndDeprecated' is unavailable: not available on iOS (on macOS)}} + // expected-warning@-2 {{'mixUnavailableAndDeprecated' is deprecated: first deprecated in macOS 10.14}} + + iOSUnavailableMacGood(); + // expected-error@-1 {{'iOSUnavailableMacGood' is unavailable: not available on iOS (on macOS)}} + + macOSDeprecatedIOSGood(); + // expected-warning@-1 {{'macOSDeprecatedIOSGood' is deprecated: first deprecated in macOS 10.14}} + + bothDeprecatedDiffReplacement(); // expected-warning {{'bothDeprecatedDiffReplacement' is deprecated: first deprecated in macOS 10.14 and first deprecated in iOS (on macOS) 12}} + + bothUnavailableSameReplacement(); // expected-error {{'bothUnavailableSameReplacement' is unavailable: not available on macOS and not available on iOS (on macOS)}} + + justIosUnavailable(); // expected-error {{'justIosUnavailable' is unavailable: not available on iOS (on macOS)}} + + justMacOSDeprecated(); // expected-warning {{'justMacOSDeprecated' is deprecated: first deprecated in macOS 10.14}} +} + +int deprecatedFunc() __attribute__((deprecated)); // expected-note {{'deprecatedFunc' has been explicitly marked deprecated here}} +int unavailFunc() __attribute__((unavailable)); // expected-note {{'unavailFunc' has been explicitly marked unavailable here}} + +void a() { + int (*ptr)() = deprecatedFunc; // expected-warning {{'deprecatedFunc' is deprecated}} + int (*ptr2)() = unavailFunc; // expected-error {{'unavailFunc' is unavailable}} +} + + +struct IntroducedLaterBoth { } __attribute__((availability(macos, introduced=10.15), availability(ios, introduced=13))); +// expected-note@-1 {{'IntroducedLaterBoth' has been explicitly marked partial here}} + +struct NotYetIntroduced { // expected-note {{annotate 'NotYetIntroduced' with an availability attribute to silence this warning}} + struct IntroducedLaterBoth x; // expected-warning {{'IntroducedLaterBoth' is only available on macOS 10.15 and iOS (on macOS) 13 or newer}} +}; diff --git a/clang/test/SemaObjC/unguarded-availability-zippered.m b/clang/test/SemaObjC/unguarded-availability-zippered.m new file mode 100644 index 00000000000000..f90c4a72421268 --- /dev/null +++ b/clang/test/SemaObjC/unguarded-availability-zippered.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos10.14 -darwin-target-variant-triple x86_64-apple-ios12-macabi -fblocks -fsyntax-only -verify %s + +// XFAIL: * +#ifdef NO_WARNING + // expected-no-diagnostics +#endif + + +#define AVAILABLE_PREV_MAC __attribute__((availability(macos, introduced = 10.13))) +#define AVAILABLE_CURRENT_MAC __attribute__((availability(macos, introduced = 10.14))) +#define AVAILABLE_NEXT_MAC __attribute__((availability(macos, introduced = 10.15))) + +#define AVAILABLE_PREV_IOS __attribute__((availability(ios, introduced = 11))) +#define AVAILABLE_CURRENT_IOS __attribute__((availability(iOSMac, introduced = 12))) +#define AVAILABLE_NEXT_IOS __attribute__((availability(ios, introduced = 13))) + +void bothPreviouslyAvailable() AVAILABLE_PREV_MAC AVAILABLE_PREV_IOS; +void bothCurrentlyAvailable() AVAILABLE_CURRENT_MAC AVAILABLE_CURRENT_IOS; +void bothWillBeAvailable() AVAILABLE_NEXT_MAC AVAILABLE_NEXT_IOS; +// expected-note@-1 3 {{'bothWillBeAvailable' has been explicitly marked partial here}} + +void macOSCurrentlyAvailable() AVAILABLE_CURRENT_MAC AVAILABLE_NEXT_IOS; +// expected-note@-1 {{'macOSCurrentlyAvailable' has been explicitly marked partial here}} + +void test() { + bothPreviouslyAvailable(); + bothCurrentlyAvailable(); + bothWillBeAvailable(); // expected-warning {{'bothWillBeAvailable' is only available on macOS 10.15 and iOS (on macOS) 13 or newer}} + // expected-note@-1{{enclose 'bothWillBeAvailable' in an @available check to silence this warning}} + + macOSCurrentlyAvailable(); // expected-warning {{'macOSCurrentlyAvailable' is only available on iOS (on macOS) 13 or newer}} + // expected-note@-1 {{enclose 'macOSCurrentlyAvailable' in an @available check to silence this warning}} + + if (@available(ios 13, macos 10.15, *)) + bothWillBeAvailable(); + if (@available(ios 13, *)) + bothWillBeAvailable(); // expected-warning {{'bothWillBeAvailable' is only available on macOS 10.15 or newer}} + // expected-note@-1 {{enclose}} + if (@available(macos 10.15, *)) + bothWillBeAvailable(); // expected-warning {{'bothWillBeAvailable' is only available on iOS (on macOS) 13 or newer}} + // expected-note@-1 {{enclose}} +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 47b641446576c6..0dff1054f3b1e4 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8893,7 +8893,10 @@ static CXAvailabilityKind getCursorAvailabilityForDecl(const Decl *D) { if (isa(D) && cast(D)->isDeleted()) return CXAvailability_NotAvailable; - switch (D->getAvailability()) { + const TargetInfo &TI = + D->getTranslationUnitDecl()->getASTContext().getTargetInfo(); + switch ( + D->getAvailability(TI.getPlatformName(), TI.getPlatformMinVersion())) { case AR_Available: case AR_NotYetIntroduced: if (const EnumConstantDecl *EnumConst = dyn_cast(D))