Skip to content

Commit 38f2e6c

Browse files
authored
Merge pull request swiftlang#65450 from bnbarham/format-macro-expansions
[Macros] Automatically format expanded macros
2 parents ac218c2 + fe2104c commit 38f2e6c

File tree

9 files changed

+306
-100
lines changed

9 files changed

+306
-100
lines changed

include/swift/Refactoring/RefactoringKinds.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ CURSOR_REFACTORING(AddAsyncWrapper, "Add Async Wrapper", add.async-wrapper)
6666

6767
CURSOR_REFACTORING(ExpandMacro, "Expand Macro", expand.macro)
6868

69+
CURSOR_REFACTORING(InlineMacro, "Inline Macro", inline.macro)
70+
6971
RANGE_REFACTORING(ExtractExpr, "Extract Expression", extract.expr)
7072

7173
RANGE_REFACTORING(ExtractFunction, "Extract Method", extract.function)

lib/ASTGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_pure_swift_host_library(swiftASTGen STATIC
2525
DEPENDENCIES
2626
swiftAST
2727
SWIFT_DEPENDENCIES
28+
SwiftSyntax::SwiftBasicFormat
2829
SwiftSyntax::SwiftDiagnostics
2930
SwiftSyntax::SwiftOperators
3031
SwiftSyntax::SwiftParser

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,11 @@ func expandFreestandingMacroInProcess(
566566
}
567567

568568
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
569+
let macro = macroPtr.pointee.macro
569570

570571
let evaluatedSyntax: Syntax
571572
do {
572-
switch macroPtr.pointee.macro {
573+
switch macro {
573574
// Handle expression macro.
574575
case let exprMacro as ExpressionMacro.Type:
575576
func expandExpressionMacro<Node: FreestandingMacroExpansionSyntax>(
@@ -630,7 +631,7 @@ func expandFreestandingMacroInProcess(
630631
return nil
631632
}
632633

633-
return evaluatedSyntax.trimmedDescription
634+
return evaluatedSyntax.formattedExpansion(macro.formatMode)
634635
}
635636

636637
/// Retrieve a syntax node in the given source file, with the given type.
@@ -936,7 +937,7 @@ func expandAttachedMacroInProcess(
936937

937938
// Form a buffer of accessor declarations to return to the caller.
938939
expandedSources = accessors.map {
939-
$0.trimmedDescription
940+
$0.formattedExpansion(macro.formatMode)
940941
}
941942

942943
case (let attachedMacro as MemberAttributeMacro.Type, .MemberAttribute):
@@ -970,7 +971,7 @@ func expandAttachedMacroInProcess(
970971

971972
// Form a buffer containing an attribute list to return to the caller.
972973
expandedSources = attributes.map {
973-
$0.trimmedDescription
974+
$0.formattedExpansion(macro.formatMode)
974975
}
975976

976977
case (let attachedMacro as MemberMacro.Type, .Member):
@@ -998,7 +999,7 @@ func expandAttachedMacroInProcess(
998999

9991000
// Form a buffer of member declarations to return to the caller.
10001001
expandedSources = members.map {
1001-
$0.trimmedDescription
1002+
$0.formattedExpansion(macro.formatMode)
10021003
}
10031004

10041005
case (let attachedMacro as PeerMacro.Type, .Peer):
@@ -1016,7 +1017,7 @@ func expandAttachedMacroInProcess(
10161017

10171018
// Form a buffer of peer declarations to return to the caller.
10181019
expandedSources = peers.map {
1019-
$0.trimmedDescription
1020+
$0.formattedExpansion(macro.formatMode)
10201021
}
10211022

10221023
case (let attachedMacro as ConformanceMacro.Type, .Conformance):
@@ -1064,43 +1065,68 @@ func expandAttachedMacroInProcess(
10641065
return expandedSources
10651066
}
10661067

1068+
fileprivate extension SyntaxProtocol {
1069+
/// Perform a format if required and then trim any leading/trailing
1070+
/// whitespace.
1071+
func formattedExpansion(_ mode: FormatMode) -> String {
1072+
let formatted: Syntax
1073+
switch mode {
1074+
case .auto:
1075+
formatted = self.formatted()
1076+
case .disabled:
1077+
formatted = Syntax(self)
1078+
}
1079+
return formatted.trimmedDescription(matching: { $0.isWhitespace })
1080+
}
1081+
}
1082+
10671083
fileprivate func collapse<Node: SyntaxProtocol>(
10681084
expansions: [String],
10691085
for role: MacroRole,
10701086
attachedTo declarationNode: Node
10711087
) -> String {
1088+
if expansions.isEmpty {
1089+
return ""
1090+
}
1091+
1092+
var expansions = expansions
10721093
var separator: String = "\n\n"
1073-
var prefix: String = ""
1074-
var suffix: String = ""
1075-
1076-
switch role {
1077-
case .Accessor:
1078-
if let varDecl = declarationNode.as(VariableDeclSyntax.self),
1079-
let binding = varDecl.bindings.first,
1080-
binding.accessor == nil {
1081-
prefix = "{\n"
1082-
suffix = "\n}"
1083-
}
1084-
case .Member:
1085-
prefix = "\n\n"
1086-
suffix = "\n"
1087-
case .MemberAttribute:
1094+
1095+
if role == .Accessor,
1096+
let varDecl = declarationNode.as(VariableDeclSyntax.self),
1097+
let binding = varDecl.bindings.first,
1098+
binding.accessor == nil {
1099+
let indentation = String(repeating: " ", count: 4)
1100+
1101+
expansions = expansions.map({ indent($0, with: indentation) })
1102+
expansions[0] = "{\n" + expansions[0]
1103+
expansions[expansions.count - 1] += "\n}"
1104+
} else if role == .MemberAttribute {
10881105
separator = " "
1089-
suffix = " "
1090-
case .Peer:
1091-
prefix = "\n\n"
1092-
suffix = "\n"
1093-
case .Conformance:
1094-
prefix = "\n\n"
1095-
suffix = "\n"
1096-
case .Expression,
1097-
.FreestandingDeclaration:
1098-
fatalError("unreachable")
10991106
}
11001107

1101-
let separated = expansions.joined(separator: separator)
1102-
if separated.isEmpty {
1103-
return separated
1108+
return expansions.joined(separator: separator)
1109+
}
1110+
1111+
fileprivate func indent(_ source: String, with indentation: String) -> String {
1112+
if source.isEmpty || indentation.isEmpty {
1113+
return source
11041114
}
1105-
return prefix + separated + suffix
1115+
1116+
var indented = ""
1117+
var remaining = source[...]
1118+
while let nextNewline = remaining.firstIndex(where: { $0.isNewline }) {
1119+
if nextNewline != remaining.startIndex {
1120+
indented += indentation
1121+
}
1122+
indented += remaining[...nextNewline]
1123+
remaining = remaining[remaining.index(after: nextNewline)...]
1124+
}
1125+
1126+
if !remaining.isEmpty {
1127+
indented += indentation
1128+
indented += remaining
1129+
}
1130+
1131+
return indented
11061132
}

lib/Refactoring/Refactoring.cpp

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8754,18 +8754,53 @@ getMacroExpansionBuffers(SourceManager &sourceMgr, ResolvedCursorInfoPtr Info) {
87548754
return {};
87558755
}
87568756

8757-
bool RefactoringActionExpandMacro::isApplicable(ResolvedCursorInfoPtr Info,
8758-
DiagnosticEngine &Diag) {
8759-
return !getMacroExpansionBuffers(Diag.SourceMgr, Info).empty();
8760-
}
8761-
8762-
bool RefactoringActionExpandMacro::performChange() {
8763-
auto bufferIDs = getMacroExpansionBuffers(SM, CursorInfo);
8757+
/// Given the expanded code for a particular macro, perform whitespace
8758+
/// adjustments to make the refactoring more suitable for inline insertion.
8759+
static StringRef adjustMacroExpansionWhitespace(
8760+
GeneratedSourceInfo::Kind kind, StringRef expandedCode,
8761+
llvm::SmallString<64> &scratch) {
8762+
scratch.clear();
8763+
8764+
switch (kind) {
8765+
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
8766+
// Attributes are added to the beginning, add a space to separate from
8767+
// any existing.
8768+
scratch += expandedCode;
8769+
scratch += " ";
8770+
return scratch;
8771+
8772+
case GeneratedSourceInfo::MemberMacroExpansion:
8773+
case GeneratedSourceInfo::PeerMacroExpansion:
8774+
case GeneratedSourceInfo::ConformanceMacroExpansion:
8775+
// All added to the end. Note that conformances are always expanded as
8776+
// extensions, hence treating them the same as peer.
8777+
scratch += "\n\n";
8778+
scratch += expandedCode;
8779+
scratch += "\n";
8780+
return scratch;
8781+
8782+
case GeneratedSourceInfo::ExpressionMacroExpansion:
8783+
case GeneratedSourceInfo::FreestandingDeclMacroExpansion:
8784+
case GeneratedSourceInfo::AccessorMacroExpansion:
8785+
case GeneratedSourceInfo::ReplacedFunctionBody:
8786+
case GeneratedSourceInfo::PrettyPrinted:
8787+
return expandedCode;
8788+
}
8789+
}
8790+
8791+
static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo,
8792+
SourceEditConsumer &editConsumer, bool adjustExpansion) {
8793+
auto bufferIDs = getMacroExpansionBuffers(SM, cursorInfo);
87648794
if (bufferIDs.empty())
87658795
return true;
87668796

8797+
SourceFile *containingSF = cursorInfo->getSourceFile();
8798+
if (!containingSF)
8799+
return true;
8800+
87678801
// Send all of the rewritten buffer snippets.
87688802
CustomAttr *attachedMacroAttr = nullptr;
8803+
SmallString<64> scratchBuffer;
87698804
for (auto bufferID: bufferIDs) {
87708805
auto generatedInfo = SM.getGeneratedSourceInfo(bufferID);
87718806
if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid())
@@ -8779,8 +8814,12 @@ bool RefactoringActionExpandMacro::performChange() {
87798814
rewrittenBuffer.empty())
87808815
continue;
87818816

8782-
// `TheFile` is the file of the actual expansion site, where as
8783-
// `OriginalFile` is the possibly enclosing buffer. Concretely:
8817+
if (adjustExpansion) {
8818+
rewrittenBuffer = adjustMacroExpansionWhitespace(generatedInfo->kind, rewrittenBuffer, scratchBuffer);
8819+
}
8820+
8821+
// `containingFile` is the file of the actual expansion site, where as
8822+
// `originalFile` is the possibly enclosing buffer. Concretely:
87848823
// ```
87858824
// // m.swift
87868825
// @AddMemberAttributes
@@ -8799,14 +8838,14 @@ bool RefactoringActionExpandMacro::performChange() {
87998838
// expansion.
88008839
auto originalSourceRange = generatedInfo->originalSourceRange;
88018840
SourceFile *originalFile =
8802-
MD->getSourceFileContainingLocation(originalSourceRange.getStart());
8841+
containingSF->getParentModule()->getSourceFileContainingLocation(originalSourceRange.getStart());
88038842
StringRef originalPath;
88048843
if (originalFile->getBufferID().hasValue() &&
8805-
TheFile->getBufferID() != originalFile->getBufferID()) {
8844+
containingSF->getBufferID() != originalFile->getBufferID()) {
88068845
originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID());
88078846
}
88088847

8809-
EditConsumer.accept(SM, {originalPath,
8848+
editConsumer.accept(SM, {originalPath,
88108849
originalSourceRange,
88118850
SM.getIdentifierForBuffer(bufferID),
88128851
rewrittenBuffer,
@@ -8821,12 +8860,31 @@ bool RefactoringActionExpandMacro::performChange() {
88218860
if (attachedMacroAttr) {
88228861
SourceRange range = attachedMacroAttr->getRangeWithAt();
88238862
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
8824-
EditConsumer.accept(SM, charRange, StringRef());
8863+
editConsumer.accept(SM, charRange, StringRef());
88258864
}
88268865

88278866
return false;
88288867
}
88298868

8869+
bool RefactoringActionExpandMacro::isApplicable(ResolvedCursorInfoPtr Info,
8870+
DiagnosticEngine &Diag) {
8871+
// Never list in available refactorings. Only allow requesting directly.
8872+
return false;
8873+
}
8874+
8875+
bool RefactoringActionExpandMacro::performChange() {
8876+
return expandMacro(SM, CursorInfo, EditConsumer, /*adjustExpansion=*/false);
8877+
}
8878+
8879+
bool RefactoringActionInlineMacro::isApplicable(ResolvedCursorInfoPtr Info,
8880+
DiagnosticEngine &Diag) {
8881+
return !getMacroExpansionBuffers(Diag.SourceMgr, Info).empty();
8882+
}
8883+
8884+
bool RefactoringActionInlineMacro::performChange() {
8885+
return expandMacro(SM, CursorInfo, EditConsumer, /*adjustExpansion=*/true);
8886+
}
8887+
88308888
} // end of anonymous namespace
88318889

88328890
StringRef swift::ide::
@@ -8938,8 +8996,8 @@ swift::ide::collectRefactorings(ResolvedCursorInfoPtr CursorInfo,
89388996

89398997
// Only macro expansion is available within generated buffers
89408998
if (CursorInfo->getSourceFile()->Kind == SourceFileKind::MacroExpansion) {
8941-
if (RefactoringActionExpandMacro::isApplicable(CursorInfo, DiagEngine)) {
8942-
Infos.emplace_back(RefactoringKind::ExpandMacro,
8999+
if (RefactoringActionInlineMacro::isApplicable(CursorInfo, DiagEngine)) {
9000+
Infos.emplace_back(RefactoringKind::InlineMacro,
89439001
RefactorAvailableKind::Available);
89449002
}
89459003
return Infos;
@@ -9017,6 +9075,7 @@ refactorSwiftModule(ModuleDecl *M, RefactoringOptions Opts,
90179075
case RefactoringKind::KIND: { \
90189076
RefactoringAction##KIND Action(M, Opts, EditConsumer, DiagConsumer); \
90199077
if (RefactoringKind::KIND == RefactoringKind::LocalRename || \
9078+
RefactoringKind::KIND == RefactoringKind::ExpandMacro || \
90209079
Action.isApplicable()) \
90219080
return Action.performChange(); \
90229081
return true; \

0 commit comments

Comments
 (0)