mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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.
This commit is contained in:
@@ -10191,6 +10191,10 @@ getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article,
|
|||||||
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article,
|
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article,
|
||||||
bool underscored);
|
bool underscored);
|
||||||
|
|
||||||
|
/// Retrieve a textual representation for a memberwise initializer in a given
|
||||||
|
/// nominal decl.
|
||||||
|
void printMemberwiseInit(NominalTypeDecl *nominal, llvm::raw_ostream &out);
|
||||||
|
|
||||||
void simple_display(llvm::raw_ostream &out,
|
void simple_display(llvm::raw_ostream &out,
|
||||||
OptionSet<NominalTypeDecl::LookupDirectFlags> options);
|
OptionSet<NominalTypeDecl::LookupDirectFlags> options);
|
||||||
|
|
||||||
|
|||||||
@@ -13114,6 +13114,76 @@ MacroDiscriminatorContext::getParentOf(FreestandingMacroExpansion *expansion) {
|
|||||||
expansion->getPoundLoc(), expansion->getDeclContext());
|
expansion->getPoundLoc(), expansion->getDeclContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void swift::printMemberwiseInit(NominalTypeDecl *nominal,
|
||||||
|
llvm::raw_ostream &out) {
|
||||||
|
auto &ctx = nominal->getASTContext();
|
||||||
|
auto &SM = ctx.SourceMgr;
|
||||||
|
|
||||||
|
auto printMemberName = [](Identifier name, llvm::raw_ostream &OS) {
|
||||||
|
if (escapeIdentifierInContext(name, PrintNameContext::TypeMember)) {
|
||||||
|
OS << '`' << name << '`';
|
||||||
|
} else {
|
||||||
|
OS << name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SmallVector<VarDecl *, 4> vars;
|
||||||
|
for (auto *var : nominal->getMemberwiseInitProperties()) {
|
||||||
|
// Skip printing lazy properties since we don't have a way of spelling
|
||||||
|
// their initialization in source (since we'd be initializing the backing
|
||||||
|
// variable).
|
||||||
|
if (var->getAttrs().hasAttribute<LazyAttr>())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vars.push_back(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "init(";
|
||||||
|
|
||||||
|
// Process the list of members, inserting commas as appropriate.
|
||||||
|
for (const auto &[idx, var] : llvm::enumerate(vars)) {
|
||||||
|
if (idx > 0)
|
||||||
|
out << ", ";
|
||||||
|
|
||||||
|
printMemberName(var->getName(), out);
|
||||||
|
out << ": ";
|
||||||
|
|
||||||
|
// Unconditionally print '@escaping' if we print out a function type -
|
||||||
|
// the assignments we generate below will escape this parameter.
|
||||||
|
auto ty = var->getTypeInContext();
|
||||||
|
if (ty->is<AnyFunctionType>())
|
||||||
|
out << "@" << TypeAttribute::getAttrName(TypeAttrKind::Escaping) << " ";
|
||||||
|
|
||||||
|
out << ty.getString();
|
||||||
|
|
||||||
|
bool hasAddedDefault = false;
|
||||||
|
if (auto *PBD = var->getParentPatternBinding()) {
|
||||||
|
auto idx = PBD->getPatternEntryIndexForVarDecl(var);
|
||||||
|
auto *init = PBD->getOriginalInit(idx);
|
||||||
|
if (auto range = init ? init->getSourceRange() : SourceRange()) {
|
||||||
|
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
|
||||||
|
out << " = " << SM.extractText(charRange);
|
||||||
|
hasAddedDefault = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasAddedDefault && ty->isOptional())
|
||||||
|
out << " = nil";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the body.
|
||||||
|
out << ") {\n";
|
||||||
|
for (auto *var : vars) {
|
||||||
|
// self.<property> = <property>
|
||||||
|
auto name = var->getName();
|
||||||
|
out << "self.";
|
||||||
|
printMemberName(name, out);
|
||||||
|
out << " = ";
|
||||||
|
printMemberName(name, out);
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
out << "}";
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Type>
|
std::optional<Type>
|
||||||
CatchNode::getThrownErrorTypeInContext(ASTContext &ctx) const {
|
CatchNode::getThrownErrorTypeInContext(ASTContext &ctx) const {
|
||||||
if (auto func = dyn_cast<AbstractFunctionDecl *>()) {
|
if (auto func = dyn_cast<AbstractFunctionDecl *>()) {
|
||||||
|
|||||||
@@ -14,154 +14,50 @@
|
|||||||
|
|
||||||
using namespace swift::refactoring;
|
using namespace swift::refactoring;
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct MemberwiseParameter {
|
|
||||||
Identifier Name;
|
|
||||||
Type MemberType;
|
|
||||||
Expr *DefaultExpr;
|
|
||||||
|
|
||||||
MemberwiseParameter(Identifier name, Type type, Expr *initialExpr)
|
|
||||||
: Name(name), MemberType(type), DefaultExpr(initialExpr) {}
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static void printMemberName(Identifier name, llvm::raw_ostream &OS) {
|
|
||||||
if (escapeIdentifierInContext(name, PrintNameContext::TypeMember)) {
|
|
||||||
OS << '`' << name << '`';
|
|
||||||
} else {
|
|
||||||
OS << name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
|
static void generateMemberwiseInit(SourceEditConsumer &EditConsumer,
|
||||||
SourceManager &SM,
|
SourceManager &SM, NominalTypeDecl *nominal,
|
||||||
ArrayRef<MemberwiseParameter> memberVector,
|
|
||||||
SourceLoc targetLocation) {
|
SourceLoc targetLocation) {
|
||||||
|
llvm::SmallString<64> buffer;
|
||||||
EditConsumer.accept(SM, targetLocation, "\ninternal init(");
|
llvm::raw_svector_ostream OS(buffer);
|
||||||
auto insertMember = [&SM](const MemberwiseParameter &memberData,
|
OS << "\ninternal ";
|
||||||
raw_ostream &OS, bool wantsSeparator) {
|
printMemberwiseInit(nominal, OS);
|
||||||
{
|
OS << "\n";
|
||||||
printMemberName(memberData.Name, OS);
|
|
||||||
OS << ": ";
|
|
||||||
// Unconditionally print '@escaping' if we print out a function type -
|
|
||||||
// the assignments we generate below will escape this parameter.
|
|
||||||
if (isa<AnyFunctionType>(memberData.MemberType->getCanonicalType())) {
|
|
||||||
OS << "@" << TypeAttribute::getAttrName(TypeAttrKind::Escaping) << " ";
|
|
||||||
}
|
|
||||||
OS << memberData.MemberType.getString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasAddedDefault = false;
|
|
||||||
if (auto *expr = memberData.DefaultExpr) {
|
|
||||||
if (expr->getSourceRange().isValid()) {
|
|
||||||
auto range = Lexer::getCharSourceRangeFromSourceRange(
|
|
||||||
SM, expr->getSourceRange());
|
|
||||||
OS << " = " << SM.extractText(range);
|
|
||||||
HasAddedDefault = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!HasAddedDefault && memberData.MemberType->isOptional()) {
|
|
||||||
OS << " = nil";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wantsSeparator) {
|
|
||||||
OS << ", ";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process the initial list of members, inserting commas as appropriate.
|
|
||||||
std::string Buffer;
|
|
||||||
llvm::raw_string_ostream OS(Buffer);
|
|
||||||
for (const auto &memberData : llvm::enumerate(memberVector)) {
|
|
||||||
bool wantsSeparator = (memberData.index() != memberVector.size() - 1);
|
|
||||||
insertMember(memberData.value(), OS, wantsSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synthesize the body.
|
|
||||||
OS << ") {\n";
|
|
||||||
for (auto &member : memberVector) {
|
|
||||||
// self.<property> = <property>
|
|
||||||
OS << "self.";
|
|
||||||
printMemberName(member.Name, OS);
|
|
||||||
OS << " = ";
|
|
||||||
printMemberName(member.Name, OS);
|
|
||||||
OS << "\n";
|
|
||||||
}
|
|
||||||
OS << "}\n";
|
|
||||||
|
|
||||||
// Accept the entire edit.
|
// Accept the entire edit.
|
||||||
EditConsumer.accept(SM, targetLocation, OS.str());
|
EditConsumer.accept(SM, targetLocation, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SourceLoc
|
static NominalTypeDecl *getMemberwiseNominal(ResolvedCursorInfoPtr CursorInfo) {
|
||||||
collectMembersForInit(ResolvedCursorInfoPtr CursorInfo,
|
|
||||||
SmallVectorImpl<MemberwiseParameter> &memberVector) {
|
|
||||||
auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
|
auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
|
||||||
if (!ValueRefInfo || !ValueRefInfo->getValueD())
|
if (!ValueRefInfo || !ValueRefInfo->getValueD())
|
||||||
return SourceLoc();
|
return nullptr;
|
||||||
|
|
||||||
NominalTypeDecl *nominalDecl =
|
auto *nominalDecl = dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD());
|
||||||
dyn_cast<NominalTypeDecl>(ValueRefInfo->getValueD());
|
|
||||||
if (!nominalDecl || nominalDecl->getStoredProperties().empty() ||
|
if (!nominalDecl || nominalDecl->getStoredProperties().empty() ||
|
||||||
ValueRefInfo->isRef()) {
|
ValueRefInfo->isRef()) {
|
||||||
return SourceLoc();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return nominalDecl;
|
||||||
SourceLoc bracesStart = nominalDecl->getBraces().Start;
|
|
||||||
if (!bracesStart.isValid())
|
|
||||||
return SourceLoc();
|
|
||||||
|
|
||||||
SourceLoc targetLocation = bracesStart.getAdvancedLoc(1);
|
|
||||||
if (!targetLocation.isValid())
|
|
||||||
return SourceLoc();
|
|
||||||
|
|
||||||
for (auto member : nominalDecl->getMemberwiseInitProperties()) {
|
|
||||||
auto varDecl = dyn_cast<VarDecl>(member);
|
|
||||||
if (!varDecl) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (varDecl->getAttrs().hasAttribute<LazyAttr>()) {
|
|
||||||
// Exclude lazy members from the memberwise initializer. This is
|
|
||||||
// inconsistent with the implicitly synthesized memberwise initializer but
|
|
||||||
// we think it makes more sense because otherwise the lazy variable's
|
|
||||||
// initializer gets evaluated eagerly.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto patternBinding = varDecl->getParentPatternBinding();
|
|
||||||
if (!patternBinding)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto i = patternBinding->getPatternEntryIndexForVarDecl(varDecl);
|
|
||||||
Expr *defaultInit = nullptr;
|
|
||||||
if (patternBinding->isExplicitlyInitialized(i) ||
|
|
||||||
patternBinding->isDefaultInitializable()) {
|
|
||||||
defaultInit = patternBinding->getOriginalInit(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
memberVector.emplace_back(varDecl->getName(), varDecl->getTypeInContext(),
|
|
||||||
defaultInit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetLocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefactoringActionMemberwiseInitLocalRefactoring::isApplicable(
|
bool RefactoringActionMemberwiseInitLocalRefactoring::isApplicable(
|
||||||
ResolvedCursorInfoPtr Tok, DiagnosticEngine &Diag) {
|
ResolvedCursorInfoPtr Tok, DiagnosticEngine &Diag) {
|
||||||
|
auto nominal = getMemberwiseNominal(Tok);
|
||||||
|
if (!nominal)
|
||||||
|
return false;
|
||||||
|
|
||||||
SmallVector<MemberwiseParameter, 8> memberVector;
|
return nominal->getBraces().isValid();
|
||||||
return collectMembersForInit(Tok, memberVector).isValid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RefactoringActionMemberwiseInitLocalRefactoring::performChange() {
|
bool RefactoringActionMemberwiseInitLocalRefactoring::performChange() {
|
||||||
|
auto nominal = getMemberwiseNominal(CursorInfo);
|
||||||
SmallVector<MemberwiseParameter, 8> memberVector;
|
if (!nominal)
|
||||||
SourceLoc targetLocation = collectMembersForInit(CursorInfo, memberVector);
|
|
||||||
if (targetLocation.isInvalid())
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
generateMemberwiseInit(EditConsumer, SM, memberVector, targetLocation);
|
auto targetLocation = nominal->getBraces().Start.getAdvancedLocOrInvalid(1);
|
||||||
|
if (!targetLocation.isValid())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
generateMemberwiseInit(EditConsumer, SM, nominal, targetLocation);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user