Better handle surrounding directives when inlining a local variable.#21472
Better handle surrounding directives when inlining a local variable.#21472sharwell merged 7 commits intodotnet:dev15.7.xfrom
Conversation
2b4ec39 to
2e8b0b6
Compare
431fa4c to
52c908f
Compare
|
Tagging @dotnet/roslyn-ide @dotnet/roslyn-compiler @mattwar As part of this change, i've tweaked out SyntaxNode.RemoveNode works when removing a node from a separated list. Specifically, i've tweaked the heuristic that is used to decide which comma to remove from teh seprated list when removing the node. Previously, we would always remove the preceding comma if there was one. If there wasn't one, we'd remove hte following comma. This was suboptimal for some coding patterns. For example, if the user has: SomeMethod(a, // comment about a
b, // some comment about b
c // come comment about c
);Then previously removing 'b' would leave you with: SomeMethod(a, // some comment about b
c // come comment about c
);Now it gives you the expected SomeMethod(a, // comment about a
c // come comment about c
); |
52c908f to
5a6071b
Compare
5a6071b to
7188a08
Compare
|
@CyrusNajmabadi I don't see any difference between your before/after examples in the previous comment. |
| { | ||
| int y, | ||
| #if true | ||
|
|
There was a problem hiding this comment.
💡 This blank line should be removed
There was a problem hiding this comment.
I can live with this behavior. Critical part is not leaving dangling preprocessor directives.
I've highlighted the relevant part with asterisk here: SomeMethod(a, // comment about **b**
c // come comment about c
);Now it gives you the expected SomeMethod(a, // comment about **a**
c // come comment about c
); |
|
@CyrusNajmabadi Can you verify that the comment preservation works with leading comments too: SomeMethod(/*arg1:*/ a,
/*arg2:*/ b,
/*arg3:*/ c);I'd rather leave the extraneous comment on a blank line than have cases where a comment is incorrectly removed. In other words, the following outcome in your original example would be either acceptable or preferred: SomeMethod(a, // comment about a
// some comment about b
c // come comment about c
); |
|
Also test the following: SomeMethod(// comment about a
a,
// some comment about b
b,
// some comment about c
c); |
| return list; | ||
| } | ||
|
|
||
| private bool ContainsEndOfLine(SyntaxTriviaList triviaList) |
There was a problem hiding this comment.
💡 Several other kinds of trivia have a newline built-in. StyleCop Analyzers uses a HasBuiltinEndLine helper method for this function.
There was a problem hiding this comment.
I considered the cases here, but felt they were not suitably interesting or important enough to handle right now.
|
Sure, will add additional tests. |
|
Added tests. |
|
@dotnet/roslyn-compiler @mattwar Please review the changes (and tests) to 'RemoveNode'. Thanks! |
| return list; | ||
| } | ||
|
|
||
| private bool ContainsEndOfLine(SyntaxTriviaList triviaList) |
There was a problem hiding this comment.
Method can be static or perhaps an instance method on SyntaxTriviaList. Or alternatively, replace with triviaList.IndexOf(SyntaxKind.EndOfLineTrivia) >= 0.
| @@ -199,6 +227,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax | |||
| Return list | |||
| End Function | |||
There was a problem hiding this comment.
Can VisitList be shared between C# and VB?
There was a problem hiding this comment.
Yup. I think i can move to a shared helper.
There was a problem hiding this comment.
Ok. I looked into this, and we definitely can't share the entire method. First, it accesses a lot of instance state. And we can't make the REmover classes shared either (because htey need to respectively subclass either CSharpSyntaxRewriter and VisualBasicSyntaxRewriter).
However, i can try to share some smaller pieces of logic.
|
@cston Another look plz? |
| // If there is no next comma, or the next comma is not on the | ||
| // same line, then just remove the preceding comma if there is | ||
| // one. If there is no next or previous comma there's nothing | ||
| // in the list that needs to be fixed up. |
There was a problem hiding this comment.
The comment and the name of this class are a little confusing because the methods are only classifying separators not removing any nodes.
|
@dotnet/roslyn-compiler @mattwar i need a second pair of eyes when you get a chance. thanks! |
|
Need another @dotnet/roslyn-compiler review. Thanks! |
|
@dotnet/roslyn-compiler @mattwar i need a second pair of eyes when you get a chance. thanks! |
|
@dotnet/roslyn-compiler @mattwar i need a second pair of eyes when you get a chance. thanks! |
|
What about: SomeMethod( /* A */ a, /* B */ b, /* C */ c);Removing b and trailing comma gives me: SomeMethod( /* A */ a, /* B */ c);Because the comments are trailing the tokens. Though this pattern is probably less likely to be used than the end-of-line comments. |
|
@mattwar That case works properly (and has tests). Specifically, in your example of So, in this case, we'll do the right thing and will make |
|
@dotnet/roslyn-compiler Are you ok with just Chuck and Matt's reviews? |
|
@agocke Can you take a look? Thanks! |
|
@sharwell can you, or someone on @dotnet/roslyn-ide help get this in? Thanks! |
|
@jcouv can you get a second review for this from compiler side |
| if (alternate.Count > 0 && alternate[alternate.Count - 1].IsToken) | ||
| CommonSyntaxNodeRemover.GetSeparatorInfo( | ||
| withSeps, i, (int)SyntaxKind.EndOfLineTrivia, | ||
| out var nextTokenIsSeparator, out var nextSeparatorBelongsToNode); |
|
|
||
| using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Syntax; |
|
@jcouv Updated for your two changes |
|
@sharwell FYI The PR is all green, if you're ready for ask-mode approval (probably needs template). |
|
@Pilchie for ask mode |
|
To comment - this change missed the cutoff for 15.6 Escrow. Given the narrowness of the scenario, we're going to just take it for 15.7 instead. |
|
Approved - sorry for the delay |
Customer scenario
The Inline Temporary Variable code fix is applied to code containing preprocessor directives. The operation fails to consider the syntax requirements of the preprocessor directives, and the resulting file is no longer valid code.
Bugs this fixes
Fixes #21455
Workarounds, if any
None.
Risk
Low. Existing test behavior was not altered except where the previous code was obviously incorrect, and new tests were implemented to cover the new behavior.
Performance impact
Low. There are no key algorithm changes and the approach is consistent with other cases needing similar patterns.
Is this a regression from a previous update?
No.
Root cause analysis
Tests were configured to ignore trivia by default (including preprocessor directives), so the validation described in previous unit tests did not match the actual validation occurring. The test suite was corrected in #21439, leaving this bug as a follow-up work item.
How was the bug found?
Unit tests.
Test documentation updated?
No.