diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1882,6 +1882,10 @@ /// Retrieve the current captured region, if any. sema::CapturedRegionScopeInfo *getCurCapturedRegion(); + /// Retrieve the current function, if any, that should be analyzed for + /// potential availability violations. + sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); + /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls SmallVectorImpl &WeakTopLevelDecls() { return WeakTopLevelDecl; } diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -666,13 +666,6 @@ SemaRef.Context.getTargetInfo().getPlatformMinVersion()); } - bool TraverseDecl(Decl *D) { - // Avoid visiting nested functions to prevent duplicate warnings. - if (!D || isa(D)) - return true; - return Base::TraverseDecl(D); - } - bool TraverseStmt(Stmt *S) { if (!S) return true; @@ -686,8 +679,6 @@ bool TraverseIfStmt(IfStmt *If); - bool TraverseLambdaExpr(LambdaExpr *E) { return true; } - // for 'case X:' statements, don't bother looking at the 'X'; it can't lead // to any useful diagnostics. bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } @@ -919,6 +910,17 @@ DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); } +FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { + if (FunctionScopes.empty()) + return nullptr; + + // Conservatively search the entire current function scope context for + // availability violations. This ensures we always correctly analyze nested + // classes, blocks, lambdas, etc. that may or may not be inside if(@available) + // checks themselves. + return FunctionScopes.front(); +} + void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, @@ -941,11 +943,8 @@ // 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 (getCurFunctionOrMethodDecl()) { - getEnclosingFunction()->HasPotentialAvailabilityViolations = true; - return; - } else if (getCurBlock() || getCurLambda()) { - getCurFunction()->HasPotentialAvailabilityViolations = true; + if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { + Context->HasPotentialAvailabilityViolations = true; return; } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19650,12 +19650,10 @@ if (Spec != AvailSpecs.end()) Version = Spec->getVersion(); - // The use of `@available` in the enclosing function should be analyzed to + // The use of `@available` in the enclosing context should be analyzed to // warn when it's used inappropriately (i.e. not if(@available)). - if (getCurFunctionOrMethodDecl()) - getEnclosingFunction()->HasPotentialAvailabilityViolations = true; - else if (getCurBlock() || getCurLambda()) - getCurFunction()->HasPotentialAvailabilityViolations = true; + if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) + Context->HasPotentialAvailabilityViolations = true; return new (Context) ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy); diff --git a/clang/test/SemaObjC/unguarded-availability.m b/clang/test/SemaObjC/unguarded-availability.m --- a/clang/test/SemaObjC/unguarded-availability.m +++ b/clang/test/SemaObjC/unguarded-availability.m @@ -125,6 +125,14 @@ (void) ^{ func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{@available}} }; + + if (@available(macos 10.12, *)) + (void) ^{ + func_10_12(); + (void) ^{ + func_10_12(); + }; + }; } void test_params(int_10_12 x); // expected-warning {{'int_10_12' is only available on macOS 10.12 or newer}} expected-note{{annotate 'test_params' with an availability attribute to silence this warning}} @@ -233,6 +241,36 @@ (void)(^ { func_10_12(); // expected-warning{{'func_10_12' is only available on macOS 10.12 or newer}} expected-note{{@available}} }); + + if (@available(macos 10.12, *)) { + void([]() { + func_10_12(); + void([] () { + func_10_12(); + }); + struct DontWarn { + void f() { + func_10_12(); + } + }; + }); + } + + if (@available(macos 10.12, *)) { + struct DontWarn { + void f() { + func_10_12(); + void([] () { + func_10_12(); + }); + struct DontWarn2 { + void f() { + func_10_12(); + } + }; + } + }; + } } #endif @@ -259,9 +297,14 @@ @end void with_local_struct() { - struct local { // expected-note{{annotate 'local' with an availability attribute}} - new_int x; // expected-warning{{'new_int' is only available}} + struct local { + new_int x; // expected-warning{{'new_int' is only available}} expected-note{{enclose 'new_int' in an @available check}} }; + if (@available(macos 10.12, *)) { + struct DontWarn { + new_int x; + }; + } } // rdar://33156429: