Link to PR: Introduce FixIt.Change.textualReplacement by DougGregor · Pull Request #3030 · swiftlang/swift-syntax · GitHub Summary: Fix-It changes are currently expressed in terms of replacing one syntax node with another. That's great when you're working with structured edits to a syntax tree, but sometimes one wants to be able to perform unstructured edits (if your changes don't map well to replacing syntax nodes) or having diagnostics coming from some other place that doesn't describe them in a structured manner (e.g., the compiler does this). This change adds a new case textualReplacement to FixIt.Change that lets us express a textual replacement of a particular source range in a file with another bit of text.
API Changes: Adds this new case to FixIt.Change:
/// Replace the text within the given range in a source file with new text.
///
/// Generally, one should use other cases to replace specific syntax nodes
/// or trivia, because it more easily leads to a correct result. However,
/// this case provides a fallback for textual replacement that ignores
/// syntactic structure. After applying a textual replacement, there is no
/// way to get back to a syntax tree without reparsing.
case textualReplacement(replacementRange: Range<AbsolutePosition>, sourceFile: SourceFileSyntax, newText: String)
Almost all uses of Fix-Its in the compiler itself use these C++ APIs, which are all textual replacements. Our serialized-diagnostic format also only provides textual replacements and has no notion of structured edits.
As a concrete example, I just put up a fix for strict memory safety mode that does a textual edit to turn this:
if let x { }
into
if let x = unsafe x { }
when x is not memory safe to use. It's expressed as "add = unsafe x after the x". There's no syntax tree in this part of the compiler, just raw source locations.
This would also be great for changes that add or remove a node. Currently, these changes need to replace the parent of the node to be added or removed, which leads to undesirable results when multiple fix-its target the same node and a worse editor experience.
Sounds good to me! I'm also going to generalize the "in" to an arbitrary Syntax. This doesn't have to be a SourceFileSyntax, although it often will be.
We have the same two ways of replacing/adding/removing nodes in SwiftLint. If possible one should use a SyntaxRewriter. But sometimes it’s just more straightforward to modify the code in a textual way. For that reason, in addition, we have the exact same notion of a “textual replacement” with the same arguments called “Correction”.
Given that context, the addition makes total sense to
me.