Skip to content

Commit c02fb8b

Browse files
committed
[AST] Factor out printMemberwiseInit
Move the printing logic into libAST such that it can be used both by the refactoring and by fix-it logic in Sema.
1 parent 01b965f commit c02fb8b

File tree

3 files changed

+96
-126
lines changed

3 files changed

+96
-126
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10191,6 +10191,10 @@ getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article,
1019110191
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article,
1019210192
bool underscored);
1019310193

10194+
/// Retrieve a textual representation for a memberwise initializer in a given
10195+
/// nominal decl.
10196+
void printMemberwiseInit(NominalTypeDecl *nominal, llvm::raw_ostream &out);
10197+
1019410198
void simple_display(llvm::raw_ostream &out,
1019510199
OptionSet<NominalTypeDecl::LookupDirectFlags> options);
1019610200

lib/AST/Decl.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13114,6 +13114,76 @@ MacroDiscriminatorContext::getParentOf(FreestandingMacroExpansion *expansion) {
1311413114
expansion->getPoundLoc(), expansion->getDeclContext());
1311513115
}
1311613116

13117+
void swift::printMemberwiseInit(NominalTypeDecl *nominal,
13118+
llvm::raw_ostream &out) {
13119+
auto &ctx = nominal->getASTContext();
13120+
auto &SM = ctx.SourceMgr;
13121+
13122+
auto printMemberName = [](Identifier name, llvm::raw_ostream &OS) {
13123+
if (escapeIdentifierInContext(name, PrintNameContext::TypeMember)) {
13124+
OS << '`' << name << '`';
13125+
} else {
13126+
OS << name;
13127+
}
13128+
};
13129+
13130+
SmallVector<VarDecl *, 4> vars;
13131+
for (auto *var : nominal->getMemberwiseInitProperties()) {
13132+
// Skip printing lazy properties since we don't have a way of spelling
13133+
// their initialization in source (since we'd be initializing the backing
13134+
// variable).
13135+
if (var->getAttrs().hasAttribute<LazyAttr>())
13136+
continue;
13137+
13138+
vars.push_back(var);
13139+
}
13140+
13141+
out << "init(";
13142+
13143+
// Process the list of members, inserting commas as appropriate.
13144+
for (const auto &[idx, var] : llvm::enumerate(vars)) {
13145+
if (idx > 0)
13146+
out << ", ";
13147+
13148+
printMemberName(var->getName(), out);
13149+
out << ": ";
13150+
13151+
// Unconditionally print '@escaping' if we print out a function type -
13152+
// the assignments we generate below will escape this parameter.
13153+
auto ty = var->getTypeInContext();
13154+
if (ty->is<AnyFunctionType>())
13155+
out << "@" << TypeAttribute::getAttrName(TypeAttrKind::Escaping) << " ";
13156+
13157+
out << ty.getString();
13158+
13159+
bool hasAddedDefault = false;
13160+
if (auto *PBD = var->getParentPatternBinding()) {
13161+
auto idx = PBD->getPatternEntryIndexForVarDecl(var);
13162+
auto *init = PBD->getOriginalInit(idx);
13163+
if (auto range = init ? init->getSourceRange() : SourceRange()) {
13164+
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
13165+
out << " = " << SM.extractText(charRange);
13166+
hasAddedDefault = true;
13167+
}
13168+
}
13169+
if (!hasAddedDefault && ty->isOptional())
13170+
out << " = nil";
13171+
}
13172+
13173+
// Print the body.
13174+
out << ") {\n";
13175+
for (auto *var : vars) {
13176+
// self.<property> = <property>
13177+
auto name = var->getName();
13178+
out << "self.";
13179+
printMemberName(name, out);
13180+
out << " = ";
13181+
printMemberName(name, out);
13182+
out << "\n";
13183+
}
13184+
out << "}";
13185+
}
13186+
1311713187
std::optional<Type>
1311813188
CatchNode::getThrownErrorTypeInContext(ASTContext &ctx) const {
1311913189
if (auto func = dyn_cast<AbstractFunctionDecl *>()) {

lib/Refactoring/MemberwiseInitLocalRefactoring.cpp

Lines changed: 22 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -14,154 +14,50 @@
1414

1515
using namespace swift::refactoring;
1616

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-
3617
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
37-
SourceManager &SM,
38-
ArrayRef<MemberwiseParameter> memberVector,
18+
SourceManager &SM, NominalTypeDecl *nominal,
3919
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";
9225

9326
// Accept the entire edit.
94-
EditConsumer.accept(SM, targetLocation, OS.str());
27+
EditConsumer.accept(SM, targetLocation, buffer);
9528
}
9629

97-
static SourceLoc
98-
collectMembersForInit(ResolvedCursorInfoPtr CursorInfo,
99-
SmallVectorImpl<MemberwiseParameter> &memberVector) {
30+
static NominalTypeDecl *getMemberwiseNominal(ResolvedCursorInfoPtr CursorInfo) {
10031
auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
10132
if (!ValueRefInfo || !ValueRefInfo->getValueD())
102-
return SourceLoc();
33+
return nullptr;
10334

104-
NominalTypeDecl *nominalDecl =
105-
dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD());
35+
auto *nominalDecl = dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD());
10636
if (!nominalDecl || nominalDecl->getStoredProperties().empty() ||
10737
ValueRefInfo->isRef()) {
108-
return SourceLoc();
38+
return nullptr;
10939
}
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;
14841
}
14942

15043
bool RefactoringActionMemberwiseInitLocalRefactoring::isApplicable(
15144
ResolvedCursorInfoPtr Tok, DiagnosticEngine &Diag) {
45+
auto nominal = getMemberwiseNominal(Tok);
46+
if (!nominal)
47+
return false;
15248

153-
SmallVector<MemberwiseParameter, 8> memberVector;
154-
return collectMembersForInit(Tok, memberVector).isValid();
49+
return nominal->getBraces().isValid();
15550
}
15651

15752
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)
16255
return true;
16356

164-
generateMemberwiseInit(EditConsumer, SM, memberVector, targetLocation);
57+
auto targetLocation = nominal->getBraces().Start.getAdvancedLocOrInvalid(1);
58+
if (!targetLocation.isValid())
59+
return true;
16560

61+
generateMemberwiseInit(EditConsumer, SM, nominal, targetLocation);
16662
return false;
16763
}

0 commit comments

Comments
 (0)