[ObjC] Add a command line flag that disables recognition of objc_direct for testability

Programmers would like to be able to test direct methods by calling them from a
different linkage unit or mocking them, both of which are impossible. This
patch adds a flag that effectively disables the attribute, which will fix this
when enabled in testable builds. rdar://71190891

Differential revision: https://reviews.llvm.org/D95845
This commit is contained in:
Erik Pilkington 2021-04-05 19:23:27 -04:00
parent 844012940e
commit b660abc80d
9 changed files with 87 additions and 9 deletions

View File

@ -852,9 +852,7 @@ public:
bool isClassProperty() const {
return PropertyAttributes & ObjCPropertyAttribute::kind_class;
}
bool isDirectProperty() const {
return PropertyAttributes & ObjCPropertyAttribute::kind_direct;
}
bool isDirectProperty() const;
ObjCPropertyQueryKind getQueryKind() const {
return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class :

View File

@ -298,6 +298,8 @@ LANGOPT(ObjCSubscriptingLegacyRuntime , 1, 0, "Subscripting support in l
BENIGN_LANGOPT(CompatibilityQualifiedIdBlockParamTypeChecking, 1, 0,
"compatibility mode for type checking block parameters "
"involving qualified id types")
LANGOPT(ObjCDisableDirectMethodsForTesting, 1, 0,
"Disable recognition of objc_direct methods")
LANGOPT(CFProtectionBranch , 1, 0, "Control-Flow Branch Protection enabled")
LANGOPT(FakeAddressSpaceMap , 1, 0, "OpenCL fake address space map")
ENUM_LANGOPT(AddressSpaceMapMangling , AddrSpaceMapMangling, 2, ASMM_Target, "OpenCL address space map mangling mode")

View File

@ -2225,6 +2225,12 @@ def fobjc_nonfragile_abi : Flag<["-"], "fobjc-nonfragile-abi">, Group<f_Group>;
def fno_objc_nonfragile_abi : Flag<["-"], "fno-objc-nonfragile-abi">, Group<f_Group>;
def fobjc_sender_dependent_dispatch : Flag<["-"], "fobjc-sender-dependent-dispatch">, Group<f_Group>;
def fobjc_disable_direct_methods_for_testing :
Flag<["-"], "fobjc-disable-direct-methods-for-testing">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Ignore attribute objc_direct so that direct methods can be tested">,
MarshallingInfoFlag<LangOpts<"ObjCDisableDirectMethodsForTesting">>;
def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group<f_Group>;
def fopenmp : Flag<["-"], "fopenmp">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused, FlangOption, FC1Option]>,
HelpText<"Parse OpenMP pragmas and generate parallel code.">;

View File

@ -826,7 +826,8 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
}
bool ObjCMethodDecl::isDirectMethod() const {
return hasAttr<ObjCDirectAttr>();
return hasAttr<ObjCDirectAttr>() &&
!getASTContext().getLangOpts().ObjCDisableDirectMethodsForTesting;
}
bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
@ -2295,6 +2296,11 @@ QualType ObjCPropertyDecl::getUsageType(QualType objectType) const {
ObjCSubstitutionContext::Property);
}
bool ObjCPropertyDecl::isDirectProperty() const {
return (PropertyAttributes & ObjCPropertyAttribute::kind_direct) &&
!getASTContext().getLangOpts().ObjCDisableDirectMethodsForTesting;
}
//===----------------------------------------------------------------------===//
// ObjCPropertyImplDecl
//===----------------------------------------------------------------------===//

View File

@ -3665,6 +3665,9 @@ static void RenderObjCOptions(const ToolChain &TC, const Driver &D,
WeakArg->render(Args, CmdArgs);
}
}
if (Args.hasArg(options::OPT_fobjc_disable_direct_methods_for_testing))
CmdArgs.push_back("-fobjc-disable-direct-methods-for-testing");
}
static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args,

View File

@ -4407,10 +4407,12 @@ private:
void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
ObjCMethodDecl *overridden) {
if (const auto *attr = overridden->getAttr<ObjCDirectAttr>()) {
if (overridden->isDirectMethod()) {
const auto *attr = overridden->getAttr<ObjCDirectAttr>();
Diag(method->getLocation(), diag::err_objc_override_direct_method);
Diag(attr->getLocation(), diag::note_previous_declaration);
} else if (const auto *attr = method->getAttr<ObjCDirectAttr>()) {
} else if (method->isDirectMethod()) {
const auto *attr = method->getAttr<ObjCDirectAttr>();
Diag(attr->getLocation(), diag::err_objc_direct_on_override)
<< isa<ObjCProtocolDecl>(overridden->getDeclContext());
Diag(overridden->getLocation(), diag::note_previous_declaration);
@ -4856,7 +4858,8 @@ Decl *Sema::ActOnMethodDeclaration(
// the canonical declaration.
if (!ObjCMethod->isDirectMethod()) {
const ObjCMethodDecl *CanonicalMD = ObjCMethod->getCanonicalDecl();
if (const auto *attr = CanonicalMD->getAttr<ObjCDirectAttr>()) {
if (CanonicalMD->isDirectMethod()) {
const auto *attr = CanonicalMD->getAttr<ObjCDirectAttr>();
ObjCMethod->addAttr(
ObjCDirectAttr::CreateImplicit(Context, attr->getLocation()));
}
@ -4901,14 +4904,16 @@ Decl *Sema::ActOnMethodDeclaration(
Diag(IMD->getLocation(), diag::note_previous_declaration);
};
if (const auto *attr = ObjCMethod->getAttr<ObjCDirectAttr>()) {
if (ObjCMethod->isDirectMethod()) {
const auto *attr = ObjCMethod->getAttr<ObjCDirectAttr>();
if (ObjCMethod->getCanonicalDecl() != IMD) {
diagContainerMismatch();
} else if (!IMD->isDirectMethod()) {
Diag(attr->getLocation(), diag::err_objc_direct_missing_on_decl);
Diag(IMD->getLocation(), diag::note_previous_declaration);
}
} else if (const auto *attr = IMD->getAttr<ObjCDirectAttr>()) {
} else if (IMD->isDirectMethod()) {
const auto *attr = IMD->getAttr<ObjCDirectAttr>();
if (ObjCMethod->getCanonicalDecl() != IMD) {
diagContainerMismatch();
} else {

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin10 -fobjc-disable-direct-methods-for-testing %s -o - | FileCheck %s
@interface Y
@property (direct) int direct_property;
@end
@implementation Y @end
// CHECK: @OBJC_PROP_NAME_ATTR_ = private unnamed_addr constant [16 x i8] c"direct_property\00"
// CHECK: @"_OBJC_$_PROP_LIST_Y" =
// CHECK-SAME: @OBJC_PROP_NAME_ATTR_,
@interface X
-(void)m __attribute__((objc_direct));
@end
// CHECK-LABEL: define void @f
void f(X *x) {
[x m];
// CHECK: call void bitcast ({{.*}} @objc_msgSend to {{.*}})
}

View File

@ -576,3 +576,8 @@
// RUN: %clang -### -S -fno-temp-file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-TEMP-FILE %s
// CHECK-NO-TEMP-FILE: "-fno-temp-file"
// RUN: %clang -### -xobjective-c -fobjc-disable-direct-methods-for-testing %s 2>&1 | FileCheck -check-prefix=CHECK_DISABLE_DIRECT %s
// RUN: %clang -### -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_NO_DISABLE_DIRECT %s
// CHECK_DISABLE_DIRECT: -fobjc-disable-direct-methods-for-testing
// CHECK_NO_DISABLE_DIRECT-NOT: -fobjc-disable-direct-methods-for-testing

View File

@ -0,0 +1,32 @@
// RUN: %clang_cc1 -verify -fobjc-disable-direct-methods-for-testing %s
// expected-no-diagnostics
#define DIRECT __attribute__((objc_direct))
#define DIRECT_MEMBERS __attribute__((objc_direct_members))
__attribute__((objc_root_class))
@interface X
-(void)direct_method DIRECT;
@end
@implementation X
-(void)direct_method DIRECT {}
@end
__attribute__((objc_root_class))
DIRECT_MEMBERS
@interface Y
-(void)direct_method2;
@end
@implementation Y
-(void)direct_method2 {}
@end
__attribute__((objc_root_class))
@interface Z
@property (direct) int direct_property;
@end
@implementation Z @end