зеркало из https://github.com/microsoft/clang-1.git
Add a clarifying note when a return statement is rejected because
we expect a related result type. rdar://12493140 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177378 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
a5a3e01c50
Коммит
7cca821e1a
|
@ -6198,15 +6198,19 @@ def warn_related_result_type_compatibility_class : Warning<
|
||||||
def warn_related_result_type_compatibility_protocol : Warning<
|
def warn_related_result_type_compatibility_protocol : Warning<
|
||||||
"protocol method is expected to return an instance of the implementing "
|
"protocol method is expected to return an instance of the implementing "
|
||||||
"class, but is declared to return %0">;
|
"class, but is declared to return %0">;
|
||||||
def note_related_result_type_overridden_family : Note<
|
def note_related_result_type_family : Note<
|
||||||
"overridden method is part of the '%select{|alloc|copy|init|mutableCopy|"
|
"%select{overridden|current}0 method is part of the '%select{|alloc|copy|init|"
|
||||||
"new|autorelease|dealloc|finalize|release|retain|retainCount|self}0' method "
|
"mutableCopy|new|autorelease|dealloc|finalize|release|retain|retainCount|"
|
||||||
"family">;
|
"self}1' method family%select{| and is expected to return an instance of its "
|
||||||
|
"class type}0">;
|
||||||
def note_related_result_type_overridden : Note<
|
def note_related_result_type_overridden : Note<
|
||||||
"overridden method returns an instance of its class type">;
|
"overridden method returns an instance of its class type">;
|
||||||
def note_related_result_type_inferred : Note<
|
def note_related_result_type_inferred : Note<
|
||||||
"%select{class|instance}0 method %1 is assumed to return an instance of "
|
"%select{class|instance}0 method %1 is assumed to return an instance of "
|
||||||
"its receiver type (%2)">;
|
"its receiver type (%2)">;
|
||||||
|
def note_related_result_type_explicit : Note<
|
||||||
|
"%select{overridden|current}0 method is explicitly declared 'instancetype'"
|
||||||
|
"%select{| and is expected to return an instance of its class type}0">;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7026,6 +7026,11 @@ public:
|
||||||
/// with a related result type, emit a note describing what happened.
|
/// with a related result type, emit a note describing what happened.
|
||||||
void EmitRelatedResultTypeNote(const Expr *E);
|
void EmitRelatedResultTypeNote(const Expr *E);
|
||||||
|
|
||||||
|
/// \brief Given that we had incompatible pointer types in a return
|
||||||
|
/// statement, check whether we're in a method with a related result
|
||||||
|
/// type, and if so, emit a note describing what happened.
|
||||||
|
void EmitRelatedResultTypeNoteForReturn(QualType destType);
|
||||||
|
|
||||||
/// CheckBooleanCondition - Diagnose problems involving the use of
|
/// CheckBooleanCondition - Diagnose problems involving the use of
|
||||||
/// the given expression as a boolean condition (e.g. in an if
|
/// the given expression as a boolean condition (e.g. in an if
|
||||||
/// statement). Also performs the standard function and array
|
/// statement). Also performs the standard function and array
|
||||||
|
|
|
@ -151,7 +151,8 @@ void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
|
||||||
|
|
||||||
if (ObjCMethodFamily Family = Overridden->getMethodFamily())
|
if (ObjCMethodFamily Family = Overridden->getMethodFamily())
|
||||||
Diag(Overridden->getLocation(),
|
Diag(Overridden->getLocation(),
|
||||||
diag::note_related_result_type_overridden_family)
|
diag::note_related_result_type_family)
|
||||||
|
<< /*overridden method*/ 0
|
||||||
<< Family;
|
<< Family;
|
||||||
else
|
else
|
||||||
Diag(Overridden->getLocation(),
|
Diag(Overridden->getLocation(),
|
||||||
|
|
|
@ -10039,6 +10039,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
|
||||||
|
|
||||||
if (CheckInferredResultType)
|
if (CheckInferredResultType)
|
||||||
EmitRelatedResultTypeNote(SrcExpr);
|
EmitRelatedResultTypeNote(SrcExpr);
|
||||||
|
|
||||||
|
if (Action == AA_Returning && ConvTy == IncompatiblePointer)
|
||||||
|
EmitRelatedResultTypeNoteForReturn(DstType);
|
||||||
|
|
||||||
if (Complained)
|
if (Complained)
|
||||||
*Complained = true;
|
*Complained = true;
|
||||||
|
|
|
@ -1094,6 +1094,73 @@ QualType Sema::getMessageSendResultType(QualType ReceiverType,
|
||||||
return ReceiverType;
|
return ReceiverType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look for an ObjC method whose result type exactly matches the given type.
|
||||||
|
static const ObjCMethodDecl *
|
||||||
|
findExplicitInstancetypeDeclarer(const ObjCMethodDecl *MD,
|
||||||
|
QualType instancetype) {
|
||||||
|
if (MD->getResultType() == instancetype) return MD;
|
||||||
|
|
||||||
|
// For these purposes, a method in an @implementation overrides a
|
||||||
|
// declaration in the @interface.
|
||||||
|
if (const ObjCImplDecl *impl =
|
||||||
|
dyn_cast<ObjCImplDecl>(MD->getDeclContext())) {
|
||||||
|
const ObjCContainerDecl *iface;
|
||||||
|
if (const ObjCCategoryImplDecl *catImpl =
|
||||||
|
dyn_cast<ObjCCategoryImplDecl>(impl)) {
|
||||||
|
iface = catImpl->getCategoryDecl();
|
||||||
|
} else {
|
||||||
|
iface = impl->getClassInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ObjCMethodDecl *ifaceMD =
|
||||||
|
iface->getMethod(MD->getSelector(), MD->isInstanceMethod());
|
||||||
|
if (ifaceMD) return findExplicitInstancetypeDeclarer(ifaceMD, instancetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<const ObjCMethodDecl *, 4> overrides;
|
||||||
|
MD->getOverriddenMethods(overrides);
|
||||||
|
for (unsigned i = 0, e = overrides.size(); i != e; ++i) {
|
||||||
|
if (const ObjCMethodDecl *result =
|
||||||
|
findExplicitInstancetypeDeclarer(overrides[i], instancetype))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sema::EmitRelatedResultTypeNoteForReturn(QualType destType) {
|
||||||
|
// Only complain if we're in an ObjC method and the required return
|
||||||
|
// type doesn't match the method's declared return type.
|
||||||
|
ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CurContext);
|
||||||
|
if (!MD || !MD->hasRelatedResultType() ||
|
||||||
|
Context.hasSameUnqualifiedType(destType, MD->getResultType()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Look for a method overridden by this method which explicitly uses
|
||||||
|
// 'instancetype'.
|
||||||
|
if (const ObjCMethodDecl *overridden =
|
||||||
|
findExplicitInstancetypeDeclarer(MD, Context.getObjCInstanceType())) {
|
||||||
|
SourceLocation loc;
|
||||||
|
SourceRange range;
|
||||||
|
if (TypeSourceInfo *TSI = overridden->getResultTypeSourceInfo()) {
|
||||||
|
range = TSI->getTypeLoc().getSourceRange();
|
||||||
|
loc = range.getBegin();
|
||||||
|
}
|
||||||
|
if (loc.isInvalid())
|
||||||
|
loc = overridden->getLocation();
|
||||||
|
Diag(loc, diag::note_related_result_type_explicit)
|
||||||
|
<< /*current method*/ 1 << range;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if we have an interesting method family, note that.
|
||||||
|
// This should always trigger if the above didn't.
|
||||||
|
if (ObjCMethodFamily family = MD->getMethodFamily())
|
||||||
|
Diag(MD->getLocation(), diag::note_related_result_type_family)
|
||||||
|
<< /*current method*/ 1
|
||||||
|
<< family;
|
||||||
|
}
|
||||||
|
|
||||||
void Sema::EmitRelatedResultTypeNote(const Expr *E) {
|
void Sema::EmitRelatedResultTypeNote(const Expr *E) {
|
||||||
E = E->IgnoreParenImpCasts();
|
E = E->IgnoreParenImpCasts();
|
||||||
const ObjCMessageExpr *MsgSend = dyn_cast<ObjCMessageExpr>(E);
|
const ObjCMessageExpr *MsgSend = dyn_cast<ObjCMessageExpr>(E);
|
||||||
|
|
|
@ -5590,6 +5590,26 @@ static bool DiagnoseUninitializedReference(Sema &S, SourceLocation Loc,
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Diagnose initialization failures
|
// Diagnose initialization failures
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// Emit notes associated with an initialization that failed due to a
|
||||||
|
/// "simple" conversion failure.
|
||||||
|
static void emitBadConversionNotes(Sema &S, const InitializedEntity &entity,
|
||||||
|
Expr *op) {
|
||||||
|
QualType destType = entity.getType();
|
||||||
|
if (destType.getNonReferenceType()->isObjCObjectPointerType() &&
|
||||||
|
op->getType()->isObjCObjectPointerType()) {
|
||||||
|
|
||||||
|
// Emit a possible note about the conversion failing because the
|
||||||
|
// operand is a message send with a related result type.
|
||||||
|
S.EmitRelatedResultTypeNote(op);
|
||||||
|
|
||||||
|
// Emit a possible note about a return failing because we're
|
||||||
|
// expecting a related result type.
|
||||||
|
if (entity.getKind() == InitializedEntity::EK_Result)
|
||||||
|
S.EmitRelatedResultTypeNoteForReturn(destType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool InitializationSequence::Diagnose(Sema &S,
|
bool InitializationSequence::Diagnose(Sema &S,
|
||||||
const InitializedEntity &Entity,
|
const InitializedEntity &Entity,
|
||||||
const InitializationKind &Kind,
|
const InitializationKind &Kind,
|
||||||
|
@ -5734,9 +5754,7 @@ bool InitializationSequence::Diagnose(Sema &S,
|
||||||
<< Args[0]->isLValue()
|
<< Args[0]->isLValue()
|
||||||
<< Args[0]->getType()
|
<< Args[0]->getType()
|
||||||
<< Args[0]->getSourceRange();
|
<< Args[0]->getSourceRange();
|
||||||
if (DestType.getNonReferenceType()->isObjCObjectPointerType() &&
|
emitBadConversionNotes(S, Entity, Args[0]);
|
||||||
Args[0]->getType()->isObjCObjectPointerType())
|
|
||||||
S.EmitRelatedResultTypeNote(Args[0]);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FK_ConversionFailed: {
|
case FK_ConversionFailed: {
|
||||||
|
@ -5749,9 +5767,7 @@ bool InitializationSequence::Diagnose(Sema &S,
|
||||||
<< Args[0]->getSourceRange();
|
<< Args[0]->getSourceRange();
|
||||||
S.HandleFunctionTypeMismatch(PDiag, FromType, DestType);
|
S.HandleFunctionTypeMismatch(PDiag, FromType, DestType);
|
||||||
S.Diag(Kind.getLocation(), PDiag);
|
S.Diag(Kind.getLocation(), PDiag);
|
||||||
if (DestType.getNonReferenceType()->isObjCObjectPointerType() &&
|
emitBadConversionNotes(S, Entity, Args[0]);
|
||||||
Args[0]->getType()->isObjCObjectPointerType())
|
|
||||||
S.EmitRelatedResultTypeNote(Args[0]);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2536,20 +2536,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
|
if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
|
||||||
// we have a non-void function with an expression, continue checking
|
// we have a non-void function with an expression, continue checking
|
||||||
|
|
||||||
if (!RelatedRetType.isNull()) {
|
QualType RetType = (RelatedRetType.isNull() ? FnRetType : RelatedRetType);
|
||||||
// If we have a related result type, perform an extra conversion here.
|
|
||||||
// FIXME: The diagnostics here don't really describe what is happening.
|
|
||||||
InitializedEntity Entity =
|
|
||||||
InitializedEntity::InitializeTemporary(RelatedRetType);
|
|
||||||
|
|
||||||
ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(),
|
|
||||||
RetValExp);
|
|
||||||
if (Res.isInvalid()) {
|
|
||||||
// FIXME: Cleanup temporaries here, anyway?
|
|
||||||
return StmtError();
|
|
||||||
}
|
|
||||||
RetValExp = Res.takeAs<Expr>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// C99 6.8.6.4p3(136): The return statement is not an assignment. The
|
// C99 6.8.6.4p3(136): The return statement is not an assignment. The
|
||||||
// overlap restriction of subclause 6.5.16.1 does not apply to the case of
|
// overlap restriction of subclause 6.5.16.1 does not apply to the case of
|
||||||
|
@ -2559,18 +2546,33 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
||||||
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false);
|
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false);
|
||||||
InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
|
InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
|
||||||
FnRetType,
|
RetType,
|
||||||
NRVOCandidate != 0);
|
NRVOCandidate != 0);
|
||||||
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
|
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
|
||||||
FnRetType, RetValExp);
|
RetType, RetValExp);
|
||||||
if (Res.isInvalid()) {
|
if (Res.isInvalid()) {
|
||||||
// FIXME: Cleanup temporaries here, anyway?
|
// FIXME: Clean up temporaries here anyway?
|
||||||
return StmtError();
|
return StmtError();
|
||||||
}
|
}
|
||||||
|
|
||||||
RetValExp = Res.takeAs<Expr>();
|
RetValExp = Res.takeAs<Expr>();
|
||||||
if (RetValExp)
|
|
||||||
CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
|
// If we have a related result type, we need to implicitly
|
||||||
|
// convert back to the formal result type. We can't pretend to
|
||||||
|
// initialize the result again --- we might end double-retaining
|
||||||
|
// --- so instead we initialize a notional temporary; this can
|
||||||
|
// lead to less-than-great diagnostics, but this stage is much
|
||||||
|
// less likely to fail than the previous stage.
|
||||||
|
if (!RelatedRetType.isNull()) {
|
||||||
|
Entity = InitializedEntity::InitializeTemporary(FnRetType);
|
||||||
|
Res = PerformCopyInitialization(Entity, ReturnLoc, RetValExp);
|
||||||
|
if (Res.isInvalid()) {
|
||||||
|
// FIXME: Clean up temporaries here anyway?
|
||||||
|
return StmtError();
|
||||||
|
}
|
||||||
|
RetValExp = Res.takeAs<Expr>();
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RetValExp) {
|
if (RetValExp) {
|
||||||
|
|
|
@ -621,7 +621,9 @@ void test21(unsigned n) {
|
||||||
// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]]
|
// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]]
|
||||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]]
|
// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]]
|
||||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8*
|
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8*
|
||||||
// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]])
|
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]])
|
||||||
|
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST27]]*
|
||||||
|
// CHECK-NEXT: [[RET:%.*]] = bitcast [[TEST27]]* [[T3]] to i8*
|
||||||
// CHECK-NEXT: store i32 {{[0-9]+}}, i32* [[DEST]]
|
// CHECK-NEXT: store i32 {{[0-9]+}}, i32* [[DEST]]
|
||||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]]
|
// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]]
|
||||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8*
|
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8*
|
||||||
|
@ -685,7 +687,9 @@ static id _test29_allocator = 0;
|
||||||
// Return statement.
|
// Return statement.
|
||||||
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[CALL]]
|
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[CALL]]
|
||||||
// CHECK-NEXT: [[CALL:%.*]] = bitcast
|
// CHECK-NEXT: [[CALL:%.*]] = bitcast
|
||||||
// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[CALL]]) [[NUW]]
|
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* [[CALL]]) [[NUW]]
|
||||||
|
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[TEST29]]*
|
||||||
|
// CHECK-NEXT: [[RET:%.*]] = bitcast [[TEST29]]* [[T1]] to i8*
|
||||||
// CHECK-NEXT: store i32 1, i32* [[CLEANUP]]
|
// CHECK-NEXT: store i32 1, i32* [[CLEANUP]]
|
||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
|
@ -739,7 +743,9 @@ static id _test29_allocator = 0;
|
||||||
// Return statement.
|
// Return statement.
|
||||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]]
|
// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]]
|
||||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8*
|
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8*
|
||||||
// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]]) [[NUW]]
|
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* [[T1]]) [[NUW]]
|
||||||
|
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[TEST29]]*
|
||||||
|
// CHECK-NEXT: [[RET:%.*]] = bitcast [[TEST29]]* [[T1]] to i8*
|
||||||
// CHECK-NEXT: store i32 1, i32* [[CLEANUP]]
|
// CHECK-NEXT: store i32 1, i32* [[CLEANUP]]
|
||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
|
@ -794,7 +800,9 @@ char *helper;
|
||||||
// Return.
|
// Return.
|
||||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]]
|
// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]]
|
||||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8*
|
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8*
|
||||||
// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]])
|
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* [[T1]])
|
||||||
|
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[TEST30]]*
|
||||||
|
// CHECK-NEXT: [[RET:%.*]] = bitcast [[TEST30]]* [[T1]] to i8*
|
||||||
// CHECK-NEXT: store i32 1
|
// CHECK-NEXT: store i32 1
|
||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@interface Root
|
@interface Root
|
||||||
+ (instancetype)alloc;
|
+ (instancetype)alloc; // expected-note {{explicitly declared 'instancetype'}}
|
||||||
- (instancetype)init; // expected-note{{overridden method is part of the 'init' method family}}
|
- (instancetype)init; // expected-note{{overridden method is part of the 'init' method family}}
|
||||||
- (instancetype)self;
|
- (instancetype)self; // expected-note {{explicitly declared 'instancetype'}}
|
||||||
- (Class)class;
|
- (Class)class;
|
||||||
|
|
||||||
@property (assign) Root *selfProp;
|
@property (assign) Root *selfProp;
|
||||||
|
@ -143,7 +143,7 @@ void test_instancetype_narrow_method_search() {
|
||||||
|
|
||||||
@implementation Subclass4
|
@implementation Subclass4
|
||||||
+ (id)alloc {
|
+ (id)alloc {
|
||||||
return self; // expected-warning{{incompatible pointer types casting 'Class' to type 'Subclass4 *'}}
|
return self; // expected-warning{{incompatible pointer types returning 'Class' from a function with result type 'Subclass4 *'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (Subclass3 *)init { return 0; } // don't complain: we lost the related return type
|
- (Subclass3 *)init { return 0; } // don't complain: we lost the related return type
|
||||||
|
@ -164,14 +164,14 @@ void test_instancetype_inherited() {
|
||||||
// Check that related return types tighten up the semantics of
|
// Check that related return types tighten up the semantics of
|
||||||
// Objective-C method implementations.
|
// Objective-C method implementations.
|
||||||
@implementation Subclass2
|
@implementation Subclass2
|
||||||
- (instancetype)initSubclass2 {
|
- (instancetype)initSubclass2 { // expected-note {{explicitly declared 'instancetype'}}
|
||||||
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
||||||
return sc1; // expected-warning{{incompatible pointer types casting 'Subclass1 *' to type 'Subclass2 *'}}
|
return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}}
|
||||||
}
|
}
|
||||||
- (void)methodOnSubclass2 {}
|
- (void)methodOnSubclass2 {}
|
||||||
- (id)self {
|
- (id)self {
|
||||||
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
||||||
return sc1; // expected-warning{{incompatible pointer types casting 'Subclass1 *' to type 'Subclass2 *'}}
|
return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -188,3 +188,29 @@ void test_instancetype_inherited() {
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
// rdar://12493140
|
||||||
|
@protocol P4
|
||||||
|
- (instancetype) foo; // expected-note {{current method is explicitly declared 'instancetype' and is expected to return an instance of its class type}}
|
||||||
|
@end
|
||||||
|
@interface A4 : Root <P4>
|
||||||
|
- (instancetype) bar; // expected-note {{current method is explicitly declared 'instancetype' and is expected to return an instance of its class type}}
|
||||||
|
- (instancetype) baz; // expected-note {{overridden method returns an instance of its class type}} expected-note {{previous definition is here}}
|
||||||
|
@end
|
||||||
|
@interface B4 : Root @end
|
||||||
|
|
||||||
|
@implementation A4 {
|
||||||
|
B4 *_b;
|
||||||
|
}
|
||||||
|
- (id) foo {
|
||||||
|
return _b; // expected-warning {{incompatible pointer types returning 'B4 *' from a function with result type 'A4 *'}}
|
||||||
|
}
|
||||||
|
- (id) bar {
|
||||||
|
return _b; // expected-warning {{incompatible pointer types returning 'B4 *' from a function with result type 'A4 *'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is really just to ensure that we don't crash.
|
||||||
|
// FIXME: only one diagnostic, please
|
||||||
|
- (float) baz { // expected-warning {{method is expected to return an instance of its class type 'A4', but is declared to return 'float'}} expected-warning {{conflicting return type in implementation}}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
|
@ -175,7 +175,7 @@ void test_inference() {
|
||||||
@implementation Fail
|
@implementation Fail
|
||||||
- (id<X>) initWithX
|
- (id<X>) initWithX
|
||||||
{
|
{
|
||||||
return (id)self; // expected-warning {{returning 'Fail *' from a function with incompatible result type 'id<X>'}}
|
return (id)self; // expected-warning {{casting 'Fail *' to incompatible type 'id<X>'}}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
#if !__has_feature(objc_instancetype)
|
||||||
|
# error Missing 'instancetype' feature macro.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@interface Root
|
||||||
|
+ (instancetype)alloc;
|
||||||
|
- (instancetype)init; // expected-note{{overridden method is part of the 'init' method family}}
|
||||||
|
- (instancetype)self; // expected-note {{explicitly declared 'instancetype'}}
|
||||||
|
- (Class)class;
|
||||||
|
|
||||||
|
@property (assign) Root *selfProp;
|
||||||
|
- (instancetype)selfProp;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@protocol Proto1
|
||||||
|
@optional
|
||||||
|
- (instancetype)methodInProto1;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@protocol Proto2
|
||||||
|
@optional
|
||||||
|
- (instancetype)methodInProto2; // expected-note{{overridden method returns an instance of its class type}}
|
||||||
|
- (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface Subclass1 : Root
|
||||||
|
- (instancetype)initSubclass1;
|
||||||
|
- (void)methodOnSubclass1;
|
||||||
|
+ (instancetype)allocSubclass1;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface Subclass2 : Root
|
||||||
|
- (instancetype)initSubclass2;
|
||||||
|
- (void)methodOnSubclass2;
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Sanity check: the basic initialization pattern.
|
||||||
|
void test_instancetype_alloc_init_simple() {
|
||||||
|
Root *r1 = [[Root alloc] init];
|
||||||
|
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that message sends to instancetype methods have the right type.
|
||||||
|
void test_instancetype_narrow_method_search() {
|
||||||
|
// instancetype on class methods
|
||||||
|
Subclass1 *sc1 = [[Subclass1 alloc] initSubclass2]; // expected-warning{{'Subclass1' may not respond to 'initSubclass2'}}
|
||||||
|
Subclass2 *sc2 = [[Subclass2 alloc] initSubclass2]; // okay
|
||||||
|
|
||||||
|
// instancetype on instance methods
|
||||||
|
[[[Subclass1 alloc] init] methodOnSubclass2]; // expected-warning{{'Subclass1' may not respond to 'methodOnSubclass2'}}
|
||||||
|
[[[Subclass2 alloc] init] methodOnSubclass2];
|
||||||
|
|
||||||
|
// instancetype on class methods using protocols
|
||||||
|
typedef Subclass1<Proto1> SC1Proto1;
|
||||||
|
typedef Subclass1<Proto2> SC1Proto2;
|
||||||
|
[[SC1Proto1 alloc] methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}}
|
||||||
|
[[SC1Proto2 alloc] methodInProto2];
|
||||||
|
|
||||||
|
// instancetype on instance methods
|
||||||
|
Subclass1<Proto1> *sc1proto1 = 0;
|
||||||
|
[[sc1proto1 self] methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}}
|
||||||
|
Subclass1<Proto2> *sc1proto2 = 0;
|
||||||
|
[[sc1proto2 self] methodInProto2];
|
||||||
|
|
||||||
|
// Exact type checks
|
||||||
|
typeof([[Subclass1 alloc] init]) *ptr1 = (Subclass1 **)0;
|
||||||
|
typeof([[Subclass2 alloc] init]) *ptr2 = (Subclass2 **)0;
|
||||||
|
|
||||||
|
// Message sends to Class.
|
||||||
|
Subclass1<Proto1> *sc1proto1_2 = [[[sc1proto1 class] alloc] init];
|
||||||
|
|
||||||
|
// Property access
|
||||||
|
[sc1proto1.self methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}}
|
||||||
|
[sc1proto2.self methodInProto2];
|
||||||
|
[Subclass1.alloc initSubclass2]; // expected-warning{{'Subclass1' may not respond to 'initSubclass2'}}
|
||||||
|
[Subclass2.alloc initSubclass2];
|
||||||
|
|
||||||
|
[sc1proto1.selfProp methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}}
|
||||||
|
[sc1proto2.selfProp methodInProto2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that message sends to super methods have the right type.
|
||||||
|
@interface Subsubclass1 : Subclass1
|
||||||
|
- (instancetype)initSubclass1;
|
||||||
|
+ (instancetype)allocSubclass1;
|
||||||
|
|
||||||
|
- (void)onlyInSubsubclass1;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation Subsubclass1
|
||||||
|
- (instancetype)initSubclass1 {
|
||||||
|
// Check based on method search.
|
||||||
|
[[super initSubclass1] methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}}
|
||||||
|
[super.initSubclass1 methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}}
|
||||||
|
|
||||||
|
self = [super init]; // common pattern
|
||||||
|
|
||||||
|
// Exact type check.
|
||||||
|
typeof([super initSubclass1]) *ptr1 = (Subsubclass1**)0;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (instancetype)allocSubclass1 {
|
||||||
|
// Check based on method search.
|
||||||
|
[[super allocSubclass1] methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}}
|
||||||
|
|
||||||
|
// The ASTs don't model super property accesses well enough to get this right
|
||||||
|
[super.allocSubclass1 methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}}
|
||||||
|
|
||||||
|
// Exact type check.
|
||||||
|
typeof([super allocSubclass1]) *ptr1 = (Subsubclass1**)0;
|
||||||
|
|
||||||
|
return [super allocSubclass1];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onlyInSubsubclass1 {}
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Check compatibility rules for inheritance of related return types.
|
||||||
|
@class Subclass4;
|
||||||
|
|
||||||
|
@interface Subclass3 <Proto1, Proto2>
|
||||||
|
- (Subclass3 *)methodInProto1;
|
||||||
|
- (Subclass4 *)methodInProto2; // expected-warning{{method is expected to return an instance of its class type 'Subclass3', but is declared to return 'Subclass4 *'}}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface Subclass4 : Root
|
||||||
|
+ (Subclass4 *)alloc; // okay
|
||||||
|
- (Subclass3 *)init; // expected-warning{{method is expected to return an instance of its class type 'Subclass4', but is declared to return 'Subclass3 *'}}
|
||||||
|
- (id)self; // expected-note{{overridden method is part of the 'self' method family}}
|
||||||
|
- (instancetype)initOther;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@protocol Proto3 <Proto1, Proto2>
|
||||||
|
@optional
|
||||||
|
- (id)methodInProto1;
|
||||||
|
- (Subclass1 *)methodInProto2;
|
||||||
|
- (int)otherMethodInProto2; // expected-warning{{protocol method is expected to return an instance of the implementing class, but is declared to return 'int'}}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation Subclass4
|
||||||
|
+ (id)alloc {
|
||||||
|
return self; // FIXME: we accept this in ObjC++ but not ObjC?
|
||||||
|
}
|
||||||
|
|
||||||
|
- (Subclass3 *)init { return 0; } // don't complain: we lost the related return type
|
||||||
|
|
||||||
|
- (Subclass3 *)self { return 0; } // expected-warning{{method is expected to return an instance of its class type 'Subclass4', but is declared to return 'Subclass3 *'}}
|
||||||
|
|
||||||
|
- (Subclass4 *)initOther { return 0; }
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Check that inherited related return types influence the types of
|
||||||
|
// message sends.
|
||||||
|
void test_instancetype_inherited() {
|
||||||
|
[[Subclass4 alloc] initSubclass1]; // expected-warning{{'Subclass4' may not respond to 'initSubclass1'}}
|
||||||
|
[[Subclass4 alloc] initOther];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that related return types tighten up the semantics of
|
||||||
|
// Objective-C method implementations.
|
||||||
|
@implementation Subclass2
|
||||||
|
- (instancetype)initSubclass2 { // expected-note {{explicitly declared 'instancetype'}}
|
||||||
|
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
||||||
|
return sc1; // expected-error{{cannot initialize return object of type 'Subclass2 *' with an lvalue of type 'Subclass1 *'}}
|
||||||
|
}
|
||||||
|
- (void)methodOnSubclass2 {}
|
||||||
|
- (id)self {
|
||||||
|
Subclass1 *sc1 = [[Subclass1 alloc] init];
|
||||||
|
return sc1; // expected-error{{cannot initialize return object of type 'Subclass2 *' with an lvalue of type 'Subclass1 *'}}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MyClass : Root
|
||||||
|
+ (int)myClassMethod;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MyClass
|
||||||
|
+ (int)myClassMethod { return 0; }
|
||||||
|
|
||||||
|
- (void)blah {
|
||||||
|
int i = [[MyClass self] myClassMethod];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
// rdar://12493140
|
||||||
|
@protocol P4
|
||||||
|
- (instancetype) foo; // expected-note {{current method is explicitly declared 'instancetype' and is expected to return an instance of its class type}}
|
||||||
|
@end
|
||||||
|
@interface A4 : Root <P4>
|
||||||
|
- (instancetype) bar; // expected-note {{current method is explicitly declared 'instancetype' and is expected to return an instance of its class type}}
|
||||||
|
- (instancetype) baz; // expected-note {{overridden method returns an instance of its class type}} expected-note {{previous definition is here}}
|
||||||
|
@end
|
||||||
|
@interface B4 : Root @end
|
||||||
|
|
||||||
|
@implementation A4 {
|
||||||
|
B4 *_b;
|
||||||
|
}
|
||||||
|
- (id) foo {
|
||||||
|
return _b; // expected-error {{cannot initialize return object of type 'A4 *' with an lvalue of type 'B4 *'}}
|
||||||
|
}
|
||||||
|
- (id) bar {
|
||||||
|
return _b; // expected-error {{cannot initialize return object of type 'A4 *' with an lvalue of type 'B4 *'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is really just to ensure that we don't crash.
|
||||||
|
// FIXME: only one diagnostic, please
|
||||||
|
- (float) baz { // expected-warning {{method is expected to return an instance of its class type 'A4', but is declared to return 'float'}} expected-warning {{conflicting return type in implementation}}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@end
|
Загрузка…
Ссылка в новой задаче