Index: cfe/trunk/include/clang/AST/Stmt.h =================================================================== --- cfe/trunk/include/clang/AST/Stmt.h +++ cfe/trunk/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: cfe/trunk/include/clang/Basic/DiagnosticGroups.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td +++ cfe/trunk/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: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -2656,8 +2656,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< @@ -4279,15 +4291,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 : @@ -4738,6 +4741,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: cfe/trunk/include/clang/Sema/ScopeInfo.h =================================================================== --- cfe/trunk/include/clang/Sema/ScopeInfo.h +++ cfe/trunk/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: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -3639,6 +3639,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. Index: cfe/trunk/lib/AST/Stmt.cpp =================================================================== --- cfe/trunk/lib/AST/Stmt.cpp +++ cfe/trunk/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: cfe/trunk/lib/Sema/JumpDiagnostics.cpp =================================================================== --- cfe/trunk/lib/Sema/JumpDiagnostics.cpp +++ cfe/trunk/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: cfe/trunk/lib/Sema/ScopeInfo.cpp =================================================================== --- cfe/trunk/lib/Sema/ScopeInfo.cpp +++ cfe/trunk/lib/Sema/ScopeInfo.cpp @@ -29,6 +29,7 @@ HasIndirectGoto = false; HasDroppedStmt = false; HasOMPDeclareReductionCombiner = false; + HasPotentialAvailabilityViolations = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -11736,6 +11736,9 @@ return nullptr; } + if (Body && getCurFunction()->HasPotentialAvailabilityViolations) + DiagnoseUnguardedAvailabilityViolations(dcl); + assert(!getCurFunction()->ObjCShouldCallSuper && "This should only be set for ObjC methods, which should have been " "handled in the block above."); Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/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 AR_NotYetIntroduced: + 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; @@ -6511,3 +6515,146 @@ return std::max(DeclVersion, Context.getTargetInfo().getPlatformMinVersion()); } + +namespace { + +/// \brief This class implements -Wunguarded-availability. +/// +/// This is done with a traversal of the AST of a function that makes reference +/// to a partially available declaration. Whenever we encounter an \c if of the +/// form: \c if(@available(...)), we use the version from the condition to visit +/// the then statement. +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 TraverseIfStmt(IfStmt *If); + + bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { + if (ObjCMethodDecl *D = Msg->getMethodDecl()) + DiagnoseDeclAvailability( + D, SourceRange(Msg->getSelectorStartLoc(), Msg->getLocEnd())); + return true; + } + + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + DiagnoseDeclAvailability(DRE->getDecl(), + SourceRange(DRE->getLocStart(), DRE->getLocEnd())); + return true; + } + + bool VisitMemberExpr(MemberExpr *ME) { + DiagnoseDeclAvailability(ME->getMemberDecl(), + SourceRange(ME->getLocStart(), ME->getLocEnd())); + return true; + } + + bool VisitTypeLoc(TypeLoc Ty); +}; + +void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( + NamedDecl *D, SourceRange Range) { + + VersionTuple ContextVersion = AvailabilityStack.back(); + if (AvailabilityResult Result = SemaRef.ShouldDiagnoseAvailabilityOfDecl( + D, ContextVersion, nullptr)) { + // All other diagnostic kinds have already been handled in + // DiagnoseAvailabilityOfDecl. + if (Result != AR_NotYetIntroduced) + 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::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())) { + CondVersion = 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()) + return Base::TraverseStmt(If->getThen()) && + Base::TraverseStmt(If->getElse()); + } else { + // This isn't an availability checking 'if', we can just continue. + return Base::TraverseIfStmt(If); + } + + 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()) { + // FIXME: We only examine the pattern decl for availability violations now, + // but we should also examine instantiated templates. + if (FD->isTemplateInstantiation()) + return; + + 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: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -184,6 +184,11 @@ if (AvailabilityResult Result = S.ShouldDiagnoseAvailabilityOfDecl(D, ContextVersion, &Message)) { + if (Result == AR_NotYetIntroduced && S.getCurFunctionOrMethodDecl()) { + S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true; + return; + } + const ObjCPropertyDecl *ObjCPDecl = nullptr; if (const ObjCMethodDecl *MD = dyn_cast(D)) { if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { @@ -15198,11 +15203,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: cfe/trunk/lib/Sema/SemaStmt.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp +++ cfe/trunk/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: cfe/trunk/test/Parser/objc-available.m =================================================================== --- cfe/trunk/test/Parser/objc-available.m +++ cfe/trunk/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: cfe/trunk/test/Sema/attr-availability.c =================================================================== --- cfe/trunk/test/Sema/attr-availability.c +++ cfe/trunk/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: cfe/trunk/test/SemaObjC/attr-availability.m =================================================================== --- cfe/trunk/test/SemaObjC/attr-availability.m +++ cfe/trunk/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: cfe/trunk/test/SemaObjC/property-deprecated-warning.m =================================================================== --- cfe/trunk/test/SemaObjC/property-deprecated-warning.m +++ cfe/trunk/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: cfe/trunk/test/SemaObjC/unguarded-availability.m =================================================================== --- cfe/trunk/test/SemaObjC/unguarded-availability.m +++ cfe/trunk/test/SemaObjC/unguarded-availability.m @@ -0,0 +1,180 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx-10.9 -Wunguarded-availability -fblocks -fsyntax-only -verify %s +// RUN: %clang_cc1 -xobjective-c++ -DOBJCPP -triple x86_64-apple-macosx-10.9 -Wunguarded-availability -fblocks -fsyntax-only -verify %s + +#define AVAILABLE_10_0 __attribute__((availability(macos, introduced = 10.0))) +#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 4 {{'func_10_11' has been explicitly marked partial here}} + +#ifdef OBJCPP +// expected-note@+2 {{marked partial here}} +#endif +int func_10_12() AVAILABLE_10_12; // expected-note 5 {{'func_10_12' has been explicitly marked partial here}} + +int func_10_0() AVAILABLE_10_0; + +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(); // 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}} + func_10_0(); + } 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}} + + if (@available(macos 10.11, *)) { + if (@available(ios 8, *)) { + func_10_11(); + func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{enclose}} + } else { + func_10_11(); + func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{enclose}} + } + } +} + +typedef int int_10_11 AVAILABLE_10_11; // expected-note {{'int_10_11' has been explicitly marked partial here}} +#ifdef OBJCPP +// expected-note@+2 {{marked partial here}} +#endif +typedef int int_10_12 AVAILABLE_10_12; // expected-note 3 {{'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}} +} + +struct S { + int m1; + int m2 __attribute__((availability(macos, introduced = 10.12))); // expected-note{{marked partial here}} + + struct Nested { + int nested_member __attribute__((availability(macos, introduced = 10.12))); // expected-note{{marked partial here}} + } n; +}; + +int test_members() { + struct S s; + (void)s.m1; + (void)s.m2; // expected-warning{{'m2' is only available on macOS 10.12 or newer}} expected-note{{@available}} + + (void)s.n.nested_member; // expected-warning{{'nested_member' is only available on macOS 10.12 or newer}} expected-note{{@available}} +} + +void test_blocks() { + (void) ^{ + func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{@available}} + }; +} + +void test_params(int_10_12 x); // expected-warning {{'int_10_12' is partial: introduced in macOS 10.12}} expected-note{{redeclare}} + +// FIXME: This should be fine! +void test_params2(int_10_12 x) AVAILABLE_10_12; // expected-warning {{'int_10_12' is partial: introduced in macOS 10.12}} expected-note{{redeclare}} + +#ifdef OBJCPP + +int f(char) AVAILABLE_10_12; +int f(int); + +template int use_f() { + // FIXME: We should warn here! + return f(T()); +} + +int a = use_f(); +int b = use_f(); + +template int use_at_available() { + if (@available(macos 10.12, *)) + return func_10_12(); + else + return func_10_12(); // expected-warning {{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{enclose}} +} + +int instantiate_template() { + if (@available(macos 10.12, *)) { + use_at_available(); + } else { + use_at_available(); + } +} + +template +int with_availability_attr() AVAILABLE_10_11 { // expected-note 2 {{marked partial here}} + return 0; +} + +int instantiate_with_availability_attr() { + if (@available(macos 10.12, *)) + with_availability_attr(); + else + with_availability_attr(); // expected-warning {{'with_availability_attr' is only available on macOS 10.11 or newer}} expected-note {{enclose}} +} + +int instantiate_availability() { + if (@available(macos 10.12, *)) + with_availability_attr(); + else + with_availability_attr(); // expected-warning{{'with_availability_attr' is only available on macOS 10.11 or newer}} expected-warning{{'int_10_12' is only available on macOS 10.12 or newer}} expected-note 2 {{enclose}} +} + +#endif