Skip to content

Commit 19ef366

Browse files
authored
Merge pull request #65491 from bnbarham/cherry-format-macro-expansions
[5.9][Macros] Automatically format expanded macros
2 parents 893d94d + cb38a8a commit 19ef366

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
@@ -8756,18 +8756,53 @@ getMacroExpansionBuffers(SourceManager &sourceMgr, ResolvedCursorInfoPtr Info) {
87568756
return {};
87578757
}
87588758

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

8799+
SourceFile *containingSF = cursorInfo->getSourceFile();
8800+
if (!containingSF)
8801+
return true;
8802+
87698803
// Send all of the rewritten buffer snippets.
87708804
CustomAttr *attachedMacroAttr = nullptr;
8805+
SmallString<64> scratchBuffer;
87718806
for (auto bufferID: bufferIDs) {
87728807
auto generatedInfo = SM.getGeneratedSourceInfo(bufferID);
87738808
if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid())
@@ -8781,8 +8816,12 @@ bool RefactoringActionExpandMacro::performChange() {
87818816
rewrittenBuffer.empty())
87828817
continue;
87838818

8784-
// `TheFile` is the file of the actual expansion site, where as
8785-
// `OriginalFile` is the possibly enclosing buffer. Concretely:
8819+
if (adjustExpansion) {
8820+
rewrittenBuffer = adjustMacroExpansionWhitespace(generatedInfo->kind, rewrittenBuffer, scratchBuffer);
8821+
}
8822+
8823+
// `containingFile` is the file of the actual expansion site, where as
8824+
// `originalFile` is the possibly enclosing buffer. Concretely:
87868825
// ```
87878826
// // m.swift
87888827
// @AddMemberAttributes
@@ -8801,14 +8840,14 @@ bool RefactoringActionExpandMacro::performChange() {
88018840
// expansion.
88028841
auto originalSourceRange = generatedInfo->originalSourceRange;
88038842
SourceFile *originalFile =
8804-
MD->getSourceFileContainingLocation(originalSourceRange.getStart());
8843+
containingSF->getParentModule()->getSourceFileContainingLocation(originalSourceRange.getStart());
88058844
StringRef originalPath;
88068845
if (originalFile->getBufferID().hasValue() &&
8807-
TheFile->getBufferID() != originalFile->getBufferID()) {
8846+
containingSF->getBufferID() != originalFile->getBufferID()) {
88088847
originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID());
88098848
}
88108849

8811-
EditConsumer.accept(SM, {originalPath,
8850+
editConsumer.accept(SM, {originalPath,
88128851
originalSourceRange,
88138852
SM.getIdentifierForBuffer(bufferID),
88148853
rewrittenBuffer,
@@ -8823,12 +8862,31 @@ bool RefactoringActionExpandMacro::performChange() {
88238862
if (attachedMacroAttr) {
88248863
SourceRange range = attachedMacroAttr->getRangeWithAt();
88258864
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
8826-
EditConsumer.accept(SM, charRange, StringRef());
8865+
editConsumer.accept(SM, charRange, StringRef());
88278866
}
88288867

88298868
return false;
88308869
}
88318870

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

88348892
StringRef swift::ide::
@@ -8940,8 +8998,8 @@ swift::ide::collectRefactorings(ResolvedCursorInfoPtr CursorInfo,
89408998

89418999
// Only macro expansion is available within generated buffers
89429000
if (CursorInfo->getSourceFile()->Kind == SourceFileKind::MacroExpansion) {
8943-
if (RefactoringActionExpandMacro::isApplicable(CursorInfo, DiagEngine)) {
8944-
Infos.emplace_back(RefactoringKind::ExpandMacro,
9001+
if (RefactoringActionInlineMacro::isApplicable(CursorInfo, DiagEngine)) {
9002+
Infos.emplace_back(RefactoringKind::InlineMacro,
89459003
RefactorAvailableKind::Available);
89469004
}
89479005
return Infos;
@@ -9019,6 +9077,7 @@ refactorSwiftModule(ModuleDecl *M, RefactoringOptions Opts,
90199077
case RefactoringKind::KIND: { \
90209078
RefactoringAction##KIND Action(M, Opts, EditConsumer, DiagConsumer); \
90219079
if (RefactoringKind::KIND == RefactoringKind::LocalRename || \
9080+
RefactoringKind::KIND == RefactoringKind::ExpandMacro || \
90229081
Action.isApplicable()) \
90239082
return Action.performChange(); \
90249083
return true; \

0 commit comments

Comments
 (0)