diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index ad6e16eaa87dc..f8db0238db2ad 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -919,6 +919,7 @@ class ASTContext : public RefCountedBase { FeatureAvailKind Kind = FeatureAvailKind::None; clang::Decl *Decl = nullptr; ImplicitCastExpr *Call = nullptr; + bool IsDeprecated = false; bool isInvalid() const { return Kind == FeatureAvailKind::None; } }; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 15885907d39e3..d98c6ef380a41 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -287,6 +287,8 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion, ]>, DiagCategory<"Deprecations">; +def PermanentlyAvailableDomain : DiagGroup<"permanently-available-domain">; + def CXX20Designator : DiagGroup<"c++20-designator">; // Allow -Wno-c99-designator to be used to turn off all warnings on valid C99 // designators (including the warning controlled by -Wc++20-designator). diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fd33296ed2185..6a7ef7c011366 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4136,6 +4136,14 @@ def err_features_invalid_for_decl : Error< "feature attributes cannot be applied to %0">; def err_features_invalid_name : Error< "cannot find definition of feature attribute '%0'">; +def warn_deprecated_availability_domain : Warning< + "availability domain '%0' is deprecated">, InGroup>; +def warn_permanently_available_domain_expr : Warning< + "unnecessary check for '%0'; this expression will always evaluate to true">, + InGroup; +def warn_permanently_available_domain_decl : Warning< + "%select{attribute has no effect because '%0' is always available|declaration is permanently unavailable because '%0' is always available}1">, + InGroup; def err_features_invalid__arg : Error< "invalid argument %0: must evaluate to 0 or 1">; def err_feature_invalid_for_decl : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ab22e8991da15..2020f63987e06 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -143,6 +143,7 @@ class InitializedEntity; enum class LangAS : unsigned int; class LocalInstantiationScope; class LookupResult; +class MacroInfo; class MangleNumberingContext; typedef ArrayRef ModuleIdPath; class ModuleLoader; @@ -2670,6 +2671,15 @@ class Sema final : public SemaBase { void DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs); + void diagnoseDeprecatedAvailabilityDomain(StringRef DomainName, + SourceLocation AvailLoc, + SourceLocation DomainLoc, + bool IsUnavailable = false, + const ParsedAttr *PA = nullptr); + + /// A map that indicates whether a macro is simple. + llvm::SmallDenseMap SimpleFeatureAvailabiltyMacros; + /// Retrieve the current function, if any, that should be analyzed for /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 015b1204045d6..67c5c30e7b0c4 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -997,7 +997,8 @@ ASTContext::getFeatureAvailInfo(Decl *D) const { llvm_unreachable("invalid feature kind"); } - ASTContext::AvailabilityDomainInfo Info{Kind, D, nullptr}; + ASTContext::AvailabilityDomainInfo Info{Kind, D, nullptr, + D->hasAttr()}; if (Kind == FeatureAvailKind::Dynamic) { Expr *FnExpr = Init->getInit(1); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 7ec9be50ce1c0..e3e2692b41e87 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2492,6 +2492,10 @@ static void handleFeatureAvailabilityAttr(Sema &S, Decl *D, int IsUnavailable = AL.getArgAsExpr(1)->getIntegerConstantExpr(S.Context)->getExtValue(); + S.diagnoseDeprecatedAvailabilityDomain(II->getName(), AL.getLoc(), + AL.getArgAsIdent(0)->getLoc(), + IsUnavailable, &AL); + if (IsUnavailable != 0 && IsUnavailable != 1) { S.Diag(AL.getArgAsExpr(1)->getExprLoc(), diag::err_features_invalid__arg) << IsUnavailable; diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index cf1e4945aaf08..f0e6855aedf33 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -5162,6 +5162,10 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr( if (FunctionScopeInfo *Info = SemaRef.getCurFunctionAvailabilityContext()) Info->HasPotentialFeatureAvailabilityViolations = true; auto Spec = AvailSpecs.front(); + + SemaRef.diagnoseDeprecatedAvailabilityDomain(Spec.getDomainName(), AtLoc, + Spec.getBeginLoc()); + return ObjCAvailabilityCheckExpr::CreateAvailabilityFeatureCheck( AtLoc, RParen, Context.BoolTy, Spec.getDomainName(), Context); } diff --git a/clang/lib/Sema/SemaFeatureAvailability.cpp b/clang/lib/Sema/SemaFeatureAvailability.cpp index 4e4344cc30414..82f2ef493fff4 100644 --- a/clang/lib/Sema/SemaFeatureAvailability.cpp +++ b/clang/lib/Sema/SemaFeatureAvailability.cpp @@ -13,6 +13,9 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" @@ -240,3 +243,156 @@ void Sema::DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, Decl *Ctx = cast(getCurLexicalContext()); diagnoseDeclFeatureAvailability(D, Locs.front(), Ctx, *this); } + +static bool isSimpleFeatureAvailabiltyMacro(MacroInfo *Info) { + // Must match: + // __attribute__((availability(domain : id, id/numeric_constant))) + if (Info->getNumTokens() != 13) + return false; + + if (!Info->getReplacementToken(0).is(tok::kw___attribute) || + !Info->getReplacementToken(1).is(tok::l_paren) || + !Info->getReplacementToken(2).is(tok::l_paren)) + return false; + + if (const Token &Tk = Info->getReplacementToken(3); + !Tk.is(tok::identifier) || + Tk.getIdentifierInfo()->getName() != "availability") + return false; + + if (!Info->getReplacementToken(4).is(tok::l_paren)) + return false; + + if (const Token &Tk = Info->getReplacementToken(5); + !Tk.is(tok::identifier) || Tk.getIdentifierInfo()->getName() != "domain") + return false; + + if (!Info->getReplacementToken(6).is(tok::colon)) + return false; + + if (const Token &Tk = Info->getReplacementToken(7); !Tk.is(tok::identifier)) + return false; + + if (!Info->getReplacementToken(8).is(tok::comma)) + return false; + + if (const Token &Tk = Info->getReplacementToken(9); + !Tk.is(tok::identifier) && !Tk.is(tok::numeric_constant)) + return false; + + if (!Info->getReplacementToken(10).is(tok::r_paren) || + !Info->getReplacementToken(11).is(tok::r_paren) || + !Info->getReplacementToken(12).is(tok::r_paren)) + return false; + + return true; +} + +void Sema::diagnoseDeprecatedAvailabilityDomain(StringRef DomainName, + SourceLocation AvailLoc, + SourceLocation DomainLoc, + bool IsUnavailable, + const ParsedAttr *PA) { + auto CreateFixIt = [&]() { + if (PA->getRange().getBegin().isMacroID()) { + auto *MacroII = PA->getMacroIdentifier(); + + // Macro identifier isn't always set. + if (!MacroII) + return FixItHint{}; + + MacroDefinition MD = PP.getMacroDefinition(MacroII); + MacroInfo *Info = MD.getMacroInfo(); + + bool IsSimple; + auto It = SimpleFeatureAvailabiltyMacros.find(Info); + + if (It == SimpleFeatureAvailabiltyMacros.end()) { + IsSimple = isSimpleFeatureAvailabiltyMacro(Info); + SimpleFeatureAvailabiltyMacros[Info] = IsSimple; + } else { + IsSimple = It->second; + } + + if (!IsSimple) + return FixItHint{}; + + FileID FID = SourceMgr.getFileID(AvailLoc); + const SrcMgr::ExpansionInfo *EI = + &SourceMgr.getSLocEntry(FID).getExpansion(); + if (IsUnavailable) + return FixItHint::CreateReplacement(EI->getExpansionLocRange(), + "__attribute__((unavailable))"); + return FixItHint::CreateRemoval(EI->getExpansionLocRange()); + } + + if (PA->getSyntax() != AttributeCommonInfo::Syntax::AS_GNU) + return FixItHint{}; + + SourceRange AttrRange = PA->getRange(); + + // Replace the availability attribute with "unavailable". + if (IsUnavailable) + return FixItHint::CreateReplacement(AttrRange, "unavailable"); + + // Remove the availability attribute. + + // If there is a leading comma, there's another operand that precedes the + // availability attribute. In that case, remove the availability attribute + // and the comma. + Token PrevTok = *Lexer::findPreviousToken(AttrRange.getBegin(), SourceMgr, + getLangOpts(), false); + if (PrevTok.is(tok::comma)) + return FixItHint::CreateRemoval( + SourceRange(PrevTok.getLocation(), AttrRange.getEnd())); + + // If there is a trailing comma, there's another operand that follows the + // availability attribute. In that case, remove the availability attribute + // and the comma. + Token NextTok = *Lexer::findNextToken(AttrRange.getEnd(), SourceMgr, + getLangOpts(), false); + if (NextTok.is(tok::comma)) + return FixItHint::CreateRemoval( + SourceRange(AttrRange.getBegin(), NextTok.getLocation())); + + // If no leading or trailing commas are found, the availability attribute is + // the only operand. Remove the entire attribute construct. + + // Look for '__attribute'. + for (int i = 0; i < 2; ++i) + PrevTok = *Lexer::findPreviousToken(PrevTok.getLocation(), SourceMgr, + getLangOpts(), false); + if (!PrevTok.is(tok::raw_identifier) || + PrevTok.getRawIdentifier() != "__attribute__") + return FixItHint{}; + + // Look for the closing ')'. + NextTok = *Lexer::findNextToken(NextTok.getLocation(), SourceMgr, + getLangOpts(), false); + if (!NextTok.is(tok::r_paren)) + return FixItHint{}; + + return FixItHint::CreateRemoval( + SourceRange(PrevTok.getLocation(), NextTok.getLocation())); + }; + + ASTContext::AvailabilityDomainInfo Info = + Context.getFeatureAvailInfo(DomainName); + + if (Info.IsDeprecated) { + Diag(DomainLoc, diag::warn_deprecated_availability_domain) << DomainName; + if (Info.Kind == FeatureAvailKind::AlwaysAvailable) { + if (PA) { + auto FixitDiag = + Diag(AvailLoc, diag::warn_permanently_available_domain_decl) + << DomainName << IsUnavailable; + + FixItHint Hint = CreateFixIt(); + if (!Hint.isNull()) + FixitDiag << Hint; + } else + Diag(AvailLoc, diag::warn_permanently_available_domain_expr) + << DomainName; + } + } +} diff --git a/clang/test/Sema/feature-availability.c b/clang/test/Sema/feature-availability.c index dbc35cb9e3c48..d5f08fae6cfd2 100644 --- a/clang/test/Sema/feature-availability.c +++ b/clang/test/Sema/feature-availability.c @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -triple arm64-apple-macosx15 -fblocks -ffeature-availability=feature1:1 -ffeature-availability=feature2:0 -ffeature-availability=feature3:on -fsyntax-only -Wunreachable-code -verify=expected,redecl %s // RUN: %clang_cc1 -triple arm64-apple-macosx15 -fblocks -ffeature-availability=feature1:1 -ffeature-availability=feature2:0 -ffeature-availability=feature3:on -fsyntax-only -Wunreachable-code -Wno-domain-availability-redeclaration -verify=expected %s // RUN: %clang_cc1 -triple arm64-apple-macosx15 -fblocks -fsyntax-only -Wunreachable-code -verify=expected,redecl -DUSE_DOMAIN %s +// RUN: not %clang_cc1 -triple arm64-apple-macosx15 -fblocks -fsyntax-only -Wunreachable-code -DUSE_DOMAIN -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s #include @@ -8,6 +9,31 @@ #define UNAVAIL 1 #define INVALID 2 +#define FEATURE_AVAILABLE(domain_name) \ + __attribute__((availability(domain : domain_name, AVAIL))) + +#define FEATURE_UNAVAILABLE(domain_name) \ + __attribute__((availability(domain : domain_name, UNAVAIL))) + +#define MY_FEATURE_AVAILABLE(domain_name) \ + /* abc */ __attribute__((availability(domain /*abc*/ : domain_name, 0))) // comment. + +#define VISIBILITY +#define MY_FEATURE_AVAILABLE2(domain_name) \ + VISIBILITY __attribute__((availability(domain : domain_name, 0))) + +#define MY_FEATURE_AVAILABLE3(domain_name) FEATURE_AVAILABLE(domain_name) + +#define AVAIL_ARGS domain: deprecated_feature1, 0 +#define MY_FEATURE_AVAILABLE4(domain_name) \ + __attribute__((availability(AVAIL_ARGS))) + +#define MY_FEATURE_AVAILABLE5(domain_name, function) \ + FEATURE_AVAILABLE(domain_name) \ + function + +#define DEPRECATED_FEATURE1_UNAVAILABLE __attribute__((availability(domain: deprecated_feature1, 1))) + #ifdef USE_DOMAIN int pred1(void); CLANG_ENABLED_AVAILABILITY_DOMAIN(feature1); @@ -15,6 +41,10 @@ CLANG_DISABLED_AVAILABILITY_DOMAIN(feature2); CLANG_ENABLED_AVAILABILITY_DOMAIN(feature3); CLANG_DYNAMIC_AVAILABILITY_DOMAIN(feature4, pred1); CLANG_ALWAYS_ENABLED_AVAILABILITY_DOMAIN(feature5); +__attribute__((deprecated)) +CLANG_ALWAYS_ENABLED_AVAILABILITY_DOMAIN(deprecated_feature1); +__attribute__((deprecated)) +CLANG_ENABLED_AVAILABILITY_DOMAIN(deprecated_feature2); #endif #pragma clang attribute push (__attribute__((availability(domain:feature1, AVAIL))), apply_to=any(function)) @@ -38,6 +68,102 @@ __attribute__((availability(domain:feature4, AVAIL))) int g4; __attribute__((availability(domain:feature4, UNAVAIL))) int g5; __attribute__((availability(domain:feature5, AVAIL))) void func21(void); __attribute__((availability(domain:feature5, UNAVAIL))) void func22(void); + +__attribute__((visibility("hidden"))) +__attribute__((availability(domain:deprecated_feature1, AVAIL))) +void deprecated_feature1_avail_func1(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:65}:"" + +__attribute__((availability(domain:deprecated_feature1, AVAIL), visibility("hidden"))) +void deprecated_feature1_avail_func2(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:16-[[@LINE-4]]:64}:"" + +__attribute__((visibility("hidden"), availability(domain:deprecated_feature1, AVAIL))) +void deprecated_feature1_avail_func3(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:36-[[@LINE-4]]:85}:"" + +FEATURE_AVAILABLE(deprecated_feature1) +void deprecated_feature1_avail_func4(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:39}:"" + +MY_FEATURE_AVAILABLE(deprecated_feature1) +void deprecated_feature1_avail_func5(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:42}:"" + +MY_FEATURE_AVAILABLE2(deprecated_feature1) +void deprecated_feature1_avail_func6(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK-NOT: fix-it: + +MY_FEATURE_AVAILABLE3(deprecated_feature1) +void deprecated_feature1_avail_func7(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK-NOT: fix-it: + +MY_FEATURE_AVAILABLE4(deprecated_feature1) +void deprecated_feature1_avail_func8(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK-NOT: fix-it: + +MY_FEATURE_AVAILABLE5(deprecated_feature1, void deprecated_feature1_avail_func9(void);) +// expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-2 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK-NOT: fix-it: + +FEATURE_AVAILABLE(deprecated_feature1) +void deprecated_feature1_avail_func10(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{attribute has no effect because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:39}:"" + +__attribute__((visibility("hidden"))) +__attribute__((availability(domain:deprecated_feature1, UNAVAIL))) +void deprecated_feature1_unavail_func1(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:16-[[@LINE-4]]:65}:"unavailable" + +__attribute__((availability(domain:deprecated_feature1, UNAVAIL), visibility("hidden"))) +void deprecated_feature1_unavail_func2(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:16-[[@LINE-4]]:65}:"unavailable" + +__attribute__((visibility("hidden"), availability(domain:deprecated_feature1, UNAVAIL))) +void deprecated_feature1_unavail_func3(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:38-[[@LINE-4]]:87}:"unavailable" + +FEATURE_UNAVAILABLE(deprecated_feature1) +void deprecated_feature1_unavail_func4(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:41}:"__attribute__((unavailable))" + +DEPRECATED_FEATURE1_UNAVAILABLE +void deprecated_feature1_unavail_func5(void); +// expected-warning@-2 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-3 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +// CHECK: fix-it:"{{.*}}feature-availability.c":{[[@LINE-4]]:1-[[@LINE-4]]:32}:"__attribute__((unavailable))" + +__attribute__((availability(domain:deprecated_feature2, AVAIL))) +void deprecated_feature2_unavail_func1(void); +// expected-warning@-2 {{availability domain 'deprecated_feature2' is deprecated}} + #endif void test_unreachable_code(void) { @@ -269,4 +395,20 @@ void test8(void) { func22(); } } + +void test_deprecated_feature(void) { + if (__builtin_available(domain:deprecated_feature1)) + // expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} + // expected-warning@-2 {{unnecessary check for 'deprecated_feature1'; this expression will always evaluate to true}} + ; + + if (!__builtin_available(domain:deprecated_feature1)) + // expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} + // expected-warning@-2 {{unnecessary check for 'deprecated_feature1'; this expression will always evaluate to true}} + ; + + if (__builtin_available(domain:deprecated_feature2)) + // expected-warning@-1 {{availability domain 'deprecated_feature2' is deprecated}} + ; +} #endif diff --git a/clang/test/SemaObjC/feature-availability.m b/clang/test/SemaObjC/feature-availability.m index d2c9195ca3813..d90f718aca8bc 100644 --- a/clang/test/SemaObjC/feature-availability.m +++ b/clang/test/SemaObjC/feature-availability.m @@ -14,6 +14,10 @@ CLANG_ENABLED_AVAILABILITY_DOMAIN(feature1); #endif CLANG_DISABLED_AVAILABILITY_DOMAIN(feature2); +__attribute__((deprecated)) +CLANG_ALWAYS_ENABLED_AVAILABILITY_DOMAIN(deprecated_feature1); +__attribute__((deprecated)) +CLANG_ENABLED_AVAILABILITY_DOMAIN(deprecated_feature2); #endif __attribute__((availability(domain:feature1, AVAIL))) int func1(void); @@ -208,3 +212,33 @@ -(void)m4 { [super m4]; // enabled-error {{cannot use 'm4' because feature 'feature1' is unavailable in this context}} } @end + +#ifdef USE_DOMAIN +__attribute__((availability(domain:deprecated_feature1, AVAIL))) +// expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-2 {{attribute has no effect because 'deprecated_feature1' is always available}} +@interface DeprecatedC1 +@end + +__attribute__((availability(domain:deprecated_feature1, UNAVAIL))) +// expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} +// expected-warning@-2 {{declaration is permanently unavailable because 'deprecated_feature1' is always available}} +@interface DeprecatedC2 +@end + +__attribute__((availability(domain:deprecated_feature2, AVAIL))) +// expected-warning@-1 {{availability domain 'deprecated_feature2' is deprecated}} +@interface DeprecatedC3 +@end + +void test_deprecated1(void) { + if (@available(domain:deprecated_feature1)) + // expected-warning@-1 {{availability domain 'deprecated_feature1' is deprecated}} + // expected-warning@-2 {{unnecessary check for 'deprecated_feature1'; this expression will always evaluate to true}} + ; + + if (@available(domain:deprecated_feature2)) + // expected-warning@-1 {{availability domain 'deprecated_feature2' is deprecated}} + ; +} +#endif