Skip to content

Commit 379ad6c

Browse files
authored
Merge pull request swiftlang#58718 from ahoppen/pr/generate-memberwise-property-wrapper
[Refactoring] Several improvements to memberwise initializer generation
2 parents bbd2848 + 4f970b4 commit 379ad6c

File tree

5 files changed

+174
-31
lines changed

5 files changed

+174
-31
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3152,26 +3152,24 @@ bool RefactoringActionLocalizeString::performChange() {
31523152
}
31533153

31543154
struct MemberwiseParameter {
3155-
Identifier Name;
3155+
CharSourceRange NameRange;
31563156
Type MemberType;
31573157
Expr *DefaultExpr;
31583158

3159-
MemberwiseParameter(Identifier name, Type type, Expr *initialExpr)
3160-
: Name(name), MemberType(type), DefaultExpr(initialExpr) {}
3159+
MemberwiseParameter(CharSourceRange nameRange, Type type, Expr *initialExpr)
3160+
: NameRange(nameRange), MemberType(type), DefaultExpr(initialExpr) {}
31613161
};
31623162

31633163
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
31643164
SourceManager &SM,
31653165
ArrayRef<MemberwiseParameter> memberVector,
31663166
SourceLoc targetLocation) {
31673167

3168-
assert(!memberVector.empty());
3169-
31703168
EditConsumer.accept(SM, targetLocation, "\ninternal init(");
31713169
auto insertMember = [&SM](const MemberwiseParameter &memberData,
31723170
raw_ostream &OS, bool wantsSeparator) {
31733171
{
3174-
OS << memberData.Name << ": ";
3172+
OS << SM.extractText(memberData.NameRange) << ": ";
31753173
// Unconditionally print '@escaping' if we print out a function type -
31763174
// the assignments we generate below will escape this parameter.
31773175
if (isa<AnyFunctionType>(memberData.MemberType->getCanonicalType())) {
@@ -3180,16 +3178,19 @@ static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
31803178
OS << memberData.MemberType.getString();
31813179
}
31823180

3181+
bool HasAddedDefault = false;
31833182
if (auto *expr = memberData.DefaultExpr) {
3184-
if (isa<NilLiteralExpr>(expr)) {
3185-
OS << " = nil";
3186-
} else if (expr->getSourceRange().isValid()) {
3183+
if (expr->getSourceRange().isValid()) {
31873184
auto range =
31883185
Lexer::getCharSourceRangeFromSourceRange(
31893186
SM, expr->getSourceRange());
31903187
OS << " = " << SM.extractText(range);
3188+
HasAddedDefault = true;
31913189
}
31923190
}
3191+
if (!HasAddedDefault && memberData.MemberType->isOptional()) {
3192+
OS << " = nil";
3193+
}
31933194

31943195
if (wantsSeparator) {
31953196
OS << ", ";
@@ -3199,18 +3200,17 @@ static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
31993200
// Process the initial list of members, inserting commas as appropriate.
32003201
std::string Buffer;
32013202
llvm::raw_string_ostream OS(Buffer);
3202-
for (const auto &memberData : memberVector.drop_back()) {
3203-
insertMember(memberData, OS, /*wantsSeparator*/ true);
3203+
for (const auto &memberData : llvm::enumerate(memberVector)) {
3204+
bool wantsSeparator = (memberData.index() != memberVector.size() - 1);
3205+
insertMember(memberData.value(), OS, wantsSeparator);
32043206
}
32053207

3206-
// Process the last (or perhaps, only) member.
3207-
insertMember(memberVector.back(), OS, /*wantsSeparator*/ false);
3208-
32093208
// Synthesize the body.
32103209
OS << ") {\n";
32113210
for (auto &member : memberVector) {
32123211
// self.<property> = <property>
3213-
OS << "self." << member.Name << " = " << member.Name << "\n";
3212+
auto name = SM.extractText(member.NameRange);
3213+
OS << "self." << name << " = " << name << "\n";
32143214
}
32153215
OS << "}\n";
32163216

@@ -3239,30 +3239,41 @@ collectMembersForInit(const ResolvedCursorInfo &CursorInfo,
32393239
if (!targetLocation.isValid())
32403240
return SourceLoc();
32413241

3242-
for (auto varDecl : nominalDecl->getStoredProperties()) {
3243-
auto patternBinding = varDecl->getParentPatternBinding();
3244-
if (!patternBinding)
3242+
SourceManager &SM = nominalDecl->getASTContext().SourceMgr;
3243+
3244+
for (auto member : nominalDecl->getMembers()) {
3245+
auto varDecl = dyn_cast<VarDecl>(member);
3246+
if (!varDecl) {
3247+
continue;
3248+
}
3249+
if (varDecl->getAttrs().hasAttribute<LazyAttr>()) {
3250+
// Exclude lazy members from the memberwise initializer. This is
3251+
// inconsistent with the implicitly synthesized memberwise initializer but
3252+
// we think it makes more sense because otherwise the lazy variable's
3253+
// initializer gets evaluated eagerly.
32453254
continue;
3255+
}
32463256

32473257
if (!varDecl->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
32483258
continue;
32493259
}
32503260

3261+
auto patternBinding = varDecl->getParentPatternBinding();
3262+
if (!patternBinding)
3263+
continue;
3264+
32513265
const auto i = patternBinding->getPatternEntryIndexForVarDecl(varDecl);
32523266
Expr *defaultInit = nullptr;
32533267
if (patternBinding->isExplicitlyInitialized(i) ||
32543268
patternBinding->isDefaultInitializable()) {
3255-
defaultInit = varDecl->getParentInitializer();
3269+
defaultInit = patternBinding->getOriginalInit(i);
32563270
}
32573271

3258-
memberVector.emplace_back(varDecl->getName(),
3259-
varDecl->getType(), defaultInit);
3272+
auto NameRange =
3273+
Lexer::getCharSourceRangeFromSourceRange(SM, varDecl->getNameLoc());
3274+
memberVector.emplace_back(NameRange, varDecl->getType(), defaultInit);
32603275
}
3261-
3262-
if (memberVector.empty()) {
3263-
return SourceLoc();
3264-
}
3265-
3276+
32663277
return targetLocation;
32673278
}
32683279

test/refactoring/MemberwiseInit/Outputs/generate_memberwise/class_members.swift.expected

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Person {
2-
internal init(firstName: String? = nil, lastName: String? = nil, age: Int? = nil, planet: String = "Earth", solarSystem: String = "Milky Way", avgHeight: Int = 175, location: @escaping () -> Place = { fatalError() }, secondLocation: (() -> Place)? = nil) {
2+
internal init(firstName: String? = nil, lastName: String? = nil, age: Int? = nil, planet: String = "Earth", solarSystem: String = "Milky Way", avgHeight: Int = 175, location: @escaping () -> Place = { fatalError() }, secondLocation: (() -> Place)? = nil, wrapped: String = "", didSet: String = "ds") {
33
self.firstName = firstName
44
self.lastName = lastName
55
self.age = age
@@ -8,6 +8,8 @@ self.solarSystem = solarSystem
88
self.avgHeight = avgHeight
99
self.location = location
1010
self.secondLocation = secondLocation
11+
self.wrapped = wrapped
12+
self.didSet = didSet
1113
}
1214

1315
var firstName: String!
@@ -19,6 +21,15 @@ self.secondLocation = secondLocation
1921
lazy var idea: Idea = { fatalError() }()
2022
var location: () -> Place = { fatalError() }
2123
var secondLocation: (() -> Place)!
24+
@MyWrapper var wrapped: String = ""
25+
var computed: String { "hi" }
26+
var getSet: String {
27+
get { "hi" }
28+
set {}
29+
}
30+
var didSet: String = "ds" {
31+
didSet { print("didSet") }
32+
}
2233
}
2334

2435
struct Place {
@@ -31,6 +42,8 @@ struct Place {
3142
let postalCode: Int
3243
let plusFour: Int?
3344
let callback: Callback
45+
@MyWrapper var wrapped: String
46+
var `protocol`: String
3447
}
3548

3649
protocol Thing {
@@ -41,6 +54,17 @@ enum Idea {
4154
var subject: String { fatalError() }
4255
}
4356

57+
struct OnlyComputed {
58+
lazy var idea: Idea = { fatalError() }()
59+
var computed: String { "hi" }
60+
}
61+
62+
@propertyWrapper
63+
struct MyWrapper {
64+
let wrappedValue: String
65+
}
66+
67+
4468

4569

4670

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
class Person {
2+
var firstName: String!
3+
var lastName: String!
4+
var age: Int!
5+
var planet = "Earth", solarSystem = "Milky Way"
6+
var avgHeight = 175
7+
let line = #line, file = #file, handle = #dsohandle
8+
lazy var idea: Idea = { fatalError() }()
9+
var location: () -> Place = { fatalError() }
10+
var secondLocation: (() -> Place)!
11+
@MyWrapper var wrapped: String = ""
12+
var computed: String { "hi" }
13+
var getSet: String {
14+
get { "hi" }
15+
set {}
16+
}
17+
var didSet: String = "ds" {
18+
didSet { print("didSet") }
19+
}
20+
}
21+
22+
struct Place {
23+
typealias Callback = () -> ()
24+
let person: Person
25+
let street: String
26+
let apartment: Optional<String>
27+
let city: String
28+
let state: String
29+
let postalCode: Int
30+
let plusFour: Int?
31+
let callback: Callback
32+
@MyWrapper var wrapped: String
33+
var `protocol`: String
34+
}
35+
36+
protocol Thing {
37+
var idea: Idea { get }
38+
}
39+
40+
enum Idea {
41+
var subject: String { fatalError() }
42+
}
43+
44+
struct OnlyComputed {
45+
internal init() {
46+
}
47+
48+
lazy var idea: Idea = { fatalError() }()
49+
var computed: String { "hi" }
50+
}
51+
52+
@propertyWrapper
53+
struct MyWrapper {
54+
let wrappedValue: String
55+
}
56+
57+
58+
59+
60+

test/refactoring/MemberwiseInit/Outputs/generate_memberwise/struct_members.swift.expected

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@ class Person {
88
lazy var idea: Idea = { fatalError() }()
99
var location: () -> Place = { fatalError() }
1010
var secondLocation: (() -> Place)!
11+
@MyWrapper var wrapped: String = ""
12+
var computed: String { "hi" }
13+
var getSet: String {
14+
get { "hi" }
15+
set {}
16+
}
17+
var didSet: String = "ds" {
18+
didSet { print("didSet") }
19+
}
1120
}
1221

1322
struct Place {
14-
internal init(person: Person, street: String, apartment: Optional<String>, city: String, state: String, postalCode: Int, plusFour: Int?, callback: @escaping Place.Callback) {
23+
internal init(person: Person, street: String, apartment: Optional<String> = nil, city: String, state: String, postalCode: Int, plusFour: Int? = nil, callback: @escaping Place.Callback, wrapped: String, `protocol`: String) {
1524
self.person = person
1625
self.street = street
1726
self.apartment = apartment
@@ -20,6 +29,8 @@ self.state = state
2029
self.postalCode = postalCode
2130
self.plusFour = plusFour
2231
self.callback = callback
32+
self.wrapped = wrapped
33+
self.`protocol` = `protocol`
2334
}
2435

2536
typealias Callback = () -> ()
@@ -31,6 +42,8 @@ self.callback = callback
3142
let postalCode: Int
3243
let plusFour: Int?
3344
let callback: Callback
45+
@MyWrapper var wrapped: String
46+
var `protocol`: String
3447
}
3548

3649
protocol Thing {
@@ -41,6 +54,17 @@ enum Idea {
4154
var subject: String { fatalError() }
4255
}
4356

57+
struct OnlyComputed {
58+
lazy var idea: Idea = { fatalError() }()
59+
var computed: String { "hi" }
60+
}
61+
62+
@propertyWrapper
63+
struct MyWrapper {
64+
let wrappedValue: String
65+
}
66+
67+
4468

4569

4670

test/refactoring/MemberwiseInit/generate_memberwise.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ class Person {
88
lazy var idea: Idea = { fatalError() }()
99
var location: () -> Place = { fatalError() }
1010
var secondLocation: (() -> Place)!
11+
@MyWrapper var wrapped: String = ""
12+
var computed: String { "hi" }
13+
var getSet: String {
14+
get { "hi" }
15+
set {}
16+
}
17+
var didSet: String = "ds" {
18+
didSet { print("didSet") }
19+
}
1120
}
1221

1322
struct Place {
@@ -20,6 +29,8 @@ struct Place {
2029
let postalCode: Int
2130
let plusFour: Int?
2231
let callback: Callback
32+
@MyWrapper var wrapped: String
33+
var `protocol`: String
2334
}
2435

2536
protocol Thing {
@@ -30,13 +41,26 @@ enum Idea {
3041
var subject: String { fatalError() }
3142
}
3243

44+
struct OnlyComputed {
45+
lazy var idea: Idea = { fatalError() }()
46+
var computed: String { "hi" }
47+
}
48+
49+
@propertyWrapper
50+
struct MyWrapper {
51+
let wrappedValue: String
52+
}
53+
3354
// RUN: %empty-directory(%t.result)
3455
// RUN: %refactor -memberwise-init -source-filename %s -pos=1:8 > %t.result/generate_memberwise.swift
3556
// RUN: diff -u %S/Outputs/generate_memberwise/class_members.swift.expected %t.result/generate_memberwise.swift
3657

37-
// RUN: %refactor -memberwise-init -source-filename %s -pos=13:8 > %t.result/struct_members.swift
58+
// RUN: %refactor -memberwise-init -source-filename %s -pos=22:8 > %t.result/struct_members.swift
3859
// RUN: diff -u %S/Outputs/generate_memberwise/struct_members.swift.expected %t.result/struct_members.swift
3960

40-
// RUN: not %refactor -memberwise-init -source-filename %s -pos=21:10 > %t.result/protocol_members.swift
41-
// RUN: not %refactor -memberwise-init -source-filename %s -pos=25:6 > %t.result/enum_members.swift
61+
// RUN: %refactor -memberwise-init -source-filename %s -pos=44:8 > %t.result/only_computed_members.swift
62+
// RUN: diff -u %S/Outputs/generate_memberwise/only_computed_members.swift.expected %t.result/only_computed_members.swift
63+
64+
// RUN: not %refactor -memberwise-init -source-filename %s -pos=36:10 > %t.result/protocol_members.swift
65+
// RUN: not %refactor -memberwise-init -source-filename %s -pos=40:6 > %t.result/enum_members.swift
4266

0 commit comments

Comments
 (0)