Skip to content

Commit d0f0a05

Browse files
authored
Merge pull request #17506 from rudkx/rdar40068274-4.2
[4.2] [MiscDiagnostics] Emit a deprecation warning for some writes through …
2 parents 58e3abc + 99711e8 commit d0f0a05

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -485,6 +485,9 @@ ERROR(expr_keypath_subscript_index_not_hashable, none,
485485
ERROR(expr_smart_keypath_application_type_mismatch,none,
486486
"key path of type %0 cannot be applied to a base of type %1",
487487
(Type, Type))
488+
WARNING(expr_deprecated_writable_keypath,none,
489+
"forming a writable keypath to property %0 that is read-only in this context "
490+
"is deprecated and will be removed in a future release",(DeclName))
488491

489492
// Selector expressions.
490493
ERROR(expr_selector_no_objc_runtime,none,

lib/Sema/MiscDiagnostics.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3917,6 +3917,62 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E,
39173917
const_cast<Expr *>(E)->walk(Walker);
39183918
}
39193919

3920+
static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E,
3921+
const DeclContext *DC) {
3922+
if (!E || isa<ErrorExpr>(E) || !E->getType())
3923+
return;
3924+
3925+
class DeprecatedWritableKeyPathWalker : public ASTWalker {
3926+
TypeChecker &TC;
3927+
const DeclContext *DC;
3928+
3929+
void visitKeyPathApplicationExpr(KeyPathApplicationExpr *E) {
3930+
if (E->hasLValueAccessKind() &&
3931+
E->getLValueAccessKind() == AccessKind::Read)
3932+
return;
3933+
3934+
if (auto *keyPathExpr = dyn_cast<KeyPathExpr>(E->getKeyPath())) {
3935+
auto *decl = keyPathExpr->getType()->getNominalOrBoundGenericNominal();
3936+
if (decl != TC.Context.getWritableKeyPathDecl() &&
3937+
decl != TC.Context.getReferenceWritableKeyPathDecl())
3938+
return;
3939+
3940+
assert(keyPathExpr->getComponents().size() > 0);
3941+
auto &component = keyPathExpr->getComponents().back();
3942+
if (component.getKind() == KeyPathExpr::Component::Kind::Property) {
3943+
auto *storage =
3944+
cast<AbstractStorageDecl>(component.getDeclRef().getDecl());
3945+
if (!storage->isSettable(nullptr) ||
3946+
!storage->isSetterAccessibleFrom(DC)) {
3947+
TC.diagnose(keyPathExpr->getLoc(),
3948+
swift::diag::expr_deprecated_writable_keypath,
3949+
storage->getFullName());
3950+
}
3951+
}
3952+
}
3953+
}
3954+
3955+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
3956+
if (!E || isa<ErrorExpr>(E) || !E->getType())
3957+
return {false, E};
3958+
3959+
if (auto *KPAE = dyn_cast<KeyPathApplicationExpr>(E)) {
3960+
visitKeyPathApplicationExpr(KPAE);
3961+
return {true, E};
3962+
}
3963+
3964+
return {true, E};
3965+
}
3966+
3967+
public:
3968+
DeprecatedWritableKeyPathWalker(TypeChecker &TC, const DeclContext *DC)
3969+
: TC(TC), DC(DC) {}
3970+
};
3971+
3972+
DeprecatedWritableKeyPathWalker Walker(TC, DC);
3973+
const_cast<Expr *>(E)->walk(Walker);
3974+
}
3975+
39203976
//===----------------------------------------------------------------------===//
39213977
// High-level entry points.
39223978
//===----------------------------------------------------------------------===//
@@ -3930,6 +3986,8 @@ void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E,
39303986
diagRecursivePropertyAccess(TC, E, DC);
39313987
diagnoseImplicitSelfUseInClosure(TC, E, DC);
39323988
diagnoseUnintendedOptionalBehavior(TC, E, DC);
3989+
if (!TC.Context.isSwiftVersionAtLeast(5))
3990+
diagnoseDeprecatedWritableKeyPath(TC, E, DC);
39333991
if (!TC.getLangOpts().DisableAvailabilityChecking)
39343992
diagAvailability(TC, E, const_cast<DeclContext*>(DC));
39353993
if (TC.Context.LangOpts.EnableObjCInterop)

test/Constraints/keypath.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ struct S {
55

66
init() {
77
let _: WritableKeyPath<S, Int> = \.i // no error for Swift 3/4
8+
9+
S()[keyPath: \S.i] = 1
10+
// expected-error@-1 {{cannot assign to immutable expression}}
811
}
912
}
1013

1114
func test() {
1215
let _: WritableKeyPath<C, Int> = \.i // no error for Swift 3/4
16+
17+
C()[keyPath: \C.i] = 1 // warning on write with literal keypath
18+
// expected-warning@-1 {{forming a writable keypath to property}}
19+
20+
let _ = C()[keyPath: \C.i] // no warning for a read
1321
}

test/Constraints/keypath_swift_5.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ struct S {
55

66
init() {
77
let _: WritableKeyPath<S, Int> = \.i // expected-error {{type of expression is ambiguous without more context}}
8+
9+
S()[keyPath: \S.i] = 1
10+
// expected-error@-1 {{cannot assign to immutable expression}}
811
}
912
}
1013

1114
func test() {
1215
let _: WritableKeyPath<C, Int> = \.i // expected-error {{type of expression is ambiguous without more context}}
16+
17+
C()[keyPath: \C.i] = 1
18+
// expected-error@-1 {{cannot assign to immutable expression}}
19+
20+
let _ = C()[keyPath: \C.i] // no warning for a read
1321
}

0 commit comments

Comments
 (0)