Skip to content

Commit 2af3fd2

Browse files
authored
Merge pull request #58757 from ahoppen/pr-5.7/generate-memberwise-property-wrapper
[5.7][Refactoring] Several improvements to memberwise initializer generation
2 parents beb9eaf + 083b719 commit 2af3fd2

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
@@ -3157,26 +3157,24 @@ bool RefactoringActionLocalizeString::performChange() {
31573157
}
31583158

31593159
struct MemberwiseParameter {
3160-
Identifier Name;
3160+
CharSourceRange NameRange;
31613161
Type MemberType;
31623162
Expr *DefaultExpr;
31633163

3164-
MemberwiseParameter(Identifier name, Type type, Expr *initialExpr)
3165-
: Name(name), MemberType(type), DefaultExpr(initialExpr) {}
3164+
MemberwiseParameter(CharSourceRange nameRange, Type type, Expr *initialExpr)
3165+
: NameRange(nameRange), MemberType(type), DefaultExpr(initialExpr) {}
31663166
};
31673167

31683168
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
31693169
SourceManager &SM,
31703170
ArrayRef<MemberwiseParameter> memberVector,
31713171
SourceLoc targetLocation) {
31723172

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

3186+
bool HasAddedDefault = false;
31883187
if (auto *expr = memberData.DefaultExpr) {
3189-
if (isa<NilLiteralExpr>(expr)) {
3190-
OS << " = nil";
3191-
} else if (expr->getSourceRange().isValid()) {
3188+
if (expr->getSourceRange().isValid()) {
31923189
auto range =
31933190
Lexer::getCharSourceRangeFromSourceRange(
31943191
SM, expr->getSourceRange());
31953192
OS << " = " << SM.extractText(range);
3193+
HasAddedDefault = true;
31963194
}
31973195
}
3196+
if (!HasAddedDefault && memberData.MemberType->isOptional()) {
3197+
OS << " = nil";
3198+
}
31983199

31993200
if (wantsSeparator) {
32003201
OS << ", ";
@@ -3204,18 +3205,17 @@ static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
32043205
// Process the initial list of members, inserting commas as appropriate.
32053206
std::string Buffer;
32063207
llvm::raw_string_ostream OS(Buffer);
3207-
for (const auto &memberData : memberVector.drop_back()) {
3208-
insertMember(memberData, OS, /*wantsSeparator*/ true);
3208+
for (const auto &memberData : llvm::enumerate(memberVector)) {
3209+
bool wantsSeparator = (memberData.index() != memberVector.size() - 1);
3210+
insertMember(memberData.value(), OS, wantsSeparator);
32093211
}
32103212

3211-
// Process the last (or perhaps, only) member.
3212-
insertMember(memberVector.back(), OS, /*wantsSeparator*/ false);
3213-
32143213
// Synthesize the body.
32153214
OS << ") {\n";
32163215
for (auto &member : memberVector) {
32173216
// self.<property> = <property>
3218-
OS << "self." << member.Name << " = " << member.Name << "\n";
3217+
auto name = SM.extractText(member.NameRange);
3218+
OS << "self." << name << " = " << name << "\n";
32193219
}
32203220
OS << "}\n";
32213221

@@ -3244,30 +3244,41 @@ collectMembersForInit(const ResolvedCursorInfo &CursorInfo,
32443244
if (!targetLocation.isValid())
32453245
return SourceLoc();
32463246

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

32523262
if (!varDecl->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
32533263
continue;
32543264
}
32553265

3266+
auto patternBinding = varDecl->getParentPatternBinding();
3267+
if (!patternBinding)
3268+
continue;
3269+
32563270
const auto i = patternBinding->getPatternEntryIndexForVarDecl(varDecl);
32573271
Expr *defaultInit = nullptr;
32583272
if (patternBinding->isExplicitlyInitialized(i) ||
32593273
patternBinding->isDefaultInitializable()) {
3260-
defaultInit = varDecl->getParentInitializer();
3274+
defaultInit = patternBinding->getOriginalInit(i);
32613275
}
32623276

3263-
memberVector.emplace_back(varDecl->getName(),
3264-
varDecl->getType(), defaultInit);
3277+
auto NameRange =
3278+
Lexer::getCharSourceRangeFromSourceRange(SM, varDecl->getNameLoc());
3279+
memberVector.emplace_back(NameRange, varDecl->getType(), defaultInit);
32653280
}
3266-
3267-
if (memberVector.empty()) {
3268-
return SourceLoc();
3269-
}
3270-
3281+
32713282
return targetLocation;
32723283
}
32733284

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)