Skip to content

Commit 2309ee5

Browse files
authored
Merge pull request #24617 from sl/sl/sr-10297
Add a Diagnostic for .init calls which should be transformed into assignments (SR-10297)
2 parents 7ba9d65 + a13dba9 commit 2309ee5

File tree

5 files changed

+131
-12
lines changed

5 files changed

+131
-12
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,10 @@ ERROR(did_not_call_method,none,
857857
"method %0 was used as a property; add () to call it",
858858
(Identifier))
859859

860+
ERROR(init_not_instance_member_use_assignment,none,
861+
"'init' is a member of the type; use assignment "
862+
"to initalize the value instead", ())
863+
860864
ERROR(init_not_instance_member,none,
861865
"'init' is a member of the type; use 'type(of: ...)' to initialize "
862866
"a new object of the same dynamic type", ())

lib/Sema/CSDiagnostics.cpp

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,35 @@ Expr *FailureDiagnostic::getArgumentExprFor(Expr *anchor) const {
101101
return nullptr;
102102
}
103103

104+
// TODO: Replace duplications of this logic with calls to this.
105+
Optional<SelectedOverload> FailureDiagnostic::getChoiceFor(Expr *expr) {
106+
auto &cs = getConstraintSystem();
107+
ConstraintLocator *locator = nullptr;
108+
109+
if (auto *AE = dyn_cast<ApplyExpr>(expr)) {
110+
if (auto *TE = dyn_cast<TypeExpr>(AE->getFn())) {
111+
locator = cs.getConstraintLocator(AE,
112+
{ConstraintLocator::ApplyFunction,
113+
ConstraintLocator::ConstructorMember},
114+
/*summaryFlags=*/0);
115+
}
116+
return getChoiceFor(AE->getFn());
117+
} else if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
118+
locator = cs.getConstraintLocator(UDE, ConstraintLocator::Member);
119+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(expr)) {
120+
locator = cs.getConstraintLocator(UME, ConstraintLocator::UnresolvedMember);
121+
} else if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
122+
locator = cs.getConstraintLocator(SE, ConstraintLocator::SubscriptMember);
123+
} else {
124+
locator = cs.getConstraintLocator(expr);
125+
}
126+
127+
if (!locator)
128+
return None;
129+
130+
return getOverloadChoiceIfAvailable(locator);
131+
}
132+
104133
Type RequirementFailure::getOwnerType() const {
105134
return getType(getRawAnchor())
106135
->getInOutObjectType()
@@ -2019,6 +2048,7 @@ bool MissingMemberFailure::diagnoseAsError() {
20192048
bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
20202049
auto loc = getAnchor()->getLoc();
20212050
auto &cs = getConstraintSystem();
2051+
auto *DC = getDC();
20222052
auto locator = getLocator();
20232053

20242054
if (loc.isInvalid()) {
@@ -2076,11 +2106,43 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
20762106
emitDiagnostic(loc, diag::super_initializer_not_in_initializer);
20772107
return true;
20782108
}
2079-
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
2080-
emitDiagnostic(loc, diag::init_not_instance_member)
2081-
.fixItInsert(fixItRng.Start, "type(of: ")
2082-
.fixItInsertAfter(fixItRng.End, ")");
2083-
return true;
2109+
2110+
auto isCallArgument = [this](Expr *expr) {
2111+
auto &cs = getConstraintSystem();
2112+
auto argExpr = cs.getParentExpr(expr);
2113+
if (!argExpr)
2114+
return false;
2115+
auto possibleApplyExpr = cs.getParentExpr(expr);
2116+
return possibleApplyExpr && isa<ApplyExpr>(possibleApplyExpr);
2117+
};
2118+
2119+
auto *initCall = cs.getParentExpr(cs.getParentExpr(ctorRef));
2120+
2121+
auto isMutable = [&DC](ValueDecl *decl) {
2122+
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl))
2123+
return storage->isSettable(DC) && storage->isSetterAccessibleFrom(DC);
2124+
2125+
return true;
2126+
};
2127+
2128+
auto selection = getChoiceFor(ctorRef->getBase());
2129+
if (selection) {
2130+
OverloadChoice choice = selection->choice;
2131+
if (choice.isDecl() && isMutable(choice.getDecl()) &&
2132+
!isCallArgument(initCall) &&
2133+
cs.getContextualTypePurpose() == CTP_Unused) {
2134+
auto fixItLoc = ctorRef->getBase()->getSourceRange().End;
2135+
emitDiagnostic(loc, diag::init_not_instance_member_use_assignment)
2136+
.fixItInsertAfter(fixItLoc, " = ");
2137+
return true;
2138+
}
2139+
2140+
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
2141+
emitDiagnostic(loc, diag::init_not_instance_member)
2142+
.fixItInsert(fixItRng.Start, "type(of: ")
2143+
.fixItInsertAfter(fixItRng.End, ")");
2144+
return true;
2145+
}
20842146
}
20852147
}
20862148

