|
14 | 14 |
|
15 | 15 | using namespace swift::refactoring; |
16 | 16 |
|
17 | | -namespace { |
18 | | -struct MemberwiseParameter { |
19 | | - Identifier Name; |
20 | | - Type MemberType; |
21 | | - Expr *DefaultExpr; |
22 | | - |
23 | | - MemberwiseParameter(Identifier name, Type type, Expr *initialExpr) |
24 | | - : Name(name), MemberType(type), DefaultExpr(initialExpr) {} |
25 | | -}; |
26 | | -} // namespace |
27 | | - |
28 | | -static void printMemberName(Identifier name, llvm::raw_ostream &OS) { |
29 | | - if (escapeIdentifierInContext(name, PrintNameContext::TypeMember)) { |
30 | | - OS << '`' << name << '`'; |
31 | | - } else { |
32 | | - OS << name; |
33 | | - } |
34 | | -} |
35 | | - |
36 | 17 | static void generateMemberwiseInit(SourceEditConsumer &EditConsumer, |
37 | | - SourceManager &SM, |
38 | | - ArrayRef<MemberwiseParameter> memberVector, |
| 18 | + SourceManager &SM, NominalTypeDecl *nominal, |
39 | 19 | SourceLoc targetLocation) { |
40 | | - |
41 | | - EditConsumer.accept(SM, targetLocation, "\ninternal init("); |
42 | | - auto insertMember = [&SM](const MemberwiseParameter &memberData, |
43 | | - raw_ostream &OS, bool wantsSeparator) { |
44 | | - { |
45 | | - printMemberName(memberData.Name, OS); |
46 | | - OS << ": "; |
47 | | - // Unconditionally print '@escaping' if we print out a function type - |
48 | | - // the assignments we generate below will escape this parameter. |
49 | | - if (isa<AnyFunctionType>(memberData.MemberType->getCanonicalType())) { |
50 | | - OS << "@" << TypeAttribute::getAttrName(TypeAttrKind::Escaping) << " "; |
51 | | - } |
52 | | - OS << memberData.MemberType.getString(); |
53 | | - } |
54 | | - |
55 | | - bool HasAddedDefault = false; |
56 | | - if (auto *expr = memberData.DefaultExpr) { |
57 | | - if (expr->getSourceRange().isValid()) { |
58 | | - auto range = Lexer::getCharSourceRangeFromSourceRange( |
59 | | - SM, expr->getSourceRange()); |
60 | | - OS << " = " << SM.extractText(range); |
61 | | - HasAddedDefault = true; |
62 | | - } |
63 | | - } |
64 | | - if (!HasAddedDefault && memberData.MemberType->isOptional()) { |
65 | | - OS << " = nil"; |
66 | | - } |
67 | | - |
68 | | - if (wantsSeparator) { |
69 | | - OS << ", "; |
70 | | - } |
71 | | - }; |
72 | | - |
73 | | - // Process the initial list of members, inserting commas as appropriate. |
74 | | - std::string Buffer; |
75 | | - llvm::raw_string_ostream OS(Buffer); |
76 | | - for (const auto &memberData : llvm::enumerate(memberVector)) { |
77 | | - bool wantsSeparator = (memberData.index() != memberVector.size() - 1); |
78 | | - insertMember(memberData.value(), OS, wantsSeparator); |
79 | | - } |
80 | | - |
81 | | - // Synthesize the body. |
82 | | - OS << ") {\n"; |
83 | | - for (auto &member : memberVector) { |
84 | | - // self.<property> = <property> |
85 | | - OS << "self."; |
86 | | - printMemberName(member.Name, OS); |
87 | | - OS << " = "; |
88 | | - printMemberName(member.Name, OS); |
89 | | - OS << "\n"; |
90 | | - } |
91 | | - OS << "}\n"; |
| 20 | + llvm::SmallString<64> buffer; |
| 21 | + llvm::raw_svector_ostream OS(buffer); |
| 22 | + OS << "\ninternal "; |
| 23 | + printMemberwiseInit(nominal, OS); |
| 24 | + OS << "\n"; |
92 | 25 |
|
93 | 26 | // Accept the entire edit. |
94 | | - EditConsumer.accept(SM, targetLocation, OS.str()); |
| 27 | + EditConsumer.accept(SM, targetLocation, buffer); |
95 | 28 | } |
96 | 29 |
|
97 | | -static SourceLoc |
98 | | -collectMembersForInit(ResolvedCursorInfoPtr CursorInfo, |
99 | | - SmallVectorImpl<MemberwiseParameter> &memberVector) { |
| 30 | +static NominalTypeDecl *getMemberwiseNominal(ResolvedCursorInfoPtr CursorInfo) { |
100 | 31 | auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo); |
101 | 32 | if (!ValueRefInfo || !ValueRefInfo->getValueD()) |
102 | | - return SourceLoc(); |
| 33 | + return nullptr; |
103 | 34 |
|
104 | | - NominalTypeDecl *nominalDecl = |
105 | | - dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD()); |
| 35 | + auto *nominalDecl = dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD()); |
106 | 36 | if (!nominalDecl || nominalDecl->getStoredProperties().empty() || |
107 | 37 | ValueRefInfo->isRef()) { |
108 | | - return SourceLoc(); |
| 38 | + return nullptr; |
109 | 39 | } |
110 | | - |
111 | | - SourceLoc bracesStart = nominalDecl->getBraces().Start; |
112 | | - if (!bracesStart.isValid()) |
113 | | - return SourceLoc(); |
114 | | - |
115 | | - SourceLoc targetLocation = bracesStart.getAdvancedLoc(1); |
116 | | - if (!targetLocation.isValid()) |
117 | | - return SourceLoc(); |
118 | | - |
119 | | - for (auto member : nominalDecl->getMemberwiseInitProperties()) { |
120 | | - auto varDecl = dyn_cast<VarDecl>(member); |
121 | | - if (!varDecl) { |
122 | | - continue; |
123 | | - } |
124 | | - if (varDecl->getAttrs().hasAttribute<LazyAttr>()) { |
125 | | - // Exclude lazy members from the memberwise initializer. This is |
126 | | - // inconsistent with the implicitly synthesized memberwise initializer but |
127 | | - // we think it makes more sense because otherwise the lazy variable's |
128 | | - // initializer gets evaluated eagerly. |
129 | | - continue; |
130 | | - } |
131 | | - |
132 | | - auto patternBinding = varDecl->getParentPatternBinding(); |
133 | | - if (!patternBinding) |
134 | | - continue; |
135 | | - |
136 | | - const auto i = patternBinding->getPatternEntryIndexForVarDecl(varDecl); |
137 | | - Expr *defaultInit = nullptr; |
138 | | - if (patternBinding->isExplicitlyInitialized(i) || |
139 | | - patternBinding->isDefaultInitializable()) { |
140 | | - defaultInit = patternBinding->getOriginalInit(i); |
141 | | - } |
142 | | - |
143 | | - memberVector.emplace_back(varDecl->getName(), varDecl->getTypeInContext(), |
144 | | - defaultInit); |
145 | | - } |
146 | | - |
147 | | - return targetLocation; |
| 40 | + return nominalDecl; |
148 | 41 | } |
149 | 42 |
|
150 | 43 | bool RefactoringActionMemberwiseInitLocalRefactoring::isApplicable( |
151 | 44 | ResolvedCursorInfoPtr Tok, DiagnosticEngine &Diag) { |
| 45 | + auto nominal = getMemberwiseNominal(Tok); |
| 46 | + if (!nominal) |
| 47 | + return false; |
152 | 48 |
|
153 | | - SmallVector<MemberwiseParameter, 8> memberVector; |
154 | | - return collectMembersForInit(Tok, memberVector).isValid(); |
| 49 | + return nominal->getBraces().isValid(); |
155 | 50 | } |
156 | 51 |
|
157 | 52 | bool RefactoringActionMemberwiseInitLocalRefactoring::performChange() { |
158 | | - |
159 | | - SmallVector<MemberwiseParameter, 8> memberVector; |
160 | | - SourceLoc targetLocation = collectMembersForInit(CursorInfo, memberVector); |
161 | | - if (targetLocation.isInvalid()) |
| 53 | + auto nominal = getMemberwiseNominal(CursorInfo); |
| 54 | + if (!nominal) |
162 | 55 | return true; |
163 | 56 |
|
164 | | - generateMemberwiseInit(EditConsumer, SM, memberVector, targetLocation); |
| 57 | + auto targetLocation = nominal->getBraces().Start.getAdvancedLocOrInvalid(1); |
| 58 | + if (!targetLocation.isValid()) |
| 59 | + return true; |
165 | 60 |
|
| 61 | + generateMemberwiseInit(EditConsumer, SM, nominal, targetLocation); |
166 | 62 | return false; |
167 | 63 | } |
0 commit comments