Skip to content

Commit f0e73c8

Browse files
authored
Merge pull request #26480 from CodaFi/makes-a-nominal-decl-healthy-wealthy-and-memberwise
[FB6918951] Expand "Make Memberwise Initializer" Refactoring Action
2 parents 9d73ead + 546734a commit f0e73c8

File tree

6 files changed

+202
-86
lines changed

6 files changed

+202
-86
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 83 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,78 +2753,107 @@ bool RefactoringActionLocalizeString::performChange() {
27532753
return false;
27542754
}
27552755

2756+
struct MemberwiseParameter {
2757+
Identifier Name;
2758+
Type MemberType;
2759+
Expr *DefaultExpr;
2760+
2761+
MemberwiseParameter(Identifier name, Type type, Expr *initialExpr)
2762+
: Name(name), MemberType(type), DefaultExpr(initialExpr) {}
2763+
};
2764+
27562765
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
2757-
SourceManager &SM,
2758-
SmallVectorImpl<std::string>& memberNameVector,
2759-
SmallVectorImpl<std::string>& memberTypeVector,
2760-
SourceLoc targetLocation) {
2761-
2762-
assert(!memberTypeVector.empty());
2763-
assert(memberTypeVector.size() == memberNameVector.size());
2766+
SourceManager &SM,
2767+
ArrayRef<MemberwiseParameter> memberVector,
2768+
SourceLoc targetLocation) {
27642769

2770+
assert(!memberVector.empty());
2771+
27652772
EditConsumer.accept(SM, targetLocation, "\ninternal init(");
2766-
2767-
for (size_t i = 0, n = memberTypeVector.size(); i < n ; i++) {
2768-
EditConsumer.accept(SM, targetLocation, memberNameVector[i] + ": " +
2769-
memberTypeVector[i]);
2770-
2771-
if (i != memberTypeVector.size() - 1) {
2772-
EditConsumer.accept(SM, targetLocation, ", ");
2773+
auto insertMember = [&SM](const MemberwiseParameter &memberData,
2774+
llvm::raw_ostream &OS, bool wantsSeparator) {
2775+
OS << memberData.Name << ": " << memberData.MemberType.getString();
2776+
if (auto *expr = memberData.DefaultExpr) {
2777+
if (isa<NilLiteralExpr>(expr)) {
2778+
OS << " = nil";
2779+
} else if (expr->getSourceRange().isValid()) {
2780+
auto range =
2781+
Lexer::getCharSourceRangeFromSourceRange(
2782+
SM, expr->getSourceRange());
2783+
OS << " = " << SM.extractText(range);
2784+
}
2785+
}
2786+
2787+
if (wantsSeparator) {
2788+
OS << ", ";
27732789
}
2790+
};
2791+
2792+
// Process the initial list of members, inserting commas as appropriate.
2793+
std::string Buffer;
2794+
llvm::raw_string_ostream OS(Buffer);
2795+
for (const auto &memberData : memberVector.drop_back()) {
2796+
insertMember(memberData, OS, /*wantsSeparator*/ true);
27742797
}
2775-
2776-
EditConsumer.accept(SM, targetLocation, ") {\n");
2777-
2778-
for (auto varName: memberNameVector) {
2779-
EditConsumer.accept(SM, targetLocation,
2780-
"self." + varName + " = " + varName + "\n");
2798+
2799+
// Process the last (or perhaps, only) member.
2800+
insertMember(memberVector.back(), OS, /*wantsSeparator*/ false);
2801+
2802+
// Synthesize the body.
2803+
OS << ") {\n";
2804+
for (auto &member : memberVector) {
2805+
// self.<property> = <property>
2806+
OS << "self." << member.Name << " = " << member.Name << "\n";
27812807
}
2782-
2783-
EditConsumer.accept(SM, targetLocation, "}\n");
2808+
OS << "}\n";
2809+
2810+
// Accept the entire edit.
2811+
EditConsumer.accept(SM, targetLocation, OS.str());
27842812
}
2785-
2786-
static SourceLoc collectMembersForInit(ResolvedCursorInfo CursorInfo,
2787-
SmallVectorImpl<std::string>& memberNameVector,
2788-
SmallVectorImpl<std::string>& memberTypeVector) {
2789-
2813+
2814+
static SourceLoc
2815+
collectMembersForInit(ResolvedCursorInfo CursorInfo,
2816+
SmallVectorImpl<MemberwiseParameter> &memberVector) {
2817+
27902818
if (!CursorInfo.ValueD)
27912819
return SourceLoc();
27922820

2793-
ClassDecl *classDecl = dyn_cast<ClassDecl>(CursorInfo.ValueD);
2794-
if (!classDecl || classDecl->getStoredProperties().empty() ||
2821+
NominalTypeDecl *nominalDecl = dyn_cast<NominalTypeDecl>(CursorInfo.ValueD);
2822+
if (!nominalDecl || nominalDecl->getStoredProperties().empty() ||
27952823
CursorInfo.IsRef) {
27962824
return SourceLoc();
27972825
}
2798-
2799-
SourceLoc bracesStart = classDecl->getBraces().Start;
2826+
2827+
SourceLoc bracesStart = nominalDecl->getBraces().Start;
28002828
if (!bracesStart.isValid())
28012829
return SourceLoc();
28022830

28032831
SourceLoc targetLocation = bracesStart.getAdvancedLoc(1);
28042832
if (!targetLocation.isValid())
28052833
return SourceLoc();
2806-
2807-
for (auto varDecl : classDecl->getStoredProperties()) {
2808-
auto parentPatternBinding = varDecl->getParentPatternBinding();
2809-
if (!parentPatternBinding)
2834+
2835+
for (auto varDecl : nominalDecl->getStoredProperties()) {
2836+
auto patternBinding = varDecl->getParentPatternBinding();
2837+
if (!patternBinding)
2838+
continue;
2839+
2840+
if (!varDecl->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
28102841
continue;
2811-
2812-
auto varDeclIndex =
2813-
parentPatternBinding->getPatternEntryIndexForVarDecl(varDecl);
2814-
2815-
if (auto init = varDecl->getParentPatternBinding()->getInit(varDeclIndex)) {
2816-
if (init->getStartLoc().isValid())
2817-
continue;
28182842
}
2819-
2820-
StringRef memberName = varDecl->getName().str();
2821-
memberNameVector.push_back(memberName.str());
2822-
2823-
std::string memberType = varDecl->getType().getString();
2824-
memberTypeVector.push_back(memberType);
2843+
2844+
auto &entry = patternBinding->getPatternEntryForVarDecl(varDecl);
2845+
bool isExplicitlyInitialized =
2846+
entry.isInitialized() && entry.getEqualLoc().isValid();
2847+
Expr *defaultInit = nullptr;
2848+
if (isExplicitlyInitialized || patternBinding->isDefaultInitializable()) {
2849+
defaultInit = varDecl->getParentInitializer();
2850+
}
2851+
2852+
memberVector.emplace_back(varDecl->getName(),
2853+
varDecl->getType(), defaultInit);
28252854
}
28262855

2827-
if (memberNameVector.empty() || memberTypeVector.empty()) {
2856+
if (memberVector.empty()) {
28282857
return SourceLoc();
28292858
}
28302859

@@ -2834,25 +2863,18 @@ static SourceLoc collectMembersForInit(ResolvedCursorInfo CursorInfo,
28342863
bool RefactoringActionMemberwiseInitLocalRefactoring::
28352864
isApplicable(ResolvedCursorInfo Tok, DiagnosticEngine &Diag) {
28362865

2837-
SmallVector<std::string, 8> memberNameVector;
2838-
SmallVector<std::string, 8> memberTypeVector;
2839-
2840-
return collectMembersForInit(Tok, memberNameVector,
2841-
memberTypeVector).isValid();
2866+
SmallVector<MemberwiseParameter, 8> memberVector;
2867+
return collectMembersForInit(Tok, memberVector).isValid();
28422868
}
28432869

28442870
bool RefactoringActionMemberwiseInitLocalRefactoring::performChange() {
28452871

2846-
SmallVector<std::string, 8> memberNameVector;
2847-
SmallVector<std::string, 8> memberTypeVector;
2848-
2849-
SourceLoc targetLocation = collectMembersForInit(CursorInfo, memberNameVector,
2850-
memberTypeVector);
2872+
SmallVector<MemberwiseParameter, 8> memberVector;
2873+
SourceLoc targetLocation = collectMembersForInit(CursorInfo, memberVector);
28512874
if (targetLocation.isInvalid())
28522875
return true;
28532876

2854-
generateMemberwiseInit(EditConsumer, SM, memberNameVector,
2855-
memberTypeVector, targetLocation);
2877+
generateMemberwiseInit(EditConsumer, SM, memberVector, targetLocation);
28562878

28572879
return false;
28582880
}

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

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class Person {
2+
internal init(firstName: String? = nil, lastName: String? = nil, age: Int? = nil, planet: String = "Earth", solarSystem: String = "Milky Way", avgHeight: Int = 175) {
3+
self.firstName = firstName
4+
self.lastName = lastName
5+
self.age = age
6+
self.planet = planet
7+
self.solarSystem = solarSystem
8+
self.avgHeight = avgHeight
9+
}
10+
11+
var firstName: String!
12+
var lastName: String!
13+
var age: Int!
14+
var planet = "Earth", solarSystem = "Milky Way"
15+
var avgHeight = 175
16+
let line = #line, file = #file, handle = #dsohandle
17+
lazy var idea: Idea = { fatalError() }()
18+
}
19+
20+
struct Place {
21+
let person: Person
22+
let street: String
23+
let apartment: Optional<String>
24+
let city: String
25+
let state: String
26+
let postalCode: Int
27+
let plusFour: Int?
28+
}
29+
30+
protocol Thing {
31+
var idea: Idea { get }
32+
}
33+
34+
enum Idea {
35+
var subject: String { fatalError() }
36+
}
37+
38+
39+
40+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
}
10+
11+
struct Place {
12+
internal init(person: Person, street: String, apartment: Optional<String>, city: String, state: String, postalCode: Int, plusFour: Int?) {
13+
self.person = person
14+
self.street = street
15+
self.apartment = apartment
16+
self.city = city
17+
self.state = state
18+
self.postalCode = postalCode
19+
self.plusFour = plusFour
20+
}
21+
22+
let person: Person
23+
let street: String
24+
let apartment: Optional<String>
25+
let city: String
26+
let state: String
27+
let postalCode: Int
28+
let plusFour: Int?
29+
}
30+
31+
protocol Thing {
32+
var idea: Idea { get }
33+
}
34+
35+
enum Idea {
36+
var subject: String { fatalError() }
37+
}
38+
39+
40+
41+

test/refactoring/MemberwiseInit/class_members.swift

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
}
10+
11+
struct Place {
12+
let person: Person
13+
let street: String
14+
let apartment: Optional<String>
15+
let city: String
16+
let state: String
17+
let postalCode: Int
18+
let plusFour: Int?
19+
}
20+
21+
protocol Thing {
22+
var idea: Idea { get }
23+
}
24+
25+
enum Idea {
26+
var subject: String { fatalError() }
27+
}
28+
29+
// RUN: %empty-directory(%t.result)
30+
// RUN: %refactor -memberwise-init -source-filename %s -pos=1:8 > %t.result/generate_memberwise.swift
31+
// RUN: diff -u %S/Outputs/generate_memberwise/class_members.swift.expected %t.result/generate_memberwise.swift
32+
33+
// RUN: %refactor -memberwise-init -source-filename %s -pos=11:8 > %t.result/struct_members.swift
34+
// RUN: diff -u %S/Outputs/generate_memberwise/struct_members.swift.expected %t.result/struct_members.swift
35+
36+
// RUN: not %refactor -memberwise-init -source-filename %s -pos=21:10 > %t.result/protocol_members.swift
37+
// RUN: not %refactor -memberwise-init -source-filename %s -pos=25:6 > %t.result/enum_members.swift
38+

0 commit comments

Comments
 (0)