lib/Sema/CSDiagnostics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ class FailureDiagnostic {
155155
/// reference or subscript, nullptr otherwise.
156156
Expr *getArgumentExprFor(Expr *anchor) const;
157157

158+
Optional<SelectedOverload> getChoiceFor(Expr *);
159+
158160
private:
159161
/// Compute anchor expression associated with current diagnostic.
160162
std::pair<Expr *, bool> computeAnchor() const;

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2646,9 +2646,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
26462646
auto contextualPurpose = CTP_Unused;
26472647
TypeCheckExprOptions flags = TypeCheckExprFlags::ConvertTypeIsOnlyAHint;
26482648

2649+
// Set the contextual purpose even if the pattern doesn't have a type so
2650+
// if there's an error we can use that information to inform diagnostics.
2651+
contextualPurpose = CTP_Initialization;
2652+
26492653
if (pattern->hasType()) {
26502654
contextualType = TypeLoc::withoutLoc(pattern->getType());
2651-
contextualPurpose = CTP_Initialization;
26522655

26532656
// If we already had an error, don't repeat the problem.
26542657
if (contextualType.getType()->hasError())

test/expr/postfix/dot/init_ref_delegation.swift

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,23 @@ func foo<T: C>(_ x: T, y: T.Type) where T: P {
273273
var ci1 = x.init(required: 0) // expected-error{{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{15-15=type(of: }} {{19-19=)}}
274274
var ci2 = x.init(x: 0) // expected-error{{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{15-15=type(of: }} {{19-19=)}}
275275
var ci3 = x.init() // expected-error{{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{15-15=type(of: }} {{19-19=)}}
276-
var ci4 = x.init(proto: "") // expected-error{{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{15-15=type(of: }} {{19-19=)}}
277-
278-
var ci1a = x(required: 0) // expected-error{{cannot call value of non-function type 'T'}}
279-
var ci2a = x(x: 0) // expected-error{{cannot call value of non-function type 'T'}}
280-
var ci3a = x() // expected-error{{cannot call value of non-function type 'T'}}{{15-17=}}
281-
var ci4a = x(proto: "") // expected-error{{cannot call value of non-function type 'T'}}
276+
var ci4 = x.init(proto: "") // expected-error{{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{15-15=type(of: }} {{19-19=)}}
277+
278+
var z = x
279+
z.init(required: 0) // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{4-4= = }}
280+
z.init(x: 0) // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{4-4= = }}
281+
z.init() // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{4-4= = }}
282+
z.init(proto: "") // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{4-4= = }}
283+
284+
var ci1a = z.init(required: 0) // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{16-16=type(of: }} {{20-20=)}}
285+
var ci2a = z.init(x: 0) // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{16-16=type(of: }} {{20-20=)}}
286+
var ci3a = z.init() // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{16-16=type(of: }} {{20-20=)}}
287+
var ci4a = z.init(proto: "") // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{16-16=type(of: }} {{20-20=)}}
288+
289+
var ci1b = x(required: 0) // expected-error{{cannot call value of non-function type 'T'}}
290+
var ci2b = x(x: 0) // expected-error{{cannot call value of non-function type 'T'}}
291+
var ci3b = x() // expected-error{{cannot call value of non-function type 'T'}}{{15-17=}}
292+
var ci4b = x(proto: "") // expected-error{{cannot call value of non-function type 'T'}}
282293

283294
var cm1 = y.init(required: 0)
284295
var cm2 = y.init(x: 0) // expected-error{{'required' initializer}}
@@ -499,3 +510,40 @@ class TestOptionalTrySub : TestOptionalTry {
499510
// expected-note@-1 {{force potentially-failing result with 'try!'}} {{5-9=try!}}
500511
}
501512
}
513+
514+
struct X { init() {} }
515+
516+
func +(lhs: X, rhs: X) -> X { return lhs }
517+
func testInsideOperator(x: X) {
518+
x.init() + x // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{5-5=type(of: }} {{9-9=)}}
519+
x + x.init() // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{9-9=type(of: }} {{13-13=)}}
520+
x.init() + x.init() // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{5-5=type(of: }} {{9-9=)}}
521+
// expected-error@-1 {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{16-16=type(of: }} {{20-20=)}}
522+
}
523+
524+
struct Y {
525+
var x: X
526+
let x2: X
527+
528+
init() {
529+
x.init() // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{6-6= = }}
530+
foo(x.init()) // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{11-11=type(of: }} {{15-15=)}}
531+
}
532+
533+
func foo(_: X) {}
534+
func asFunctionReturn() -> X {
535+
var a = X()
536+
return a.init() // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{14-14=type(of: }} {{18-18=)}}
537+
}
538+
}
539+
540+
struct MultipleMemberAccesses {
541+
var y: Y
542+
let y2: Y
543+
init() {
544+
y = Y()
545+
y2 = Y()
546+
y.x.init() // expected-error {{'init' is a member of the type; use assignment to initalize the value instead}} {{8-8= = }}
547+
y2.x2.init() // expected-error {{'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type}} {{11-11=type(of: }} {{15-15=)}}
548+
}
549+
}

0 commit comments

Comments
 (0)