[clang] [clang-tools-extra] Implement Sema iosmac support (PR #218843)
Ian Anderson via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 26 01:38:08 PDT 2026
https://github.com/ian-twilightcoder updated https://github.com/llvm/llvm-project/pull/218843
>From 167e143a368e47bc8b57b632ed6e6eaef2b47f0c Mon Sep 17 00:00:00 2001
From: Alex Lorenz <aleksei_lorenz at apple.com>
Date: Fri, 30 Mar 2018 15:44:31 -0700
Subject: [PATCH] Implement Sema iosmac support
rdar://38885683
apple-llvm-split-commit: 40aab84d831ddfc623de95fbbca06de6f0baf0a6
apple-llvm-split-dir: clang/
---
clang-tools-extra/clangd/FindSymbols.cpp | 4 +-
clang-tools-extra/clangd/XRefs.cpp | 4 +-
clang/include/clang/AST/DeclBase.h | 18 +-
clang/include/clang/AST/ExprObjC.h | 15 +-
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Basic/TargetInfo.h | 24 +
clang/include/clang/Sema/DelayedDiagnostic.h | 20 +-
clang/include/clang/Sema/Sema.h | 9 +-
clang/lib/AST/DeclBase.cpp | 53 +-
clang/lib/Basic/Targets/ARM.cpp | 4 +-
clang/lib/Basic/Targets/OSTargets.cpp | 14 +-
clang/lib/Basic/Targets/OSTargets.h | 21 +-
clang/lib/CodeGen/CGExprScalar.cpp | 11 +-
clang/lib/CodeGen/CGObjC.cpp | 55 +-
clang/lib/CodeGen/CodeGenFunction.h | 3 +-
clang/lib/CodeGen/CodeGenModule.h | 2 +
clang/lib/Edit/RewriteObjCFoundationAPI.cpp | 2 +-
clang/lib/Sema/CodeCompleteConsumer.cpp | 12 +-
clang/lib/Sema/DelayedDiagnostic.cpp | 16 +-
clang/lib/Sema/SemaAvailability.cpp | 1198 +++++++++++------
clang/lib/Sema/SemaDecl.cpp | 5 +-
clang/lib/Sema/SemaDeclAttr.cpp | 25 +-
clang/lib/Sema/SemaDeclObjC.cpp | 45 +-
clang/lib/Sema/SemaExpr.cpp | 4 +-
clang/lib/Sema/SemaExprObjC.cpp | 14 +-
clang/lib/Sema/SemaLookup.cpp | 2 +-
clang/lib/Sema/SemaObjCProperty.cpp | 9 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 7 +-
clang/lib/Serialization/ASTReaderStmt.cpp | 1 +
clang/lib/Serialization/ASTWriterStmt.cpp | 1 +
.../CodeGenObjC/availability-check-zippered.m | 41 +
.../test/FixIt/fixit-availability-zippered.m | 23 +
clang/test/Sema/attr-availability-zippered.c | 133 ++
.../unguarded-availability-zippered.m | 42 +
clang/tools/libclang/CIndex.cpp | 5 +-
35 files changed, 1371 insertions(+), 474 deletions(-)
create mode 100644 clang/test/CodeGenObjC/availability-check-zippered.m
create mode 100644 clang/test/FixIt/fixit-availability-zippered.m
create mode 100644 clang/test/Sema/attr-availability-zippered.c
create mode 100644 clang/test/SemaObjC/unguarded-availability-zippered.m
diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp
index 2a674d375b5d9..f3a10c281f424 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<DocumentSymbol> 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 86528d806eab3..bfc440302696c 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -1865,7 +1865,7 @@ static std::optional<TypeHierarchyItem>
declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
auto Result = declToHierarchyItem<TypeHierarchyItem>(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<CallHierarchyItem>(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 9d233be282dbb..2cf8a792c9df8 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 b424530c8447c..39f22c961ce96 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 cfb2ee3368201..c6b237c883a2b 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<warn_unguarded_availability_unavailable.Summary>,
InGroup<UnguardedAvailabilityNew>;
+def warn_zippered_unguarded_availability :
+ Warning<"%0 is only available on %1 %2 and %3 %4 or newer">,
+ InGroup<UnguardedAvailabilityNew>;
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 5a541aaf797fe..66a7ba2bd096f 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 0105089a393f1..5e45c3459f151 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<SourceLocation> Locs,
- const NamedDecl *ReferringDecl,
- const NamedDecl *OffendingDecl,
- const ObjCInterfaceDecl *UnknownObjCClass,
- const ObjCPropertyDecl *ObjCProperty,
- StringRef Msg,
- bool ObjCPropertyAccess);
+ static DelayedDiagnostic
+ makeAvailability(AvailabilityResult AR, ArrayRef<SourceLocation> 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 dcf112fd8eaa4..42858681fba88 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<SourceLocation> Locs);
std::pair<AvailabilityResult, const NamedDecl *>
- 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 70f61fa57a682..aff04541c6781 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<FunctionTemplateDecl>(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<AvailabilityAttr>(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<WeakImportAttr>(A))
return true;
@@ -885,7 +914,13 @@ bool Decl::isWeakImported() const {
if (const auto *Availability = dyn_cast<AvailabilityAttr>(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 8d6e8d6990e80..0cd4b705bbcb9 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 04d9e13d3c9a6..91f98595dbbf5 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 fb5c17cefc004..724406f82b725 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 <typename Target>
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 173931213ce68..5be65e13a34f9 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 b9cbf593fb1c4..73b1cdba09514 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<unsigned> 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<llvm::Value *> &Args) {
+ if (V.empty())
+ return;
+ std::optional<unsigned> 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<llvm::Value *, 3> Args;
+ llvm::SmallVector<llvm::Value *, 3> 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 7bdc79d86ea0a..3bf62755ac270 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 1f5ecf734c528..ffca028f65709 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 e8d4660fd36b2..fb3d1fef764b0 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 50a552272f421..750a0f00ecff9 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<EnumConstantDecl>(D))
- AR = std::max(AR, cast<Decl>(D->getDeclContext())->getAvailability());
+ AR = std::max(AR, cast<Decl>(D->getDeclContext())
+ ->getAvailability(TI.getPlatformName(),
+ TI.getPlatformMinVersion()));
return AR;
}
diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp
index cb2721b92090e..e825abaa5bc71 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<SourceLocation> Locs,
- const NamedDecl *ReferringDecl,
- const NamedDecl *OffendingDecl,
- const ObjCInterfaceDecl *UnknownObjCClass,
- const ObjCPropertyDecl *ObjCProperty,
- StringRef Msg,
- bool ObjCPropertyAccess) {
+DelayedDiagnostic DelayedDiagnostic::makeAvailability(
+ AvailabilityResult AR, ArrayRef<SourceLocation> 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 28a4b760dbd4d..2c736015c20ca 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<AvailabilityResult, const NamedDecl *>
-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<ObjCInterfaceDecl>(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<EnumDecl>(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<VarDecl>(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<SourceLocation> 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<SourceLocation> Locs;
+ const ObjCInterfaceDecl *UnknownObjCClass;
+ bool ObjCPropertyAccess;
+
+ PlatformAgnosticAvailabilityDiagInfo(
+ const NamedDecl *ReferringDecl, ArrayRef<SourceLocation> 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<TagDecl>(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<AvailabilityAttr>())
- return;
- Preprocessor &PP = S.getPreprocessor();
- if (!PP.isMacroDefined("API_AVAILABLE"))
+ if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
+ if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
+ if (TD->getDeclName().isEmpty()) {
+ S.Diag(TD->getLocation(),
+ diag::note_decl_unguarded_availability_silence)
+ << /*Anonymous*/ 1 << TD->getKindName();
return;
- std::optional<AttributeInsertion> 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<AvailabilityAttr>())
+ return;
+ Preprocessor &PP = S.getPreprocessor();
+ if (!PP.isMacroDefined("API_AVAILABLE"))
+ return;
+ std::optional<AttributeInsertion> 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<FunctionDecl>(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<DeprecatedAttr>())
- 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<DeprecatedAttr>())
+ 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<DeprecatedAttr>())
+ 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<UnavailableAttr>()) {
+ if (auto AL = PlatformInfo.OffendingDecl->getAttr<UnavailableAttr>()) {
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<FixItHint, 12> FixIts;
- if (K == AR_Deprecated) {
- StringRef Replacement;
- if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
- 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<ObjCMethodDecl>(ReferringDecl)) {
- Selector Sel = MethodDecl->getSelector();
- SmallVector<StringRef, 12> SelectorSlotNames;
- std::optional<unsigned> 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<ObjCMethodDecl>(Info.ReferringDecl)) {
+ Selector Sel = MethodDecl->getSelector();
+ SmallVector<StringRef, 12> SelectorSlotNames;
+ std::optional<unsigned> 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<FunctionDecl>(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<SourceLocation> 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<Decl>(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<VersionTuple, 8> AvailabilityStack;
+ struct ZipperedVersionTuple {
+ std::optional<VersionTuple> Version;
+ std::optional<VersionTuple> 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<ZipperedVersionTuple, 8> AvailabilityStack;
SmallVector<const Stmt *, 16> 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<CompoundStmt>(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<DeclStmt>(StmtOfUse) && Scope) {
+ for (const Decl *D : cast<DeclStmt>(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<PlatformSpecificAvailabilityDiag> {
+ 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<CompoundStmt>(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<DeclStmt>(StmtOfUse) && Scope) {
- for (const Decl *D : cast<DeclStmt>(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<PlatformSpecificAvailabilityDiag> PlatformInfo =
+ CreatePlatformDiagInfo(/*IsTargetVariantPlatform=*/false);
+ std::optional<PlatformSpecificAvailabilityDiag> 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<ObjCAvailabilityCheckExpr>(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<VersionTuple>(CondVersion),
+ VariantCondVersion.empty()
+ ? std::nullopt
+ : std::optional<VersionTuple>(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<PlatformSpecificAvailabilityDiag> {
+ 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<ObjCMethodDecl>(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<PlatformSpecificAvailabilityDiag> TargetAvailabilityDiag =
+ CreateAvailabilityDiagnostic(/*IsTargetVariantPlatform=*/false);
+ std::optional<PlatformSpecificAvailabilityDiag> 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<ObjCMethodDecl>(D)) {
- if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
- AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
- if (PDeclResult == Result)
- ObjCPDecl = PD;
+ Decl *Ctx = cast<Decl>(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 a99fcb56d1138..2181617de7a6f 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 a61fc54ade757..d56f136889d2e 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<DelayedDiagnostic &>(*(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 04ba528098af4..51da6c624972a 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<ObjCMethodDecl>(ND)) {
if (Availability != AR_Unavailable)
@@ -310,7 +313,8 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S, const NamedDecl *ND,
return;
}
if (const auto *CD = dyn_cast<ObjCCategoryDecl>(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 2b524a956ecc4..3ed20095993e5 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<Decl>(CurContext)->getAvailability() != AR_Unavailable)
+ if (TreatUnavailableAsInvalid && D->isUnavailabledForAllTargetPlatforms() &&
+ !cast<Decl>(CurContext)->isUnavailabledForAllTargetPlatforms())
return false;
if (isa<UnresolvedUsingIfExistsDecl>(D))
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index 25cc068ec30bd..2de5ee4d2598b 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 43129800e9813..e0024e4828095 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<NamespaceDecl>(D->getDeclContext());
}
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index 4d7efed23939e..73985e6964ed9 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 21781f7474e56..b0eac60ce8119 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<CXXRecordDecl *> Pattern =
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 92c555dc427b3..8fb690d800143 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 782fecdbd0c80..c1c9cf77feddd 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 0000000000000..cde108307d96b
--- /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 0000000000000..82fc8397ff21d
--- /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))
+ at interface NewClass
+ at end
+
+ at interface OldButOfferFixit
+ at property(copy) NewClass *prop;
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(macos(10.15), iosmac(13))\n"
+
+ at end
diff --git a/clang/test/Sema/attr-availability-zippered.c b/clang/test/Sema/attr-availability-zippered.c
new file mode 100644
index 0000000000000..37a3e6c157722
--- /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 at -1 {{'ZipperedTypedefDifferentOffendingDecl' is deprecated: first deprecated in iOS (on macOS) 12}}
+// expected-note at -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 at -1 {{'mixUnavailableAndDeprecated' has been explicitly marked unavailable here}}
+ // expected-note at -2 {{'mixUnavailableAndDeprecated' has been explicitly marked deprecated here}}
+}
+
+__attribute__((availability(macos, introduced=10.10, deprecated=10.15), availability(ios, unavailable)))
+void iOSUnavailableMacGood() {
+ // expected-note at -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 at -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 at -1 {{'mixUnavailableAndDeprecated' is unavailable: not available on iOS (on macOS)}}
+ // expected-warning at -2 {{'mixUnavailableAndDeprecated' is deprecated: first deprecated in macOS 10.14}}
+
+ iOSUnavailableMacGood();
+ // expected-error at -1 {{'iOSUnavailableMacGood' is unavailable: not available on iOS (on macOS)}}
+
+ macOSDeprecatedIOSGood();
+ // expected-warning at -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 at -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 0000000000000..f90c4a7242126
--- /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 at -1 3 {{'bothWillBeAvailable' has been explicitly marked partial here}}
+
+void macOSCurrentlyAvailable() AVAILABLE_CURRENT_MAC AVAILABLE_NEXT_IOS;
+// expected-note at -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 at -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 at -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 at -1 {{enclose}}
+ if (@available(macos 10.15, *))
+ bothWillBeAvailable(); // expected-warning {{'bothWillBeAvailable' is only available on iOS (on macOS) 13 or newer}}
+ // expected-note at -1 {{enclose}}
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 47b641446576c..0dff1054f3b1e 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<FunctionDecl>(D) && cast<FunctionDecl>(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<EnumConstantDecl>(D))
More information about the cfe-commits
mailing list