Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -933,6 +933,8 @@ bool isConstexpr() const { return IfStmtBits.IsConstexpr; } void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; } + bool isObjCAvailabilityCheck() const; + SourceLocation getLocStart() const LLVM_READONLY { return IfLoc; } SourceLocation getLocEnd() const LLVM_READONLY { if (SubExprs[ELSE]) Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -95,8 +95,9 @@ def DeprecatedAttributes : DiagGroup<"deprecated-attributes">; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; def UnavailableDeclarations : DiagGroup<"unavailable-declarations">; -def PartialAvailability : DiagGroup<"partial-availability">; def UnguardedAvailability : DiagGroup<"unguarded-availability">; +// partial-availability is an alias of unguarded-availability. +def : DiagGroup<"partial-availability", [UnguardedAvailability]>; def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">; def DeprecatedRegister : DiagGroup<"deprecated-register">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2622,8 +2622,20 @@ def note_protocol_method : Note< "protocol method is here">; -def warn_available_using_star_case : Warning< - "using '*' case here, platform %0 is not accounted for">, InGroup; +def warn_unguarded_availability : + Warning<"%0 is only available on %1 %2 or newer">, + InGroup, DefaultIgnore; +def warn_partial_availability : Warning<"%0 is only available conditionally">, + InGroup, DefaultIgnore; +def note_partial_availability_silence : Note< + "explicitly redeclare %0 to silence this warning">; +def note_unguarded_available_silence : Note< + "enclose %0 in an @available check to silence this warning">; +def warn_partial_message : Warning<"%0 is partial: %1">, + InGroup, DefaultIgnore; +def warn_partial_fwdclass_message : Warning< + "%0 may be partial because the receiver type is unknown">, + InGroup, DefaultIgnore; // Thread Safety Attributes def warn_invalid_capability_name : Warning< @@ -4245,15 +4257,6 @@ def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to the " "call site%select{| or in %2| or in an associated namespace of one of its arguments}1">; def err_undeclared_use : Error<"use of undeclared %0">; -def warn_partial_availability : Warning<"%0 is only available conditionally">, - InGroup, DefaultIgnore; -def note_partial_availability_silence : Note< - "explicitly redeclare %0 to silence this warning">; -def warn_partial_message : Warning<"%0 is partial: %1">, - InGroup, DefaultIgnore; -def warn_partial_fwdclass_message : Warning< - "%0 may be partial because the receiver type is unknown">, - InGroup, DefaultIgnore; def warn_deprecated : Warning<"%0 is deprecated">, InGroup; def warn_property_method_deprecated : @@ -4706,6 +4709,8 @@ "jump bypasses initialization of VLA type alias">; def note_protected_by_constexpr_if : Note< "jump enters controlled statement of constexpr if">; +def note_protected_by_if_available : Note< + "jump enters controlled statement of if available">; def note_protected_by_vla : Note< "jump bypasses initialization of variable length array">; def note_protected_by_objc_try : Note< Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -106,11 +106,15 @@ bool HasDroppedStmt : 1; /// \brief True if current scope is for OpenMP declare reduction combiner. - bool HasOMPDeclareReductionCombiner; + bool HasOMPDeclareReductionCombiner : 1; /// \brief Whether there is a fallthrough statement in this function. bool HasFallthroughStmt : 1; + /// \brief Whether we make reference to a declaration that could be + /// unavailable. + bool HasPotentialAvailabilityViolations : 1; + /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). @@ -381,6 +385,7 @@ HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false), HasFallthroughStmt(false), + HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3641,6 +3641,9 @@ bool makeUnavailableInSystemHeader(SourceLocation loc, UnavailableAttr::ImplicitReason reason); + /// \brief Issue any -Wunguarded-availability warnings in \c FD + void DiagnoseUnguardedAvailabilityViolations(Decl *FD); + //===--------------------------------------------------------------------===// // Expression Parsing Callbacks: SemaExpr.cpp. @@ -9602,6 +9605,13 @@ /// availability attribuite effectively has the availability of the interface. VersionTuple getVersionForDecl(const Decl *Ctx) const; + /// \brief Whether we should emit an availability diagnostic for \c D. + bool ShouldDiagnoseAvailabilityOfDecl( + NamedDecl *&D, const ObjCInterfaceDecl *UnknownObjCClass, + bool ObjCPropertyAccess, ObjCPropertyDecl const *&ObjCPDecl, + VersionTuple ContextVersion, std::string *Message, + AvailabilityDiagnostic &AD); + const DeclContext *getCurObjCLexicalContext() const { const DeclContext *DC = getCurLexicalContext(); // A category implicitly has the attribute of the interface. Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -794,6 +794,10 @@ VarRange.getEnd()); } +bool IfStmt::isObjCAvailabilityCheck() const { + return isa(SubExprs[COND]); +} + ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP) Index: lib/Sema/JumpDiagnostics.cpp =================================================================== --- lib/Sema/JumpDiagnostics.cpp +++ lib/Sema/JumpDiagnostics.cpp @@ -325,30 +325,27 @@ case Stmt::IfStmtClass: { IfStmt *IS = cast(S); - if (!IS->isConstexpr()) + if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck())) break; + unsigned Diag = IS->isConstexpr() ? diag::note_protected_by_constexpr_if + : diag::note_protected_by_if_available; + if (VarDecl *Var = IS->getConditionVariable()) BuildScopeInformation(Var, ParentScope); // Cannot jump into the middle of the condition. unsigned NewParentScope = Scopes.size(); - Scopes.push_back(GotoScope(ParentScope, - diag::note_protected_by_constexpr_if, 0, - IS->getLocStart())); + Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getLocStart())); BuildScopeInformation(IS->getCond(), NewParentScope); // Jumps into either arm of an 'if constexpr' are not allowed. NewParentScope = Scopes.size(); - Scopes.push_back(GotoScope(ParentScope, - diag::note_protected_by_constexpr_if, 0, - IS->getLocStart())); + Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getLocStart())); BuildScopeInformation(IS->getThen(), NewParentScope); if (Stmt *Else = IS->getElse()) { NewParentScope = Scopes.size(); - Scopes.push_back(GotoScope(ParentScope, - diag::note_protected_by_constexpr_if, 0, - IS->getLocStart())); + Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getLocStart())); BuildScopeInformation(Else, NewParentScope); } return; Index: lib/Sema/ScopeInfo.cpp =================================================================== --- lib/Sema/ScopeInfo.cpp +++ lib/Sema/ScopeInfo.cpp @@ -29,6 +29,7 @@ HasIndirectGoto = false; HasDroppedStmt = false; HasOMPDeclareReductionCombiner = false; + HasPotentialAvailabilityViolations = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11847,6 +11847,9 @@ return nullptr; } + if (getCurFunction()->HasPotentialAvailabilityViolations && Body) + DiagnoseUnguardedAvailabilityViolations(dcl); + assert(!getCurFunction()->ObjCShouldCallSuper && "This should only be set for ObjC methods, which should have been " "handled in the block above."); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -21,6 +21,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/Mangle.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -6330,6 +6331,9 @@ break; case Sema::AD_Partial: + assert(!S.getCurFunctionOrMethodDecl() && + "Function-level partial availablity should not be diagnosed here!"); + diag = diag::warn_partial_availability; diag_message = diag::warn_partial_message; diag_fwdclass_message = diag::warn_partial_fwdclass_message; @@ -6507,3 +6511,139 @@ return std::max(DeclVersion, Context.getTargetInfo().getPlatformMinVersion()); } + +namespace { + +/// \brief This class implements -Wunguarded-availability. +class DiagnoseUnguardedAvailability + : public RecursiveASTVisitor { + typedef RecursiveASTVisitor Base; + + Sema &SemaRef; + + /// Stack of potentially nested 'if (@available(...))'s. + SmallVector AvailabilityStack; + + void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range); + +public: + DiagnoseUnguardedAvailability(Sema &SemaRef, VersionTuple BaseVersion) + : SemaRef(SemaRef) { + AvailabilityStack.push_back(BaseVersion); + } + + void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } + + bool VisitObjCMessageExpr(ObjCMessageExpr *ME); + bool VisitDeclRefExpr(DeclRefExpr *DRE); + + bool VisitTypeLoc(TypeLoc TL); + + bool TraverseIfStmt(IfStmt *If); +}; + +void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( + NamedDecl *D, SourceRange Range) { + + VersionTuple ContextVersion = AvailabilityStack.back(); + const ObjCPropertyDecl *ObjCPDecl; + Sema::AvailabilityDiagnostic AD; + + if (SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, false, ObjCPDecl, + ContextVersion, nullptr, AD)) { + // All other diagnostic kinds have already been handled. + if (AD != Sema::AD_Partial) + return; + + const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), D); + VersionTuple Introduced = AA->getIntroduced(); + + SemaRef.Diag(Range.getBegin(), diag::warn_unguarded_availability) + << Range << D + << AvailabilityAttr::getPrettyPlatformName( + SemaRef.getASTContext().getTargetInfo().getPlatformName()) + << Introduced.getAsString(); + + SemaRef.Diag(D->getLocation(), diag::note_availability_specified_here) + << D << /* partial */ 3; + + // FIXME: Replace this with a fixit diagnostic. + SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) + << Range << D; + } +} + +bool DiagnoseUnguardedAvailability::VisitObjCMessageExpr(ObjCMessageExpr *Msg) { + if (ObjCMethodDecl *D = Msg->getMethodDecl()) + DiagnoseDeclAvailability(D, {Msg->getSelectorStartLoc(), Msg->getLocEnd()}); + return true; +} + +bool DiagnoseUnguardedAvailability::VisitDeclRefExpr(DeclRefExpr *DRE) { + DiagnoseDeclAvailability(DRE->getDecl(), + {DRE->getLocStart(), DRE->getLocEnd()}); + return true; +} + +bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { + const Type *TyPtr = Ty.getTypePtr(); + SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; + + if (const TagType *TT = dyn_cast(TyPtr)) { + TagDecl *TD = TT->getDecl(); + DiagnoseDeclAvailability(TD, Range); + + } else if (const TypedefType *TD = dyn_cast(TyPtr)) { + TypedefNameDecl *D = TD->getDecl(); + DiagnoseDeclAvailability(D, Range); + + } else if (const auto *ObjCO = dyn_cast(TyPtr)) { + if (NamedDecl *D = ObjCO->getInterface()) + DiagnoseDeclAvailability(D, Range); + } + + return true; +} + +bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { + VersionTuple CondVersion; + if (auto *E = dyn_cast(If->getCond())) { + // If we're using the '*' case here, then we cannot emit any warnings for + // the 'then' branch. + if (!E->hasVersion()) + return TraverseStmt(If->getElse()); + + CondVersion = E->getVersion(); + } else { + // This isn't an availability checking 'if', we can just continue. + return Base::TraverseIfStmt(If); + } + + // This check is redundant; use the enclosing availability to check the + // branches. + if (CondVersion <= AvailabilityStack.back()) + return Base::TraverseStmt(If->getThen()) && + Base::TraverseStmt(If->getElse()); + + AvailabilityStack.push_back(CondVersion); + bool ShouldContinue = TraverseStmt(If->getThen()); + AvailabilityStack.pop_back(); + + return ShouldContinue && TraverseStmt(If->getElse()); +} + +} // end anonymous namespace + +void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { + Stmt *Body = nullptr; + + if (auto *FD = D->getAsFunction()) + Body = FD->getBody(); + else if (auto *MD = dyn_cast(D)) + Body = MD->getBody(); + + assert(Body && "Need a body here!"); + + VersionTuple BaseVersion = getVersionForDecl(D); + DiagnoseUnguardedAvailability(*this, BaseVersion).IssueDiagnostics(Body); +} Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -103,18 +103,13 @@ return false; } -static AvailabilityResult -DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, - const ObjCInterfaceDecl *UnknownObjCClass, - bool ObjCPropertyAccess) { - VersionTuple ContextVersion; - if (const DeclContext *DC = S.getCurObjCLexicalContext()) - ContextVersion = S.getVersionForDecl(cast(DC)); +bool Sema::ShouldDiagnoseAvailabilityOfDecl( + NamedDecl *&D, const ObjCInterfaceDecl *UnknownObjCClass, + bool ObjCPropertyAccess, ObjCPropertyDecl const *&ObjCPDecl, + VersionTuple ContextVersion, std::string *Message, + Sema::AvailabilityDiagnostic &AD) { - // See if this declaration is unavailable, deprecated, or partial in the - // current context. - std::string Message; - AvailabilityResult Result = D->getAvailability(&Message, ContextVersion); + AvailabilityResult Result = D->getAvailability(Message, ContextVersion); // For typedefs, if the typedef declaration appears available look // to the underlying type to see if it is more restrictive. @@ -122,18 +117,18 @@ if (Result == AR_Available) { if (const TagType *TT = TD->getUnderlyingType()->getAs()) { D = TT->getDecl(); - Result = D->getAvailability(&Message, ContextVersion); + Result = D->getAvailability(Message, ContextVersion); continue; } } break; } - + // Forward class declarations get their attributes from their definition. if (ObjCInterfaceDecl *IDecl = dyn_cast(D)) { if (IDecl->getDefinition()) { D = IDecl->getDefinition(); - Result = D->getAvailability(&Message, ContextVersion); + Result = D->getAvailability(Message, ContextVersion); } } @@ -141,10 +136,9 @@ if (Result == AR_Available) { const DeclContext *DC = ECD->getDeclContext(); if (const EnumDecl *TheEnumDecl = dyn_cast(DC)) - Result = TheEnumDecl->getAvailability(&Message, ContextVersion); + Result = TheEnumDecl->getAvailability(Message, ContextVersion); } - const ObjCPropertyDecl *ObjCPDecl = nullptr; if (Result == AR_Deprecated || Result == AR_Unavailable || Result == AR_NotYetIntroduced) { if (const ObjCMethodDecl *MD = dyn_cast(D)) { @@ -156,55 +150,78 @@ } } } - - switch (Result) { - case AR_Available: - break; - case AR_Deprecated: - if (S.getCurContextAvailability() != AR_Deprecated) - S.EmitAvailabilityWarning(Sema::AD_Deprecation, - D, Message, Loc, UnknownObjCClass, ObjCPDecl, - ObjCPropertyAccess); - break; + switch (Result) { + case AR_Available: + return false; - case AR_NotYetIntroduced: { - // Don't do this for enums, they can't be redeclared. - if (isa(D) || isa(D)) - break; - - bool Warn = !D->getAttr()->isInherited(); - // Objective-C method declarations in categories are not modelled as - // redeclarations, so manually look for a redeclaration in a category - // if necessary. - if (Warn && HasRedeclarationWithoutAvailabilityInCategory(D)) - Warn = false; - // In general, D will point to the most recent redeclaration. However, - // for `@class A;` decls, this isn't true -- manually go through the - // redecl chain in that case. - if (Warn && isa(D)) - for (Decl *Redecl = D->getMostRecentDecl(); Redecl && Warn; - Redecl = Redecl->getPreviousDecl()) - if (!Redecl->hasAttr() || - Redecl->getAttr()->isInherited()) - Warn = false; - - if (Warn) - S.EmitAvailabilityWarning(Sema::AD_Partial, D, Message, Loc, - UnknownObjCClass, ObjCPDecl, - ObjCPropertyAccess); - break; + case AR_Deprecated: + if (getCurContextAvailability() != AR_Deprecated) { + AD = Sema::AD_Deprecation; + return true; } + return false; - case AR_Unavailable: - if (S.getCurContextAvailability() != AR_Unavailable) - S.EmitAvailabilityWarning(Sema::AD_Unavailable, - D, Message, Loc, UnknownObjCClass, ObjCPDecl, - ObjCPropertyAccess); - break; + case AR_NotYetIntroduced: { + // Don't do this for enums, they can't be redeclared. + if (isa(D) || isa(D)) + return false; + + bool Warn = !D->getAttr()->isInherited(); + // Objective-C method declarations in categories are not modelled as + // redeclarations, so manually look for a redeclaration in a category + // if necessary. + if (Warn && HasRedeclarationWithoutAvailabilityInCategory(D)) + Warn = false; + // In general, D will point to the most recent redeclaration. However, + // for `@class A;` decls, this isn't true -- manually go through the + // redecl chain in that case. + if (Warn && isa(D)) + for (Decl *Redecl = D->getMostRecentDecl(); Redecl && Warn; + Redecl = Redecl->getPreviousDecl()) + if (!Redecl->hasAttr() || + Redecl->getAttr()->isInherited()) + Warn = false; + if (Warn) { + AD = Sema::AD_Partial; + return true; } - return Result; + return false; + } + case AR_Unavailable: + if (getCurContextAvailability() != AR_Unavailable) { + AD = Sema::AD_Unavailable; + return true; + } + + return false; + } +} + +static void +DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, + const ObjCInterfaceDecl *UnknownObjCClass, + bool ObjCPropertyAccess) { + VersionTuple ContextVersion; + if (const DeclContext *DC = S.getCurObjCLexicalContext()) + ContextVersion = S.getVersionForDecl(cast(DC)); + + std::string Message; + const ObjCPropertyDecl *ObjCPDecl = nullptr; + + // See if this declaration is unavailable, deprecated, or partial in the + // current context. + Sema::AvailabilityDiagnostic DiagKind; + if (S.ShouldDiagnoseAvailabilityOfDecl(D, UnknownObjCClass, + ObjCPropertyAccess, ObjCPDecl, + ContextVersion, &Message, DiagKind)) { + if (DiagKind == Sema::AD_Partial && S.getCurFunctionOrMethodDecl()) + S.getCurFunction()->HasPotentialAvailabilityViolations = true; + else + S.EmitAvailabilityWarning(DiagKind, D, Message, Loc, UnknownObjCClass, + ObjCPDecl, ObjCPropertyAccess); + } } /// \brief Emit a note explaining that this function is deleted. @@ -15176,11 +15193,6 @@ VersionTuple Version; if (Spec != AvailSpecs.end()) Version = Spec->getVersion(); - else - // This is the '*' case in @available. We should diagnose this; the - // programmer should explicitly account for this case if they target this - // platform. - Diag(AtLoc, diag::warn_available_using_star_case) << RParen << Platform; return new (Context) ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy); Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -536,7 +536,7 @@ if (Cond.isInvalid()) return StmtError(); - if (IsConstexpr) + if (IsConstexpr || isa(Cond.get().second)) getCurFunction()->setHasBranchProtectedScope(); DiagnoseUnusedExprResult(thenStmt); Index: test/Parser/objc-available.m =================================================================== --- test/Parser/objc-available.m +++ test/Parser/objc-available.m @@ -15,8 +15,6 @@ (void)@available(erik_os 10.10, hat_os 1.0, *); // expected-error 2 {{unrecognized platform name}} - (void)@available(ios 8, *); // expected-warning{{using '*' case here, platform macos is not accounted for}} - (void)@available(); // expected-error{{expected a platform name here}} (void)@available(macos 10.10,); // expected-error{{expected a platform name here}} (void)@available(macos); // expected-error{{expected a version}} Index: test/Sema/attr-availability.c =================================================================== --- test/Sema/attr-availability.c +++ test/Sema/attr-availability.c @@ -30,7 +30,7 @@ ATSFontGetPostScriptName(100); // expected-error {{'ATSFontGetPostScriptName' is unavailable: obsoleted in macOS 9.0 - use ATSFontGetFullPostScriptName}} #if defined(WARN_PARTIAL) - // expected-warning@+2 {{is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'PartiallyAvailable' to silence this warning}} + // expected-warning@+2 {{is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'PartiallyAvailable' in an @available check to silence this warning}} #endif PartiallyAvailable(); } Index: test/SemaObjC/attr-availability.m =================================================================== --- test/SemaObjC/attr-availability.m +++ test/SemaObjC/attr-availability.m @@ -46,16 +46,16 @@ [b proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in macOS 10.2}} #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'partialMethod' is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'partialMethod' to silence this warning}} + // expected-warning@+2 {{'partialMethod' is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'partialMethod' in an @available check to silence this warning}} #endif [a partialMethod]; [b partialMethod]; // no warning #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'partial_proto_method' is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'partial_proto_method' to silence this warning}} + // expected-warning@+2 {{'partial_proto_method' is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'partial_proto_method' in an @available check to silence this warning}} #endif [a partial_proto_method]; #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'partial_proto_method' is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'partial_proto_method' to silence this warning}} + // expected-warning@+2 {{'partial_proto_method' is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'partial_proto_method' in an @available check to silence this warning}} #endif [b partial_proto_method]; } @@ -163,14 +163,14 @@ [a partialMethod]; // no warning [a ipartialMethod1]; // no warning #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'ipartialMethod2' is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'ipartialMethod2' to silence this warning}} + // expected-warning@+2 {{'ipartialMethod2' is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'ipartialMethod2' in an @available check to silence this warning}} #endif [a ipartialMethod2]; [a ppartialMethod]; // no warning [PartialI partialMethod]; // no warning [PartialI ipartialMethod1]; // no warning #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'ipartialMethod2' is partial: introduced in macOS 10.8}} expected-note@+2 {{explicitly redeclare 'ipartialMethod2' to silence this warning}} + // expected-warning@+2 {{'ipartialMethod2' is only available on macOS 10.8 or newer}} expected-note@+2 {{enclose 'ipartialMethod2' in an @available check to silence this warning}} #endif [PartialI ipartialMethod2]; [PartialI ppartialMethod]; // no warning @@ -309,7 +309,7 @@ @end @implementation LookupAvailabilityBase --(void)method1 { fn_10_7(); } // expected-warning{{partial}} expected-note{{explicitly redeclare}} +-(void)method1 { fn_10_7(); } // expected-warning{{only available on macOS 10.7}} expected-note{{@available}} @end __attribute__((availability(macosx, introduced=10.7))) @@ -321,7 +321,7 @@ @implementation LookupAvailability -(void)method2 { fn_10_7(); } --(void)method3 { fn_10_8(); } // expected-warning{{partial}} expected-note{{explicitly redeclare}} +-(void)method3 { fn_10_8(); } // expected-warning{{only available on macOS 10.8}} expected-note{{@available}} -(void)method4 { fn_10_8(); } @end Index: test/SemaObjC/property-deprecated-warning.m =================================================================== --- test/SemaObjC/property-deprecated-warning.m +++ test/SemaObjC/property-deprecated-warning.m @@ -9,7 +9,7 @@ @property(nonatomic,assign) id ptarget __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'ptarget' is declared deprecated here}} expected-note {{'ptarget' has been explicitly marked deprecated here}} #if defined(WARN_PARTIAL) -// expected-note@+2 {{property 'partialPtarget' is declared partial here}} expected-note@+2 {{'partialPtarget' has been explicitly marked partial here}} +// expected-note@+2 {{'partialPtarget' has been explicitly marked partial here}} #endif @property(nonatomic,assign) id partialPtarget __attribute__((availability(ios,introduced=5.0))); @end @@ -24,7 +24,7 @@ @property(nonatomic,assign) id target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'target' is declared deprecated here}} expected-note {{'setTarget:' has been explicitly marked deprecated here}} #if defined(WARN_PARTIAL) -// expected-note@+2 {{property 'partialTarget' is declared partial here}} expected-note@+2 {{'setPartialTarget:' has been explicitly marked partial here}} +// expected-note@+2 {{'setPartialTarget:' has been explicitly marked partial here}} #endif @property(nonatomic,assign) id partialTarget __attribute__((availability(ios,introduced=5.0))); @end @@ -40,7 +40,7 @@ // expected-note 2 {{'setDep_target:' has been explicitly marked deprecated here}} #if defined(WARN_PARTIAL) -// expected-note@+2 4 {{property 'partial_dep_target' is declared partial here}} expected-note@+2 2 {{'partial_dep_target' has been explicitly marked partial here}} expected-note@+2 2 {{'setPartial_dep_target:' has been explicitly marked partial here}} +// expected-note@+2 2 {{'partial_dep_target' has been explicitly marked partial here}} expected-note@+2 2 {{'setPartial_dep_target:' has been explicitly marked partial here}} #endif @property(nonatomic,assign) id partial_dep_target __attribute__((availability(ios,introduced=5.0))); @end @@ -57,7 +57,7 @@ [self setPtarget: (id)0]; // no-warning [self setPartialTarget: (id)0]; // no-warning #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'partial_dep_target' is partial: introduced in iOS 5.0}} expected-warning@+2 {{'setPartial_dep_target:' is partial: introduced in iOS 5.0}} expected-note@+2 {{explicitly redeclare 'partial_dep_target' to silence this warning}} expected-note@+2 {{explicitly redeclare 'setPartial_dep_target:' to silence this warning}} + // expected-warning@+2 {{'partial_dep_target' is only available on iOS 5.0 or newer}} expected-warning@+2 {{'setPartial_dep_target:' is only available on iOS 5.0 or newer}} expected-note@+2 {{enclose 'partial_dep_target' in an @available check to silence this warning}} expected-note@+2 {{enclose 'setPartial_dep_target:' in an @available check to silence this warning}} #endif [self setPartial_dep_target: [self partial_dep_target]]; @@ -82,11 +82,11 @@ [self setPtarget: (id)0]; // no-warning #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'setPartialTarget:' is partial: introduced in iOS 5.0}} expected-note@+2 {{explicitly redeclare 'setPartialTarget:' to silence this warning}} + // expected-warning@+2 {{'setPartialTarget:' is only available on iOS 5.0 or newer}} expected-note@+2 {{enclose 'setPartialTarget:' in an @available check to silence this warning}} #endif [self setPartialTarget: (id)0]; #if defined(WARN_PARTIAL) - // expected-warning@+2 {{'partial_dep_target' is partial: introduced in iOS 5.0}} expected-warning@+2 {{'setPartial_dep_target:' is partial: introduced in iOS 5.0}} expected-note@+2 {{explicitly redeclare 'partial_dep_target' to silence this warning}} expected-note@+2 {{explicitly redeclare 'setPartial_dep_target:' to silence this warning}} + // expected-warning@+2 {{'partial_dep_target' is only available on iOS 5.0 or newer}} expected-warning@+2 {{'setPartial_dep_target:' is only available on iOS 5.0 or newer}} expected-note@+2 {{enclose 'partial_dep_target' in an @available check to silence this warning}} expected-note@+2 {{enclose 'setPartial_dep_target:' in an @available check to silence this warning}} #endif [self setPartial_dep_target: [self partial_dep_target]]; [self setPartialPtarget: (id)0]; // no-warning @@ -100,12 +100,12 @@ @property(setter=setNewDelegate:,assign) id delegate __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{'setNewDelegate:' has been explicitly marked deprecated here}} expected-note {{property 'delegate' is declared deprecated here}} #if defined(WARN_PARTIAL) -// expected-note@+2 {{property 'partialEnabled' is declared partial here}} expected-note@+2 {{'partialIsEnabled' has been explicitly marked partial here}} +// expected-note@+2 {{'partialIsEnabled' has been explicitly marked partial here}} #endif @property(getter=partialIsEnabled,assign) BOOL partialEnabled __attribute__((availability(ios,introduced=5.0))); #if defined(WARN_PARTIAL) -// expected-note@+2 {{property 'partialDelegate' is declared partial here}} expected-note@+2 {{'partialSetNewDelegate:' has been explicitly marked partial here}} +// expected-note@+2 {{'partialSetNewDelegate:' has been explicitly marked partial here}} #endif @property(setter=partialSetNewDelegate:,assign) id partialDelegate __attribute__((availability(ios,introduced=5.0))); @end @@ -115,7 +115,7 @@ [obj setNewDelegate:0]; // expected-warning {{'setNewDelegate:' is deprecated: first deprecated in iOS 3.0}} #if defined(WARN_PARTIAL) -// expected-warning@+2 {{'partialIsEnabled' is partial: introduced in iOS 5.0}} expected-warning@+3 {{'partialSetNewDelegate:' is partial: introduced in iOS 5.0}} expected-note@+2 {{explicitly redeclare 'partialIsEnabled' to silence this warning}} expected-note@+3 {{explicitly redeclare 'partialSetNewDelegate:' to silence this warning}} + // expected-warning@+2 {{'partialIsEnabled' is only available on iOS 5.0 or newer}} expected-warning@+3 {{'partialSetNewDelegate:' is only available on iOS 5.0 or newer}} expected-note@+2 {{enclose 'partialIsEnabled' in an @available check to silence this warning}} expected-note@+3 {{enclose 'partialSetNewDelegate:' in an @available check to silence this warning}} #endif if ([obj partialIsEnabled]) [obj partialSetNewDelegate:0]; @@ -138,7 +138,7 @@ if (flag) return [obj partialPtarget]; // no-warning #if defined(WARN_PARTIAL) -// expected-warning@+2 {{'partialPtarget' is partial: introduced in iOS 5.0}} expected-note@+2 {{explicitly redeclare 'partialPtarget' to silence this warning}} +// expected-warning@+2 {{'partialPtarget' is only available on iOS 5.0 or newer}} expected-note@+2 {{enclose 'partialPtarget' in an @available check to silence this warning}} #endif return [obj2 partialPtarget]; } Index: test/SemaObjC/unguarded-availability.m =================================================================== --- test/SemaObjC/unguarded-availability.m +++ test/SemaObjC/unguarded-availability.m @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx-10.9 -Wunguarded-availability -fsyntax-only -verify %s +// RUN: %clang_cc1 -xobjective-c++ -DOBJCPP -triple x86_64-apple-macosx-10.9 -Wunguarded-availability -fsyntax-only -verify %s + +#define AVAILABLE_10_11 __attribute__((availability(macos, introduced = 10.11))) +#define AVAILABLE_10_12 __attribute__((availability(macos, introduced = 10.12))) + +int func_10_11() AVAILABLE_10_11; // expected-note 3 {{'func_10_11' has been explicitly marked partial here}} + +int func_10_12() AVAILABLE_10_12; // expected-note 2 {{'func_10_12' has been explicitly marked partial here}} + +void use_func() { + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} + + if (@available(macos 10.11, *)) + func_10_11(); + else + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +} + +void defn_10_11() AVAILABLE_10_11; + +void defn_10_11() { + func_10_11(); +} + +void nested_ifs() { + if (@available(macos 10.12, *)) { + if (@available(macos 10.10, *)) { + func_10_12(); + } else { + func_10_12(); + } + } else { + func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{enclose 'func_10_12' in an @available check to silence this warning}} + } +} + +void star_case() { + if (@available(ios 9, *)) + func_10_11(); + else + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +} + +typedef int int_10_11 AVAILABLE_10_11; // expected-note {{'int_10_11' has been explicitly marked partial here}} +typedef int int_10_12 AVAILABLE_10_12; // expected-note {{'int_10_12' has been explicitly marked partial here}} + +void use_typedef() { + int_10_11 x; // expected-warning{{'int_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'int_10_11' in an @available check to silence this warning}} +} + +__attribute__((objc_root_class)) +AVAILABLE_10_11 @interface Class_10_11 { + int_10_11 foo; + int_10_12 bar; // expected-warning {{'int_10_12' is partial: introduced in macOS 10.12}} expected-note{{redeclare}} +} +- (void)method1; +- (void)method2; +@end + +@implementation Class_10_11 +- (void) method1 { + func_10_11(); + func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{enclose 'func_10_12' in an @available check to silence this warning}} +} + +- (void)method2 AVAILABLE_10_12 { + func_10_12(); +} + +@end + +int protected_scope() { + if (@available(macos 10.20, *)) { // expected-note 2 {{jump enters controlled statement of if available}} + label1: + return 0; + } else { + label2: + goto label1; // expected-error{{cannot jump from this goto statement to its label}} + } + + goto label2; // expected-error{{cannot jump from this goto statement to its label}} +} + +#ifdef OBJCPP + +int f(char) __attribute__((availability(macos, introduced=10.12))); // expected-note {{'f' has been explicitly marked partial here}} +int f(int); + +template int use_f() { + return f(T()); // expected-warning{{'f' is only available on macOS 10.12 or newer}} expected-note{{enclose}} +} + +int a = use_f(); +int b = use_f(); // expected-note{{in instantiation}} + +#endif