diag.Triggered = true;
}
+static bool isDeclDeprecated(Decl *D) {
+ do {
+ if (D->isDeprecated())
+ return true;
+ // A category implicitly has the availability of the interface.
+ if (const ObjCCategoryDecl *CatD = dyn_cast<ObjCCategoryDecl>(D))
+ if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
+ return Interface->isDeprecated();
+ } while ((D = cast_or_null<Decl>(D->getDeclContext())));
+ return false;
+}
+
+static bool isDeclUnavailable(Decl *D) {
+ do {
+ if (D->isUnavailable())
+ return true;
+ // A category implicitly has the availability of the interface.
+ if (const ObjCCategoryDecl *CatD = dyn_cast<ObjCCategoryDecl>(D))
+ if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
+ return Interface->isUnavailable();
+ } while ((D = cast_or_null<Decl>(D->getDeclContext())));
+ return false;
+}
+
static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
const Decl *D) {
// Check each AvailabilityAttr to find the one for this platform.
return nullptr;
}
-/// \brief whether we should emit a diagnostic for \c K and \c DeclVersion in
-/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
-/// in a deprecated context, but not the other way around.
-static bool ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
- VersionTuple DeclVersion,
- Decl *Ctx) {
- assert(K != AR_Available && "Expected an unavailable declaration here!");
-
- // Checks if we should emit the availability diagnostic in the context of C.
- auto CheckContext = [&](const Decl *C) {
- if (K == AR_NotYetIntroduced) {
- if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
- if (AA->getIntroduced() >= DeclVersion)
- return true;
- } else if (K == AR_Deprecated)
- if (C->isDeprecated())
- return true;
-
- if (C->isUnavailable())
- return true;
- return false;
- };
-
- do {
- if (CheckContext(Ctx))
- return false;
-
- // An implementation implicitly has the availability of the interface.
- if (auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
- if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
- if (CheckContext(Interface))
- return false;
- }
- // A category implicitly has the availability of the interface.
- else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
- if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
- if (CheckContext(Interface))
- return false;
- } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
-
- return true;
-}
-
static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
Decl *Ctx, const NamedDecl *D,
StringRef Message, SourceLocation Loc,
// Matches diag::note_availability_specified_here.
unsigned available_here_select_kind;
- VersionTuple DeclVersion;
- if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D))
- DeclVersion = AA->getIntroduced();
-
- if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx))
- return;
-
+ // Don't warn if our current context is deprecated or unavailable.
switch (K) {
case AR_Deprecated:
+ if (isDeclDeprecated(Ctx) || isDeclUnavailable(Ctx))
+ return;
diag = !ObjCPropertyAccess ? diag::warn_deprecated
: diag::warn_property_method_deprecated;
diag_message = diag::warn_deprecated_message;
break;
case AR_Unavailable:
+ if (isDeclUnavailable(Ctx))
+ return;
diag = !ObjCPropertyAccess ? diag::err_unavailable
: diag::err_property_method_unavailable;
diag_message = diag::err_unavailable_message;
ObjCProperty, ObjCPropertyAccess);
}
+VersionTuple Sema::getVersionForDecl(const Decl *D) const {
+ assert(D && "Expected a declaration here!");
+
+ VersionTuple DeclVersion;
+ if (const auto *AA = getAttrForPlatform(getASTContext(), D))
+ DeclVersion = AA->getIntroduced();
+
+ const ObjCInterfaceDecl *Interface = nullptr;
+
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ Interface = MD->getClassInterface();
+ else if (const auto *ID = dyn_cast<ObjCImplementationDecl>(D))
+ Interface = ID->getClassInterface();
+
+ if (Interface) {
+ if (const auto *AA = getAttrForPlatform(getASTContext(), Interface))
+ if (AA->getIntroduced() > DeclVersion)
+ DeclVersion = AA->getIntroduced();
+ }
+
+ return std::max(DeclVersion, Context.getTargetInfo().getPlatformMinVersion());
+}
+
namespace {
/// \brief This class implements -Wunguarded-availability.
typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
Sema &SemaRef;
- Decl *Ctx;
/// Stack of potentially nested 'if (@available(...))'s.
SmallVector<VersionTuple, 8> AvailabilityStack;
void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range);
public:
- DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
- : SemaRef(SemaRef), Ctx(Ctx) {
- AvailabilityStack.push_back(
- SemaRef.Context.getTargetInfo().getPlatformMinVersion());
+ DiagnoseUnguardedAvailability(Sema &SemaRef, VersionTuple BaseVersion)
+ : SemaRef(SemaRef) {
+ AvailabilityStack.push_back(BaseVersion);
}
void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
NamedDecl *D, SourceRange Range) {
VersionTuple ContextVersion = AvailabilityStack.back();
- if (AvailabilityResult Result =
- SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) {
+ if (AvailabilityResult Result = SemaRef.ShouldDiagnoseAvailabilityOfDecl(
+ D, ContextVersion, nullptr)) {
// All other diagnostic kinds have already been handled in
// DiagnoseAvailabilityOfDecl.
if (Result != AR_NotYetIntroduced)
const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), D);
VersionTuple Introduced = AA->getIntroduced();
- if (ContextVersion >= Introduced)
- return;
-
- // If the context of this function is less available than D, we should not
- // emit a diagnostic.
- if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx))
- return;
-
SemaRef.Diag(Range.getBegin(), diag::warn_unguarded_availability)
<< Range << D
<< AvailabilityAttr::getPrettyPlatformName(
assert(Body && "Need a body here!");
- DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
+ VersionTuple BaseVersion = getVersionForDecl(D);
+ DiagnoseUnguardedAvailability(*this, BaseVersion).IssueDiagnostics(Body);
}
return false;
}
-AvailabilityResult
-Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) {
- AvailabilityResult Result = D->getAvailability(Message);
+AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
+ NamedDecl *&D, VersionTuple ContextVersion, std::string *Message) {
+ 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.
if (Result == AR_Available) {
if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) {
D = TT->getDecl();
- Result = D->getAvailability(Message);
+ Result = D->getAvailability(Message, ContextVersion);
continue;
}
}
if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
if (IDecl->getDefinition()) {
D = IDecl->getDefinition();
- Result = D->getAvailability(Message);
+ Result = D->getAvailability(Message, ContextVersion);
}
}
if (Result == AR_Available) {
const DeclContext *DC = ECD->getDeclContext();
if (const EnumDecl *TheEnumDecl = dyn_cast<EnumDecl>(DC))
- Result = TheEnumDecl->getAvailability(Message);
+ Result = TheEnumDecl->getAvailability(Message, ContextVersion);
}
- if (Result == AR_NotYetIntroduced) {
+ switch (Result) {
+ case AR_Available:
+ return Result;
+
+ case AR_Unavailable:
+ case AR_Deprecated:
+ return getCurContextAvailability() != Result ? Result : AR_Available;
+
+ case AR_NotYetIntroduced: {
// Don't do this for enums, they can't be redeclared.
if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D))
return AR_Available;
return Warn ? AR_NotYetIntroduced : AR_Available;
}
-
- return Result;
+ }
+ llvm_unreachable("Unknown availability result!");
}
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<Decl>(DC));
+
std::string Message;
- // See if this declaration is unavailable, deprecated, or partial.
+ // See if this declaration is unavailable, deprecated, or partial in the
+ // current context.
if (AvailabilityResult Result =
- S.ShouldDiagnoseAvailabilityOfDecl(D, &Message)) {
+ S.ShouldDiagnoseAvailabilityOfDecl(D, ContextVersion, &Message)) {
if (Result == AR_NotYetIntroduced && S.getCurFunctionOrMethodDecl()) {
S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
const ObjCPropertyDecl *ObjCPDecl = nullptr;
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
- AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
+ AvailabilityResult PDeclResult =
+ PD->getAvailability(nullptr, ContextVersion);
if (PDeclResult == Result)
ObjCPDecl = PD;
}