-
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
Add a refactoring to convert a common for-loop form to a foreach-statement. #25469
Add a refactoring to convert a common for-loop form to a foreach-statement. #25469
Conversation
// x += constant_1 | ||
|
||
ExpressionSyntax operand; | ||
if (incrementor.Kind() == SyntaxKind.PostIncrementExpression) |
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.
switch? #Resolved
if (TypeStyleHelper.IsImplicitStylePreferred( | ||
optionSet, isBuiltInTypeContext, isTypeApparentContext: false)) | ||
{ | ||
typeNode = SyntaxFactory.IdentifierName("var"); |
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.
duplication #Resolved
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.
sorry, don't know what this means. #Resolved
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.
could you use ?:
? #Resolved
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.
typeNode =
is duplicated in each branch #Resolved
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.
it feels like it would be very large and unweildy. I found this to read better. #Resolved
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.
we don't have a general rule of avoiding hte pattern of:
SomeVar v;
// initialize through branchign code
// use v
Devs decide on a case by case basis. #Resolved
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.
#wontfix
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 is nice. I should reuse this.
|
||
private static bool IsImplicitStylePreferred(TypeStylePreference stylePreferences, |
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 thought we weren't supposed to format this way?
|
||
// position has to be inside the 'for' span, or if there is a selection, it must | ||
// match the 'for' span exactly. | ||
if (context.Span.IsEmpty && !token.Span.IntersectsWith(context.Span.Start)) |
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 would personally also expect this offered after ')'
} | ||
|
||
var forStatement = token.Parent as TForStatementSyntax; | ||
if (forStatement == null) |
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.
pattern matching?
@Neme12 This has not been submitted for review. it is a WIP (work in progress). I will let you know when it's ready :) |
@heejaechang I've extracted out the helper to generate a unique name token in a specific context. you can port that over if you want. |
VB is done. Tests coming now. @dotnet/roslyn-ide @heejaechang @Neme12 @jinujoe this is ready for review now. |
return; | ||
} | ||
|
||
var forStatement = token.Parent.AncestorsAndSelf().OfType<TForStatementSyntax>().FirstOrDefault(); |
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 is a little weird. isn't there something like FirstAncestorOrSelf<> or GetAncestorOrSelf<> you could use? #Resolved
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.
yup. #resolved #Resolved
@@ -290,5 +293,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic | |||
|
|||
Return {semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken)} | |||
End Function | |||
|
|||
Private Function ISemanticFactsService_GenerateUniqueName(semanticModel As SemanticModel, location As SyntaxNode, containerOpt As SyntaxNode, baseName As String, cancellationToken As CancellationToken) As SyntaxToken Implements ISemanticFactsService.GenerateUniqueName |
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.
line way too long
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.
in this type, considering so many methods are simply to just represent the interface signature, we don't wrap and just forward things along. I agree it's not consistent, but it helps keep this type from literally becoming 5x as long and having the signatures mask what's actually relevant.
{ | ||
protected abstract ISyntaxFactsService SyntaxFactsService { get; } | ||
|
||
public SyntaxToken GenerateUniqueName( |
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.
would a better place for this be inside NameGenerator?
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.
NameGenerator is designed to be Roslyn agnostic. i.e. it's literally jsut string manipulation. we've deliberately kept it that way so that partners can use it/copy it trivially.
protected override bool TryGetForStatementComponents( | ||
ForStatementSyntax forStatement, | ||
out SyntaxToken iterationVariable, out ExpressionSyntax initializer, | ||
out MemberAccessExpressionSyntax memberAccess, out ExpressionSyntax stepValue, |
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.
it's a little confusing to use 'stepValue' for both the value and the expression it's calculated from, consider adding 'expression' to the name or differentiating it in some other way #Resolved
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.
Sure. #Resolved
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.
#resolved #Resolved
identifierName.Identifier.ValueText == iterationVariable.ValueText && | ||
binaryExpression.Right is MemberAccessExpressionSyntax) | ||
{ | ||
memberAccess = (MemberAccessExpressionSyntax)binaryExpression.Right; |
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 see waht's going on here and it's not very nice, but i can't think of any way how to improve this #WontFix
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.
#won'tfix #Resolved
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.
can these move to abstract one and use IOperation rather than doing it on language specific way?
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 don't think so. Vb and C# literally uses different types here (IForToLoop vs IForLoop). So you'd have to write custom code anyways. Given that, i don't mind explicitly writing this at the language level since each language is different.
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.
Also, i tried out hte IOperation approach and i had to jump through lots of hoops due to conversion operations in the code. It was actually more annoying.
memberAccess = (MemberAccessExpressionSyntax)binaryExpression.Right; | ||
|
||
var incrementor = forStatement.Incrementors[0]; | ||
if (TryGetStepValue(iterationVariable, incrementor, out stepValue, cancellationToken)) |
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.
can you do return TryStepGetValue?
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.
sure.
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.
#resolved
ExpressionSyntax operand; | ||
switch (incrementor.Kind()) | ||
{ | ||
case SyntaxKind.PostIncrementExpression: |
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.
it makes me sad that there's no UnaryExpressionSyntax that would let us merge these cases #WontFix
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.
#won'tfix #Resolved
|
||
return operand is IdentifierNameSyntax identifierName && | ||
identifierName.Identifier.ValueText == iterationVariable.ValueText; | ||
} |
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.
few ideas:
- could you use pattern matching instead of the casts? for assignment you'd have to use
when .IsKind()
, do you think that to be an improvement or not? - i don't like this imperative style with variables outside assigned from each case with a break everywhere, it seems like this would deserve maybe a local function or a method that could have a single return from each case with a tuple of (operand, stepValue)... in default it would return a null operand and the
operand is IdentifierNameSyntax
check would still work as expected... but this is all up to you, I'm just throwing some suggestions/ideas
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 ok with it as it is. If we had a match expression, i might consider using that. But i'm ok with things as they are here.
tests added. @dotnet/roslyn-ide please review at your earliest convenience. |
Note: one of the best things i could get from reviewers are potentially interesting cases i missed in both languages. VB can be especially surprising with all the forms it supports :) |
@heejaechang Can you review? (in exchange for the reviews i did :)) |
@jcouv Woudl appreciate your eyes here. |
@Pilchie This refactoring seems very safe for 15.7. Woudl you want it there, or would 15.8 make sense? |
I believe we want it in 15.7 as long as it's code reviewed and buddy tested in within the next week. @jinujoseph / @kuhlenh |
That would be great! The ball's in your court :) |
{ | ||
[||]for (int i = 0; i < array.Length; i++) | ||
{ | ||
Console.WriteLine(array[i]); |
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.
array[i] [](start = 30, length = 8)
Consider adding a test where array[i]
appears more than once in the foreach block.
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.
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.
#resolved
Thanks! I'm going to add some more checks and warnings for potential mutations to the collection. I think that's very reasonable! |
@jinujoseph PR feedback addressed. this can go in once green. |
@dotnet/roslyn-infrastructure I'm getting a failure on the new spanish unit test build:
Does anyone know what this is about? |
@tmeschter @jasonmalinowski do you know anything about that msbuild error? |
@CyrusNajmabadi That queue is still experimental, safe to ignore. |
oh thanks. i missed that. |
retest windows_debug_unit32_prtest please |
@Pilchie for ask mode approval |
@jinujoseph This has passed and can be merged in. All the failures are the current issue with "error MSB6002: The command-line for the "CreatePkgDef" task is too long." |
thanks @CyrusNajmabadi , this is pending approval from @Pilchie |
Approved - thanks @CyrusNajmabadi |
@333fred test failures expected ? |
test windows_release_vs-integration_prtest |
@jinujoseph It's not a test failure. The error with integration tests (as i mentioned above) is:
I don't know what can be done about this. Thoughts @333fred @jasonmalinowski -- Jason, this is not the spanish queue. This is hte integration test queue. I'm not sure i feel comfortable having my stuff not go through integration testing, even if htat queue isn't required. |
The integration test queues are in need of the same fix as the Spanish queue. You can disregard those failures. |
Thanks @jasonmalinowski . @jinujoseph This can be merged in when convenient for you. |
Thanks all. This is going to be a great release! |
Agreed - so excited. |
Tagging @heejaechang .
Todo: