[Sema] Suppress diags in overload resolution.

We were emitting diagnostics from our shiny new C-only overload
resolution mode. This patch attempts to silence all such diagnostics.

This fixes PR26085.
Differential Revision: http://reviews.llvm.org/D16159

llvm-svn: 257710
This commit is contained in:
George Burgess IV 2016-01-13 23:36:34 +00:00
parent 664fd461c2
commit 60bc972575
6 changed files with 191 additions and 101 deletions

View File

@ -2229,7 +2229,8 @@ public:
bool CheckPointerConversion(Expr *From, QualType ToType,
CastKind &Kind,
CXXCastPath& BasePath,
bool IgnoreBaseAccess);
bool IgnoreBaseAccess,
bool Diagnose = true);
bool IsMemberPointerConversion(Expr *From, QualType FromType, QualType ToType,
bool InOverloadResolution,
QualType &ConvertedType);
@ -5388,7 +5389,8 @@ public:
unsigned AmbigiousBaseConvID,
SourceLocation Loc, SourceRange Range,
DeclarationName Name,
CXXCastPath *BasePath);
CXXCastPath *BasePath,
bool IgnoreAccess = false);
std::string getAmbiguousPathsDisplayString(CXXBasePaths &Paths);
@ -7514,14 +7516,15 @@ public:
ObjCMethodDecl *&ClassMethod,
ObjCMethodDecl *&InstanceMethod,
TypedefNameDecl *&TDNDecl,
bool CfToNs);
bool CfToNs, bool Diagnose = true);
bool CheckObjCBridgeRelatedConversions(SourceLocation Loc,
QualType DestType, QualType SrcType,
Expr *&SrcExpr);
bool ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&SrcExpr);
Expr *&SrcExpr, bool Diagnose = true);
bool ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&SrcExpr,
bool Diagnose = true);
bool checkInitMethod(ObjCMethodDecl *method, QualType receiverTypeIfCall);
/// \brief Check whether the given new method is a valid override of the
@ -8613,6 +8616,7 @@ public:
ARCConversionResult CheckObjCARCConversion(SourceRange castRange,
QualType castType, Expr *&op,
CheckedConversionKind CCK,
bool Diagnose = true,
bool DiagnoseCFAudited = false,
BinaryOperatorKind Opc = BO_PtrMemD
);

View File

@ -1742,13 +1742,18 @@ void Sema::BuildBasePathArray(const CXXBasePaths &Paths,
/// otherwise. Loc is the location where this routine should point to
/// if there is an error, and Range is the source range to highlight
/// if there is an error.
///
/// If either InaccessibleBaseID or AmbigiousBaseConvID are 0, then the
/// diagnostic for the respective type of error will be suppressed, but the
/// check for ill-formed code will still be performed.
bool
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
unsigned AmbigiousBaseConvID,
SourceLocation Loc, SourceRange Range,
DeclarationName Name,
CXXCastPath *BasePath) {
CXXCastPath *BasePath,
bool IgnoreAccess) {
// First, determine whether the path from Derived to Base is
// ambiguous. This is slightly more expensive than checking whether
// the Derived to Base conversion exists, because here we need to
@ -1761,7 +1766,7 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
(void)DerivationOkay;
if (!Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) {
if (InaccessibleBaseID) {
if (!IgnoreAccess) {
// Check that the base class can be accessed.
switch (CheckBaseClassAccess(Loc, Base, Derived, Paths.front(),
InaccessibleBaseID)) {
@ -1810,12 +1815,10 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
SourceLocation Loc, SourceRange Range,
CXXCastPath *BasePath,
bool IgnoreAccess) {
return CheckDerivedToBaseConversion(Derived, Base,
IgnoreAccess ? 0
: diag::err_upcast_to_inaccessible_base,
diag::err_ambiguous_derived_to_base_conv,
Loc, Range, DeclarationName(),
BasePath);
return CheckDerivedToBaseConversion(
Derived, Base, diag::err_upcast_to_inaccessible_base,
diag::err_ambiguous_derived_to_base_conv, Loc, Range, DeclarationName(),
BasePath, IgnoreAccess);
}

View File

@ -7354,11 +7354,14 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
LHSType->isBlockPointerType()) &&
RHS.get()->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNull)) {
CastKind Kind;
CXXCastPath Path;
CheckPointerConversion(RHS.get(), LHSType, Kind, Path, false);
if (ConvertRHS)
RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_RValue, &Path);
if (Diagnose || ConvertRHS) {
CastKind Kind;
CXXCastPath Path;
CheckPointerConversion(RHS.get(), LHSType, Kind, Path,
/*IgnoreBaseAccess=*/false, Diagnose);
if (ConvertRHS)
RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_RValue, &Path);
}
return Compatible;
}
@ -7376,8 +7379,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
}
Expr *PRE = RHS.get()->IgnoreParenCasts();
if (ObjCProtocolExpr *OPE = dyn_cast<ObjCProtocolExpr>(PRE)) {
ObjCProtocolDecl *PDecl = OPE->getProtocol();
if (Diagnose && isa<ObjCProtocolExpr>(PRE)) {
ObjCProtocolDecl *PDecl = cast<ObjCProtocolExpr>(PRE)->getProtocol();
if (PDecl && !PDecl->hasDefinition()) {
Diag(PRE->getExprLoc(), diag::warn_atprotocol_protocol) << PDecl->getName();
Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl;
@ -7399,11 +7402,11 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
Expr *E = RHS.get();
if (getLangOpts().ObjCAutoRefCount)
CheckObjCARCConversion(SourceRange(), Ty, E, CCK_ImplicitConversion,
DiagnoseCFAudited);
Diagnose, DiagnoseCFAudited);
if (getLangOpts().ObjC1 &&
(CheckObjCBridgeRelatedConversions(E->getLocStart(),
LHSType, E->getType(), E) ||
ConversionToObjCStringLiteralCheck(LHSType, E))) {
(CheckObjCBridgeRelatedConversions(E->getLocStart(), LHSType,
E->getType(), E, Diagnose) ||
ConversionToObjCStringLiteralCheck(LHSType, E, Diagnose))) {
RHS = E;
return Compatible;
}
@ -8961,8 +8964,9 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
else {
Expr *E = RHS.get();
if (getLangOpts().ObjCAutoRefCount)
CheckObjCARCConversion(SourceRange(), LHSType, E, CCK_ImplicitConversion, false,
Opc);
CheckObjCARCConversion(SourceRange(), LHSType, E,
CCK_ImplicitConversion, /*Diagnose=*/true,
/*DiagnoseCFAudited=*/false, Opc);
RHS = ImpCastExprToType(E, LHSType,
LPT ? CK_BitCast :CK_CPointerToObjCPointerCast);
}
@ -11830,8 +11834,8 @@ ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) {
return new (Context) GNUNullExpr(Ty, TokenLoc);
}
bool
Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp) {
bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp,
bool Diagnose) {
if (!getLangOpts().ObjC1)
return false;
@ -11857,8 +11861,9 @@ Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp) {
StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr);
if (!SL || !SL->isAscii())
return false;
Diag(SL->getLocStart(), diag::err_missing_atsign_prefix)
<< FixItHint::CreateInsertion(SL->getLocStart(), "@");
if (Diagnose)
Diag(SL->getLocStart(), diag::err_missing_atsign_prefix)
<< FixItHint::CreateInsertion(SL->getLocStart(), "@");
Exp = BuildObjCStringLiteral(SL->getLocStart(), SL).get();
return true;
}

View File

@ -3816,7 +3816,7 @@ bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc,
ObjCMethodDecl *&ClassMethod,
ObjCMethodDecl *&InstanceMethod,
TypedefNameDecl *&TDNDecl,
bool CfToNs) {
bool CfToNs, bool Diagnose) {
QualType T = CfToNs ? SrcType : DestType;
ObjCBridgeRelatedAttr *ObjCBAttr = ObjCBridgeRelatedAttrFromType(T, TDNDecl);
if (!ObjCBAttr)
@ -3832,20 +3832,24 @@ bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc,
LookupResult R(*this, DeclarationName(RCId), SourceLocation(),
Sema::LookupOrdinaryName);
if (!LookupName(R, TUScope)) {
Diag(Loc, diag::err_objc_bridged_related_invalid_class) << RCId
<< SrcType << DestType;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Diagnose) {
Diag(Loc, diag::err_objc_bridged_related_invalid_class) << RCId
<< SrcType << DestType;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
}
return false;
}
Target = R.getFoundDecl();
if (Target && isa<ObjCInterfaceDecl>(Target))
RelatedClass = cast<ObjCInterfaceDecl>(Target);
else {
Diag(Loc, diag::err_objc_bridged_related_invalid_class_name) << RCId
<< SrcType << DestType;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Target)
Diag(Target->getLocStart(), diag::note_declared_at);
if (Diagnose) {
Diag(Loc, diag::err_objc_bridged_related_invalid_class_name) << RCId
<< SrcType << DestType;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Target)
Diag(Target->getLocStart(), diag::note_declared_at);
}
return false;
}
@ -3854,9 +3858,11 @@ bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc,
Selector Sel = Context.Selectors.getUnarySelector(CMId);
ClassMethod = RelatedClass->lookupMethod(Sel, false);
if (!ClassMethod) {
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << Sel << false;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Diagnose) {
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << Sel << false;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
}
return false;
}
}
@ -3866,9 +3872,11 @@ bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc,
Selector Sel = Context.Selectors.getNullarySelector(IMId);
InstanceMethod = RelatedClass->lookupMethod(Sel, true);
if (!InstanceMethod) {
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << Sel << true;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Diagnose) {
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << Sel << true;
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
}
return false;
}
}
@ -3878,7 +3886,7 @@ bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc,
bool
Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc,
QualType DestType, QualType SrcType,
Expr *&SrcExpr) {
Expr *&SrcExpr, bool Diagnose) {
ARCConversionTypeClass rhsExprACTC = classifyTypeForARCConversion(SrcType);
ARCConversionTypeClass lhsExprACTC = classifyTypeForARCConversion(DestType);
bool CfToNs = (rhsExprACTC == ACTC_coreFoundation && lhsExprACTC == ACTC_retainable);
@ -3891,27 +3899,29 @@ Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc,
ObjCMethodDecl *InstanceMethod = nullptr;
TypedefNameDecl *TDNDecl = nullptr;
if (!checkObjCBridgeRelatedComponents(Loc, DestType, SrcType, RelatedClass,
ClassMethod, InstanceMethod, TDNDecl, CfToNs))
ClassMethod, InstanceMethod, TDNDecl,
CfToNs, Diagnose))
return false;
if (CfToNs) {
// Implicit conversion from CF to ObjC object is needed.
if (ClassMethod) {
std::string ExpressionString = "[";
ExpressionString += RelatedClass->getNameAsString();
ExpressionString += " ";
ExpressionString += ClassMethod->getSelector().getAsString();
SourceLocation SrcExprEndLoc = getLocForEndOfToken(SrcExpr->getLocEnd());
// Provide a fixit: [RelatedClass ClassMethod SrcExpr]
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << ClassMethod->getSelector() << false
<< FixItHint::CreateInsertion(SrcExpr->getLocStart(), ExpressionString)
<< FixItHint::CreateInsertion(SrcExprEndLoc, "]");
Diag(RelatedClass->getLocStart(), diag::note_declared_at);
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
if (Diagnose) {
std::string ExpressionString = "[";
ExpressionString += RelatedClass->getNameAsString();
ExpressionString += " ";
ExpressionString += ClassMethod->getSelector().getAsString();
SourceLocation SrcExprEndLoc = getLocForEndOfToken(SrcExpr->getLocEnd());
// Provide a fixit: [RelatedClass ClassMethod SrcExpr]
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << ClassMethod->getSelector() << false
<< FixItHint::CreateInsertion(SrcExpr->getLocStart(), ExpressionString)
<< FixItHint::CreateInsertion(SrcExprEndLoc, "]");
Diag(RelatedClass->getLocStart(), diag::note_declared_at);
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
}
QualType receiverType =
Context.getObjCInterfaceType(RelatedClass);
QualType receiverType = Context.getObjCInterfaceType(RelatedClass);
// Argument.
Expr *args[] = { SrcExpr };
ExprResult msg = BuildClassMessageImplicit(receiverType, false,
@ -3925,30 +3935,34 @@ Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc,
else {
// Implicit conversion from ObjC type to CF object is needed.
if (InstanceMethod) {
std::string ExpressionString;
SourceLocation SrcExprEndLoc = getLocForEndOfToken(SrcExpr->getLocEnd());
if (InstanceMethod->isPropertyAccessor())
if (const ObjCPropertyDecl *PDecl = InstanceMethod->findPropertyDecl()) {
// fixit: ObjectExpr.propertyname when it is aproperty accessor.
ExpressionString = ".";
ExpressionString += PDecl->getNameAsString();
if (Diagnose) {
std::string ExpressionString;
SourceLocation SrcExprEndLoc =
getLocForEndOfToken(SrcExpr->getLocEnd());
if (InstanceMethod->isPropertyAccessor())
if (const ObjCPropertyDecl *PDecl =
InstanceMethod->findPropertyDecl()) {
// fixit: ObjectExpr.propertyname when it is aproperty accessor.
ExpressionString = ".";
ExpressionString += PDecl->getNameAsString();
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << InstanceMethod->getSelector() << true
<< FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString);
}
if (ExpressionString.empty()) {
// Provide a fixit: [ObjectExpr InstanceMethod]
ExpressionString = " ";
ExpressionString += InstanceMethod->getSelector().getAsString();
ExpressionString += "]";
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << InstanceMethod->getSelector() << true
<< FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString);
<< SrcType << DestType << InstanceMethod->getSelector() << true
<< FixItHint::CreateInsertion(SrcExpr->getLocStart(), "[")
<< FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString);
}
if (ExpressionString.empty()) {
// Provide a fixit: [ObjectExpr InstanceMethod]
ExpressionString = " ";
ExpressionString += InstanceMethod->getSelector().getAsString();
ExpressionString += "]";
Diag(Loc, diag::err_objc_bridged_related_known_method)
<< SrcType << DestType << InstanceMethod->getSelector() << true
<< FixItHint::CreateInsertion(SrcExpr->getLocStart(), "[")
<< FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString);
Diag(RelatedClass->getLocStart(), diag::note_declared_at);
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
}
Diag(RelatedClass->getLocStart(), diag::note_declared_at);
Diag(TDNDecl->getLocStart(), diag::note_declared_at);
ExprResult msg =
BuildInstanceMessageImplicit(SrcExpr, SrcType,
@ -3965,6 +3979,7 @@ Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc,
Sema::ARCConversionResult
Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
Expr *&castExpr, CheckedConversionKind CCK,
bool Diagnose,
bool DiagnoseCFAudited,
BinaryOperatorKind Opc) {
QualType castExprType = castExpr->getType();
@ -3980,9 +3995,9 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
if (exprACTC == castACTC) {
// check for viablity and report error if casting an rvalue to a
// life-time qualifier.
if ((castACTC == ACTC_retainable) &&
if (Diagnose && castACTC == ACTC_retainable &&
(CCK == CCK_CStyleCast || CCK == CCK_OtherCast) &&
(castType != castExprType)) {
castType != castExprType) {
const Type *DT = castType.getTypePtr();
QualType QDT = castType;
// We desugar some types but not others. We ignore those
@ -4051,19 +4066,20 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
// to 'NSString *'. Let caller issue a normal mismatched diagnostic with
// suitable fix-it.
if (castACTC == ACTC_retainable && exprACTC == ACTC_none &&
ConversionToObjCStringLiteralCheck(castType, castExpr))
ConversionToObjCStringLiteralCheck(castType, castExpr, Diagnose))
return ACR_okay;
// Do not issue "bridge cast" diagnostic when implicit casting
// a retainable object to a CF type parameter belonging to an audited
// CF API function. Let caller issue a normal type mismatched diagnostic
// instead.
if (!DiagnoseCFAudited || exprACTC != ACTC_retainable ||
castACTC != ACTC_coreFoundation)
if (Diagnose &&
(!DiagnoseCFAudited || exprACTC != ACTC_retainable ||
castACTC != ACTC_coreFoundation))
if (!(exprACTC == ACTC_voidPtr && castACTC == ACTC_retainable &&
(Opc == BO_NE || Opc == BO_EQ)))
diagnoseObjCARCConversion(*this, castRange, castType, castACTC,
castExpr, castExpr, exprACTC, CCK);
diagnoseObjCARCConversion(*this, castRange, castType, castACTC, castExpr,
castExpr, exprACTC, CCK);
return ACR_okay;
}

View File

@ -2687,15 +2687,16 @@ bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
bool Sema::CheckPointerConversion(Expr *From, QualType ToType,
CastKind &Kind,
CXXCastPath& BasePath,
bool IgnoreBaseAccess) {
bool IgnoreBaseAccess,
bool Diagnose) {
QualType FromType = From->getType();
bool IsCStyleOrFunctionalCast = IgnoreBaseAccess;
Kind = CK_BitCast;
if (!IsCStyleOrFunctionalCast && !FromType->isAnyPointerType() &&
if (Diagnose && !IsCStyleOrFunctionalCast && !FromType->isAnyPointerType() &&
From->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull) ==
Expr::NPCK_ZeroExpression) {
Expr::NPCK_ZeroExpression) {
if (Context.hasSameUnqualifiedType(From->getType(), Context.BoolTy))
DiagRuntimeBehavior(From->getExprLoc(), From,
PDiag(diag::warn_impcast_bool_to_null_pointer)
@ -2713,18 +2714,24 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType,
!Context.hasSameUnqualifiedType(FromPointeeType, ToPointeeType)) {
// We must have a derived-to-base conversion. Check an
// ambiguous or inaccessible conversion.
if (CheckDerivedToBaseConversion(FromPointeeType, ToPointeeType,
From->getExprLoc(),
From->getSourceRange(), &BasePath,
IgnoreBaseAccess))
unsigned InaccessibleID = 0;
unsigned AmbigiousID = 0;
if (Diagnose) {
InaccessibleID = diag::err_upcast_to_inaccessible_base;
AmbigiousID = diag::err_ambiguous_derived_to_base_conv;
}
if (CheckDerivedToBaseConversion(
FromPointeeType, ToPointeeType, InaccessibleID, AmbigiousID,
From->getExprLoc(), From->getSourceRange(), DeclarationName(),
&BasePath, IgnoreBaseAccess))
return true;
// The conversion was successful.
Kind = CK_DerivedToBase;
}
if (!IsCStyleOrFunctionalCast && FromPointeeType->isFunctionType() &&
ToPointeeType->isVoidType()) {
if (Diagnose && !IsCStyleOrFunctionalCast &&
FromPointeeType->isFunctionType() && ToPointeeType->isVoidType()) {
assert(getLangOpts().MSVCCompat &&
"this should only be possible with MSVCCompat!");
Diag(From->getExprLoc(), diag::ext_ms_impcast_fn_obj)

View File

@ -0,0 +1,55 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -verify %s -fobjc-arc
//
// These tests exist as a means to help ensure that diagnostics aren't printed
// in overload resolution in ObjC.
struct Type1 { int a; };
@interface Iface1 @end
@interface NeverCalled
- (void) test:(struct Type1 *)arg;
@end
@interface TakesIface1
- (void) test:(Iface1 *)arg;
@end
// PR26085, rdar://problem/24111333
void testTakesIface1(id x, Iface1 *arg) {
// This should resolve silently to `TakesIface1`.
[x test:arg];
}
@class NSString;
@interface NeverCalledv2
- (void) testStr:(NSString *)arg;
@end
@interface TakesVanillaConstChar
- (void) testStr:(const void *)a;
@end
// Not called out explicitly by PR26085, but related.
void testTakesNSString(id x) {
// Overload resolution should not emit a diagnostic about needing to add an
// '@' before "someStringLiteral".
[x testStr:"someStringLiteral"];
}
typedef const void *CFTypeRef;
id CreateSomething();
@interface NeverCalledv3
- (void) testCFTypeRef:(struct Type1 *)arg;
@end
@interface TakesCFTypeRef
- (void) testCFTypeRef:(CFTypeRef)arg;
@end
// Not called out explicitly by PR26085, but related.
void testTakesCFTypeRef(id x) {
// Overload resolution should occur silently, select the CFTypeRef overload,
// and produce a single complaint. (with notes)
[x testCFTypeRef:CreateSomething()]; // expected-error{{implicit conversion of Objective-C pointer type 'id' to C pointer type 'CFTypeRef'}} expected-note{{use __bridge}} expected-note{{use __bridge_retained}}
}