Reapply r284265: "[Sema] Refactor context checking for availability diagnostics"

The problem with the original commit was that some of Apple's headers depended
on an incorrect behaviour, this commit adds a temporary workaround until those
headers are fixed.

llvm-svn: 285098
This commit is contained in:
Erik Pilkington 2016-10-25 19:05:50 +00:00
parent c62266d680
commit f35114c543
5 changed files with 128 additions and 123 deletions

View File

@ -9909,23 +9909,16 @@ public:
return OriginalLexicalContext ? OriginalLexicalContext : CurContext;
}
AvailabilityResult getCurContextAvailability() const;
/// \brief Get the verison that this context implies.
/// For instance, a method in an interface that is annotated with an
/// availability attribuite effectively has the availability of the interface.
VersionTuple getVersionForDecl(const Decl *Ctx) const;
/// \brief The diagnostic we should emit for \c D, or \c AR_Available.
///
/// \param D The declaration to check. Note that this may be altered to point
/// to another declaration that \c D gets it's availability from. i.e., we
/// walk the list of typedefs to find an availability attribute.
///
/// \param ContextVersion The version to compare availability against.
AvailabilityResult
ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, VersionTuple ContextVersion,
std::string *Message);
/// \param Message If non-null, this will be populated with the message from
/// the availability attribute that is selected.
AvailabilityResult ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D,
std::string *Message);
const DeclContext *getCurObjCLexicalContext() const {
const DeclContext *DC = getCurLexicalContext();

View File

@ -15627,29 +15627,3 @@ void Sema::ActOnPragmaWeakAlias(IdentifierInfo* Name,
Decl *Sema::getObjCDeclContext() const {
return (dyn_cast_or_null<ObjCContainerDecl>(CurContext));
}
AvailabilityResult Sema::getCurContextAvailability() const {
const Decl *D = cast_or_null<Decl>(getCurObjCLexicalContext());
if (!D)
return AR_Available;
// If we are within an Objective-C method, we should consult
// both the availability of the method as well as the
// enclosing class. If the class is (say) deprecated,
// the entire method is considered deprecated from the
// purpose of checking if the current context is deprecated.
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
AvailabilityResult R = MD->getAvailability();
if (R != AR_Available)
return R;
D = MD->getClassInterface();
}
// If we are within an Objective-c @implementation, it
// gets the same availability context as the @interface.
else if (const ObjCImplementationDecl *ID =
dyn_cast<ObjCImplementationDecl>(D)) {
D = ID->getClassInterface();
}
// Recover from user error.
return D ? D->getAvailability() : AR_Available;
}

View File

@ -6328,30 +6328,6 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag,
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.
@ -6380,6 +6356,71 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
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;
};
// FIXME: This is a temporary workaround! Some existing Apple headers depends
// on nested declarations in an @interface having the availability of the
// interface when they really shouldn't: they are members of the enclosing
// context, and can referenced from there.
if (S.OriginalLexicalContext && cast<Decl>(S.OriginalLexicalContext) != Ctx) {
auto *OrigCtx = cast<Decl>(S.OriginalLexicalContext);
if (CheckContext(OrigCtx))
return false;
// An implementation implicitly has the availability of the interface.
if (auto *CatOrImpl = dyn_cast<ObjCImplDecl>(OrigCtx)) {
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>(OrigCtx))
if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
if (CheckContext(Interface))
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,
@ -6396,11 +6437,15 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
// Matches diag::note_availability_specified_here.
unsigned available_here_select_kind;
// Don't warn if our current context is deprecated or unavailable.
VersionTuple DeclVersion;
if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D))
DeclVersion = AA->getIntroduced();
if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx))
return;
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;
@ -6410,8 +6455,6 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
break;
case AR_Unavailable:
if (isDeclUnavailable(Ctx))
return;
diag = !ObjCPropertyAccess ? diag::err_unavailable
: diag::err_property_method_unavailable;
diag_message = diag::err_unavailable_message;
@ -6626,29 +6669,6 @@ void Sema::EmitAvailabilityWarning(AvailabilityResult AR,
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.
@ -6662,6 +6682,7 @@ class DiagnoseUnguardedAvailability
typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
Sema &SemaRef;
Decl *Ctx;
/// Stack of potentially nested 'if (@available(...))'s.
SmallVector<VersionTuple, 8> AvailabilityStack;
@ -6669,9 +6690,10 @@ class DiagnoseUnguardedAvailability
void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range);
public:
DiagnoseUnguardedAvailability(Sema &SemaRef, VersionTuple BaseVersion)
: SemaRef(SemaRef) {
AvailabilityStack.push_back(BaseVersion);
DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
: SemaRef(SemaRef), Ctx(Ctx) {
AvailabilityStack.push_back(
SemaRef.Context.getTargetInfo().getPlatformMinVersion());
}
void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
@ -6704,8 +6726,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
NamedDecl *D, SourceRange Range) {
VersionTuple ContextVersion = AvailabilityStack.back();
if (AvailabilityResult Result = SemaRef.ShouldDiagnoseAvailabilityOfDecl(
D, ContextVersion, nullptr)) {
if (AvailabilityResult Result =
SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) {
// All other diagnostic kinds have already been handled in
// DiagnoseAvailabilityOfDecl.
if (Result != AR_NotYetIntroduced)
@ -6714,6 +6736,14 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
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(
@ -6788,6 +6818,5 @@ void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
assert(Body && "Need a body here!");
VersionTuple BaseVersion = getVersionForDecl(D);
DiagnoseUnguardedAvailability(*this, BaseVersion).IssueDiagnostics(Body);
DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
}

View File

@ -103,9 +103,9 @@ static bool HasRedeclarationWithoutAvailabilityInCategory(const Decl *D) {
return false;
}
AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
NamedDecl *&D, VersionTuple ContextVersion, std::string *Message) {
AvailabilityResult Result = D->getAvailability(Message, ContextVersion);
AvailabilityResult
Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) {
AvailabilityResult Result = D->getAvailability(Message);
// For typedefs, if the typedef declaration appears available look
// to the underlying type to see if it is more restrictive.
@ -113,7 +113,7 @@ AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
if (Result == AR_Available) {
if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) {
D = TT->getDecl();
Result = D->getAvailability(Message, ContextVersion);
Result = D->getAvailability(Message);
continue;
}
}
@ -124,7 +124,7 @@ AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
if (IDecl->getDefinition()) {
D = IDecl->getDefinition();
Result = D->getAvailability(Message, ContextVersion);
Result = D->getAvailability(Message);
}
}
@ -132,18 +132,10 @@ AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
if (Result == AR_Available) {
const DeclContext *DC = ECD->getDeclContext();
if (const EnumDecl *TheEnumDecl = dyn_cast<EnumDecl>(DC))
Result = TheEnumDecl->getAvailability(Message, ContextVersion);
Result = TheEnumDecl->getAvailability(Message);
}
switch (Result) {
case AR_Available:
return Result;
case AR_Unavailable:
case AR_Deprecated:
return getCurContextAvailability() != Result ? Result : AR_Available;
case AR_NotYetIntroduced: {
if (Result == AR_NotYetIntroduced) {
// Don't do this for enums, they can't be redeclared.
if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D))
return AR_Available;
@ -166,23 +158,18 @@ AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl(
return Warn ? AR_NotYetIntroduced : AR_Available;
}
}
llvm_unreachable("Unknown availability result!");
return 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 in the
// current context.
// See if this declaration is unavailable, deprecated, or partial.
if (AvailabilityResult Result =
S.ShouldDiagnoseAvailabilityOfDecl(D, ContextVersion, &Message)) {
S.ShouldDiagnoseAvailabilityOfDecl(D, &Message)) {
if (Result == AR_NotYetIntroduced && S.getCurFunctionOrMethodDecl()) {
S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
@ -192,8 +179,7 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc,
const ObjCPropertyDecl *ObjCPDecl = nullptr;
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
AvailabilityResult PDeclResult =
PD->getAvailability(nullptr, ContextVersion);
AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
if (PDeclResult == Result)
ObjCPDecl = PD;
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-darwin10 -verify %s
// RUN: %clang_cc1 -fsyntax-only -fblocks -triple x86_64-apple-darwin10 -verify %s
// rdar://9092208
__attribute__((unavailable("not available")))
@ -98,3 +98,26 @@ UNAVAILABLE
@end
@interface UnavailSub(cat)<SomeProto> // no error
@end
int unavail_global UNAVAILABLE;
UNAVAILABLE __attribute__((objc_root_class))
@interface TestAttrContext
-meth;
@end
@implementation TestAttrContext
-meth {
unavail_global = 2; // no warn
(void) ^{
unavail_global = 4; // no warn
};
}
@end
typedef int unavailable_int UNAVAILABLE;
UNAVAILABLE
@interface A
extern unavailable_int global_unavailable; // FIXME: this should be an error!
@end