|
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
13 | 13 | #include "swift/IDE/Utils.h" |
| 14 | +#include "swift/AST/SourceFile.h" |
14 | 15 | #include "swift/Basic/Edit.h" |
15 | 16 | #include "swift/Basic/Platform.h" |
16 | 17 | #include "swift/Basic/SourceManager.h" |
@@ -622,6 +623,102 @@ remove(SourceManager &SM, CharSourceRange Range) { |
622 | 623 | accept(SM, Range, ""); |
623 | 624 | } |
624 | 625 |
|
| 626 | +/// Given the expanded code for a particular macro, perform whitespace |
| 627 | +/// adjustments to make the refactoring more suitable for inline insertion. |
| 628 | +static StringRef |
| 629 | +adjustMacroExpansionWhitespace(GeneratedSourceInfo::Kind kind, |
| 630 | + StringRef expandedCode, |
| 631 | + llvm::SmallString<64> &scratch) { |
| 632 | + scratch.clear(); |
| 633 | + |
| 634 | + switch (kind) { |
| 635 | + case GeneratedSourceInfo::MemberAttributeMacroExpansion: |
| 636 | + // Attributes are added to the beginning, add a space to separate from |
| 637 | + // any existing. |
| 638 | + scratch += expandedCode; |
| 639 | + scratch += " "; |
| 640 | + return scratch; |
| 641 | + |
| 642 | + case GeneratedSourceInfo::MemberMacroExpansion: |
| 643 | + case GeneratedSourceInfo::PeerMacroExpansion: |
| 644 | + case GeneratedSourceInfo::ConformanceMacroExpansion: |
| 645 | + // All added to the end. Note that conformances are always expanded as |
| 646 | + // extensions, hence treating them the same as peer. |
| 647 | + scratch += "\n\n"; |
| 648 | + scratch += expandedCode; |
| 649 | + scratch += "\n"; |
| 650 | + return scratch; |
| 651 | + |
| 652 | + case GeneratedSourceInfo::ExpressionMacroExpansion: |
| 653 | + case GeneratedSourceInfo::FreestandingDeclMacroExpansion: |
| 654 | + case GeneratedSourceInfo::AccessorMacroExpansion: |
| 655 | + case GeneratedSourceInfo::ReplacedFunctionBody: |
| 656 | + case GeneratedSourceInfo::PrettyPrinted: |
| 657 | + return expandedCode; |
| 658 | + } |
| 659 | +} |
| 660 | + |
| 661 | +void swift::ide::SourceEditConsumer::acceptMacroExpansionBuffer( |
| 662 | + SourceManager &SM, unsigned bufferID, SourceFile *containingSF, |
| 663 | + bool adjustExpansion, bool includeBufferName) { |
| 664 | + auto generatedInfo = SM.getGeneratedSourceInfo(bufferID); |
| 665 | + if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()) |
| 666 | + return; |
| 667 | + |
| 668 | + auto rewrittenBuffer = SM.extractText(generatedInfo->generatedSourceRange); |
| 669 | + |
| 670 | + // If there's no change, drop the edit entirely. |
| 671 | + if (generatedInfo->originalSourceRange.getStart() == |
| 672 | + generatedInfo->originalSourceRange.getEnd() && |
| 673 | + rewrittenBuffer.empty()) |
| 674 | + return; |
| 675 | + |
| 676 | + SmallString<64> scratchBuffer; |
| 677 | + if (adjustExpansion) { |
| 678 | + rewrittenBuffer = adjustMacroExpansionWhitespace( |
| 679 | + generatedInfo->kind, rewrittenBuffer, scratchBuffer); |
| 680 | + } |
| 681 | + |
| 682 | + // `containingFile` is the file of the actual expansion site, where as |
| 683 | + // `originalFile` is the possibly enclosing buffer. Concretely: |
| 684 | + // ``` |
| 685 | + // // m.swift |
| 686 | + // @AddMemberAttributes |
| 687 | + // struct Foo { |
| 688 | + // // --- expanded from @AddMemberAttributes eg. @_someBufferName --- |
| 689 | + // @AddedAttribute |
| 690 | + // // --- |
| 691 | + // let someMember: Int |
| 692 | + // } |
| 693 | + // ``` |
| 694 | + // |
| 695 | + // When expanding `AddedAttribute`, the expansion actually applies to the |
| 696 | + // original source (`m.swift`) rather than the buffer of the expansion |
| 697 | + // site (`@_someBufferName`). Thus, we need to include the path to the |
| 698 | + // original source as well. Note that this path could itself be another |
| 699 | + // expansion. |
| 700 | + auto originalSourceRange = generatedInfo->originalSourceRange; |
| 701 | + SourceFile *originalFile = |
| 702 | + containingSF->getParentModule()->getSourceFileContainingLocation( |
| 703 | + originalSourceRange.getStart()); |
| 704 | + StringRef originalPath; |
| 705 | + if (originalFile->getBufferID().hasValue() && |
| 706 | + containingSF->getBufferID() != originalFile->getBufferID()) { |
| 707 | + originalPath = SM.getIdentifierForBuffer(*originalFile->getBufferID()); |
| 708 | + } |
| 709 | + |
| 710 | + StringRef bufferName; |
| 711 | + if (includeBufferName) { |
| 712 | + bufferName = SM.getIdentifierForBuffer(bufferID); |
| 713 | + } |
| 714 | + |
| 715 | + accept(SM, {originalPath, |
| 716 | + originalSourceRange, |
| 717 | + bufferName, |
| 718 | + rewrittenBuffer, |
| 719 | + {}}); |
| 720 | +} |
| 721 | + |
625 | 722 | struct swift::ide::SourceEditJsonConsumer::Implementation { |
626 | 723 | llvm::raw_ostream &OS; |
627 | 724 | std::vector<SingleEdit> AllEdits; |
|
0 commit comments