-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UseExplicitType for var in deconstruction #23975
Conversation
/*x1*/int x/*x2*/, | ||
/*yz1*/( | ||
/*y1*/int y/*y2*/, | ||
/*z1*/Program z/*z2*/)/*yz2*/) /*after*/ = new Program(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what causes this weird formatting. I'll check with @DustinCampbell for any tips before debugging in depth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @DustinCampbell for the pointer.
If I understood properly, the approach is to drop some trivia that would cause formatting problems. I'm not sure I like this approach for this PR.
I'd rather either: (1) leave the odd formatting for the edge case of comments in such positions in a deconstruction, or (2) fix the formatter (it should not introduce newlines in certain positions).
If the IDE team is ok, I'll leave the current behavior (odd formatting).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty broken if you ask me...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasonmalinowski I spent the afternoon deep in trivia formatter and could not figure it out.
As far as I can tell the root cause of the newlines getting added is AbstractTriviaFormatter.GetLineColumnOfWhitespace
which returns a value with lines=1. This causes AbstractTriviaFormatter.AddWhitespaceTextChange
to add a newline.
I filed a follow-up issue: #24060
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind. I finally understood how Dustin's workaround works and applied a similar trick ;-)
The key is that parens and commas introduced by the codefix come with elastic whitespace trivia which causes the formatter to misbehave. Removing those avoids the problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasonmalinowski Any other review feedback?
I need to revise this. From discussion with @gafter, he'd rather the semantic model not expose a type for the Update: done. Most of the work is in the IDE layer now, with just one small fix in the compiler. |
@dotnet/roslyn-ide for review. Thanks |
@dotnet/roslyn-ide for review. Thanks |
@Pilchie for ask-mode approval. Thanks |
@jcouv I don't see appropriate compiler reviews for the compiler portions. |
Good call. I forgot there were there. |
for (int i = 1; i < builder.Count; i++) | ||
{ | ||
separatorBuilder.Add(SyntaxFactory.Token(SyntaxKind.CommaToken).WithoutTrivia()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps: var separatorBuilder = ArrayBuilder<SyntaxToken>.GetInstance(builder.Count - 1, SyntaxFactory.Token(...));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Thanks!
@dotnet/roslyn-compiler for a second review. Thanks |
@@ -277,7 +277,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, | |||
hasErrors = true; | |||
} | |||
|
|||
boundIterationVariableType = new BoundTypeExpression(variables, aliasOpt: null, type: iterationVariableType); | |||
boundIterationVariableType = new BoundTypeExpression(variables, aliasOpt: null, type: iterationVariableType).MakeCompilerGenerated(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boundIterationVariableType [](start = 24, length = 26)
As we discussed, bound types are supposed to reflect types in source; you're filing a follow-up issue to separate the representation of the type in source from the type of the iterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #24105 from our discussion. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compiler code looks correct to me, modulo a new issue you filed. IDE code looks plausible, but I assume you're getting review from the IDE team.
@Pilchie Thanks for reminding me on compiler review. That's now done. Please approve for ask-mode. Thanks |
Approved - thanks @jcouv |
Retargetd to dev15.6.x branch. |
test ubuntu_14_debug_prtest please |
@@ -1227,6 +1227,7 @@ public void Deconstruct(out int a, out int b) | |||
Assert.Equal("(System.Int32 a, System.Int32 b)", tuple2.ToTestDisplayString()); | |||
var underlying2 = tuple2.TupleUnderlyingType; | |||
Assert.Equal("System.ValueTuple<System.Int32, System.Int32>[missing]", underlying2.ToTestDisplayString()); | |||
Assert.Equal("(System.Int32 a, System.Int32 b)", model.GetTypeInfo(ab).ConvertedType.ToTestDisplayString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert.Equal("(System.Int32 a, System.Int32 b)", model.GetTypeInfo(ab).ConvertedType.ToTestDisplayString()); [](start = 12, length = 108)
Do we have similar test for a regular deconstruction declaration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs I can't find one. I'll start a PR to add such test. Thanks
Customer scenario
UseExplicitType should trigger on
var
in a deconstruction. For instance:var (x, y) = new C();
should trigger. A proper fix should be offered (something like(int x, string y) = new C();
).Bugs this fixes
Fixes #23752
Workarounds, if any
You can fix the code manually when the code fix doesn't trigger.
Risk
There are two parts of this change, both which are low risk:
GetTypeInfo
returned by the semantic model onvar (x, y)
in a deconstruction foreach should have properConvertedType
.UseExplicitType
code fixer needs to generate proper replacement code.Performance impact
Low. The analyzer only performs one additional and cheap check to see if "var" is a declaration expression with parenthesized designation.
Is this a regression from a previous update?
No
Root cause analysis
For the code fix, I didn't realize we should expect that code fix to trigger in this scenario.
How was the bug found?
Reported internally.
Question for IDE team: should short discards should also trigger this code fix (since the type isn't apparent)?