[Macros] Automatically format expanded macros

Rather than requiring macro implementations to add required whitespace
and indentation, basic format all macro expansions. Right now this uses
the default four space indentation, we can consider having that inferred
later. Macros can opt-out of automatic formatting by implementing
`formatMode` and setting it to `.disabled`.

Also moves the extra newlines before/after expansions to a new "Inline
Macro" refactoring.

Resolves rdar://107731047.
This commit is contained in:
Ben Barham
2023-04-26 15:06:18 -07:00
parent db14ae840e
commit fe2104ca99
9 changed files with 304 additions and 98 deletions

View File

@@ -8756,18 +8756,53 @@ getMacroExpansionBuffers(SourceManager &sourceMgr, ResolvedCursorInfoPtr Info) {
return {};
}
bool RefactoringActionExpandMacro::isApplicable(ResolvedCursorInfoPtr Info,
DiagnosticEngine &Diag) {
return !getMacroExpansionBuffers(Diag.SourceMgr, Info).empty();
/// Given the expanded code for a particular macro, perform whitespace
/// adjustments to make the refactoring more suitable for inline insertion.
static StringRef adjustMacroExpansionWhitespace(
GeneratedSourceInfo::Kind kind, StringRef expandedCode,
llvm::SmallString<64> &scratch) {
scratch.clear();
switch (kind) {
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
// Attributes are added to the beginning, add a space to separate from
// any existing.
scratch += expandedCode;
scratch += " ";
return scratch;
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion:
// All added to the end. Note that conformances are always expanded as
// extensions, hence treating them the same as peer.
scratch += "\n\n";
scratch += expandedCode;
scratch += "\n";
return scratch;
case GeneratedSourceInfo::ExpressionMacroExpansion:
case GeneratedSourceInfo::FreestandingDeclMacroExpansion:
case GeneratedSourceInfo::AccessorMacroExpansion:
case GeneratedSourceInfo::ReplacedFunctionBody:
case GeneratedSourceInfo::PrettyPrinted:
return expandedCode;
}
}
bool RefactoringActionExpandMacro::performChange() {
auto bufferIDs = getMacroExpansionBuffers(SM, CursorInfo);
static bool expandMacro(SourceManager &SM, ResolvedCursorInfoPtr cursorInfo,
SourceEditConsumer &editConsumer, bool adjustExpansion) {
auto bufferIDs = getMacroExpansionBuffers(SM, cursorInfo);
if (bufferIDs.empty())
return true;
SourceFile *containingSF = cursorInfo->getSourceFile();
if (!containingSF)
return true;
// Send all of the rewritten buffer snippets.
CustomAttr *attachedMacroAttr = nullptr;
SmallString<64> scratchBuffer;
for (auto bufferID: bufferIDs) {
auto generatedInfo = SM.getGeneratedSourceInfo(bufferID);
if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid())
@@ -8781,8 +8816,12 @@ bool RefactoringActionExpandMacro::performChange() {
rewrittenBuffer.empty())
continue;
// `TheFile` is the file of the actual expansion site, where as
// `OriginalFile` is the possibly enclosing buffer. Concretely:
if (adjustExpansion) {
rewrittenBuffer = adjustMacroExpansionWhitespace(generatedInfo->kind, rewrittenBuffer, scratchBuffer);
}
// `containingFile` is the file of the actual expansion site, where as
// `originalFile` is the possibly enclosing buffer. Concretely:
// ```
// // m.swift
// @AddMemberAttributes
@@ -8801,14 +8840,14 @@ bool RefactoringActionExpandMacro::performChange() {
// expansion.
auto originalSourceRange = generatedInfo->originalSourceRange;
SourceFile *originalFile =
MD->getSourceFileContainingLocation(originalSourceRange.getStart());
containingSF->getParentModule()->getSourceFileContainingLocation(originalSourceRange.getStart());
StringRef originalPath;
if (originalFile->getBufferID().hasValue() &&
TheFile->getBufferID() != originalFile->getBufferID()) {
containingSF->getBufferID() != originalFile->getBufferID()) {
originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID());
}
EditConsumer.accept(SM, {originalPath,
editConsumer.accept(SM, {originalPath,
originalSourceRange,
SM.getIdentifierForBuffer(bufferID),
rewrittenBuffer,
@@ -8823,12 +8862,31 @@ bool RefactoringActionExpandMacro::performChange() {
if (attachedMacroAttr) {
SourceRange range = attachedMacroAttr->getRangeWithAt();
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range);
EditConsumer.accept(SM, charRange, StringRef());
editConsumer.accept(SM, charRange, StringRef());
}
return false;
}
bool RefactoringActionExpandMacro::isApplicable(ResolvedCursorInfoPtr Info,
DiagnosticEngine &Diag) {
// Never list in available refactorings. Only allow requesting directly.
return false;
}
bool RefactoringActionExpandMacro::performChange() {
return expandMacro(SM, CursorInfo, EditConsumer, /*adjustExpansion=*/false);
}
bool RefactoringActionInlineMacro::isApplicable(ResolvedCursorInfoPtr Info,
DiagnosticEngine &Diag) {
return !getMacroExpansionBuffers(Diag.SourceMgr, Info).empty();
}
bool RefactoringActionInlineMacro::performChange() {
return expandMacro(SM, CursorInfo, EditConsumer, /*adjustExpansion=*/true);
}
} // end of anonymous namespace
StringRef swift::ide::
@@ -8940,8 +8998,8 @@ swift::ide::collectRefactorings(ResolvedCursorInfoPtr CursorInfo,
// Only macro expansion is available within generated buffers
if (CursorInfo->getSourceFile()->Kind == SourceFileKind::MacroExpansion) {
if (RefactoringActionExpandMacro::isApplicable(CursorInfo, DiagEngine)) {
Infos.emplace_back(RefactoringKind::ExpandMacro,
if (RefactoringActionInlineMacro::isApplicable(CursorInfo, DiagEngine)) {
Infos.emplace_back(RefactoringKind::InlineMacro,
RefactorAvailableKind::Available);
}
return Infos;
@@ -9019,6 +9077,7 @@ refactorSwiftModule(ModuleDecl *M, RefactoringOptions Opts,
case RefactoringKind::KIND: { \
RefactoringAction##KIND Action(M, Opts, EditConsumer, DiagConsumer); \
if (RefactoringKind::KIND == RefactoringKind::LocalRename || \
RefactoringKind::KIND == RefactoringKind::ExpandMacro || \
Action.isApplicable()) \
return Action.performChange(); \
return true; \