The code fix alignment problem #16999
Labels
Area-LangService-CodeFixes
Code fixes associated with diagnostics
Bug
Impact-Low
(Internal MS Team use only) Describes an issue with limited impact on existing code.
Milestone
The problem
Adding or removing text in source, as all code fixes and refactorings do, may break the indentation of a multiline, indentation-sensitive expression that begins farther to the right on the same line. This may sound like an obvious and trivial statement, but it does mean that any individual application of any code fix or refactoring, or any "fix-all" application thereof to a whole document, project, or solution, has the potential to break code.
While I originally ran into this problem when applying the "remove unnecessary parentheses" code fix to the entire VisualFSharp solution, it applies to any code fix or refactoring in any editor that uses text-based code fixes (as Visual Studio and FSAC/Ionide do; I'm not familiar with Rider).
For example, using the "remove unnecessary parentheses" code fix to remove parentheses from any arbitrary expression or pattern may break the indentation of a multiline expression that begins on the same line to the right. Such an offending trailing multiline expression could be hanging off a match-clause, a let-, use-, or do-binding, a member definition, an if-then-else expression, any arbitrary infix operator (including user-defined ones), etc., etc., etc., and it could be nested to any depth in nearly any kind of outer expression:
Recording.2024-04-07.193448.mp4
Code changes are not even safe across module boundaries:
Code that is sensitive to changes in this way is, thankfully, relatively uncommon in most F# codebases these days, and it is generally advised against, but it does nonetheless occur, including in this repo:
fsharp/vsintegration/tests/Salsa/salsa.fs
Lines 759 to 762 in 050271d
I do not however see an obviously cheap or easy way of using the existing AST traversal to determine whether any arbitrary expression is followed on the same line by an indentation-sensitive multiline expression — as far as I can tell, it would involve a significant amount of backtracking and retraversal, and it would likely require memoization to avoid massive worst-case time complexity.
Known workarounds
Potential solutions
AST-based code fixes
In theory, this problem could be avoided altogether if the tooling that offered the analysis and code fix (VS, FSAC, Rider) were also integrated with a code formatter — i.e., if the code fix were formulated as an update to the AST instead of as an update to the source text. The formatter would then simply print the updated AST following its existing rules.
Dedicated data structure
Alternatively, I wonder whether it would be possible (and, if so, worthwhile) to track indentation-sensitive multiline constructs in some kind of dedicated data structure when building the AST that would enable easy querying by range later on, e.g., something that tracked such sensitive constructs by start position and that could be queried by another construct's end position to determine whether that construct was followed on the same line by an indentation-sensitive multiline construct.
The text was updated successfully, but these errors were encountered: