forked from OSchip/llvm-project
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:
parent
c62266d680
commit
f35114c543
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue