Skip to content

Commit 05c3fb1

Browse files
Always declare inline because VB does the cast pre-comparison
1 parent 742bbf8 commit 05c3fb1

File tree

2 files changed

+19
-19
lines changed

2 files changed

+19
-19
lines changed

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -502,27 +502,27 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForBlock(VBSyntax.F
502502
var stmt = node.ForStatement;
503503
VariableDeclarationSyntax declaration = null;
504504
ExpressionSyntax id;
505-
var controlVarSymbol = _semanticModel.GetSymbolInfo(stmt.ControlVariable).Symbol;
506-
var controlVarType = controlVarSymbol?.GetSymbolType();
505+
var controlVarSymbol = _semanticModel.GetSymbolInfo(stmt.ControlVariable).Symbol ?? (_semanticModel.GetOperation(stmt.ControlVariable) as IVariableDeclaratorOperation)?.Symbol;
506+
507+
// If missing semantic info, the compiler just guesses object, let's try to improve on that guess:
508+
var controlVarType = controlVarSymbol?.GetSymbolType().Yield().Concat(
509+
new SyntaxNode[] {stmt.ControlVariable, stmt.FromValue, stmt.ToValue, stmt.StepClause?.StepValue}
510+
.Select(exp => _semanticModel.GetTypeInfo(exp).Type)
511+
).FirstOrDefault(t => t != null && t.SpecialType != SpecialType.System_Object);
507512
var startValue = await stmt.FromValue.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
508513
startValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(stmt.FromValue, startValue?.SkipIntoParens(), forceTargetType: controlVarType);
509514

510515
var initializers = new List<ExpressionSyntax>();
511-
if (stmt.ControlVariable is VBSyntax.VariableDeclaratorSyntax) {
512-
var v = (VBSyntax.VariableDeclaratorSyntax)stmt.ControlVariable;
516+
var controlVarTypeSyntax = CommonConversions.GetTypeSyntax(controlVarType);
517+
if (stmt.ControlVariable is VBSyntax.VariableDeclaratorSyntax v) {
513518
declaration = (await SplitVariableDeclarationsAsync(v)).Variables.Single().Decl;
514519
declaration = declaration.WithVariables(SyntaxFactory.SingletonSeparatedList(declaration.Variables[0].WithInitializer(SyntaxFactory.EqualsValueClause(startValue))));
515520
id = SyntaxFactory.IdentifierName(declaration.Variables[0].Identifier);
516521
} else {
517522
id = await stmt.ControlVariable.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
518-
519-
// If missing semantic info, the compiler just guesses object. In this branch there was no explicit type, so let's try to improve on that guess:
520-
controlVarType = controlVarType.Yield()
521-
.Concat(new[] { stmt.FromValue, stmt.ToValue, stmt.StepClause?.StepValue }.Select(exp => _semanticModel.GetTypeInfo(exp).Type))
522-
.FirstOrDefault(t => t != null && t.SpecialType != SpecialType.System_Object);
523-
523+
524524
if (controlVarSymbol != null && controlVarSymbol.DeclaringSyntaxReferences.Any(r => r.Span.OverlapsWith(stmt.ControlVariable.Span))) {
525-
declaration = CommonConversions.CreateVariableDeclarationAndAssignment(controlVarSymbol.Name, startValue, CommonConversions.GetTypeSyntax(controlVarType));
525+
declaration = CommonConversions.CreateVariableDeclarationAndAssignment(controlVarSymbol.Name, startValue, controlVarTypeSyntax);
526526
} else {
527527
startValue = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, id, startValue);
528528
initializers.Add(startValue);
@@ -538,18 +538,19 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForBlock(VBSyntax.F
538538
// If it could evaluate differently or has side effects, it must be extracted as a variable
539539
if (!_semanticModel.GetConstantValue(stmt.ToValue).HasValue) {
540540
var loopToVariableName = GetUniqueVariableNameInScope(node, "loopTo");
541-
var toValueType = _semanticModel.GetTypeInfo(stmt.ToValue).ConvertedType;
542541
var toVariableId = SyntaxFactory.IdentifierName(loopToVariableName);
543542

544-
// If that variable has the same type as the loop variable, we can explicitly declare the type and it inline
545-
if (controlVarType?.Equals(toValueType) == true && declaration != null) {
546-
var loopToAssignment = CommonConversions.CreateVariableDeclarator(loopToVariableName, csToValue);
547-
declaration = declaration.AddVariables(loopToAssignment).WithType(CommonConversions.GetTypeSyntax(controlVarType));
548-
} else {
543+
var loopToAssignment = CommonConversions.CreateVariableDeclarator(loopToVariableName, csToValue);
544+
if (initializers.Any()) {
549545
var loopEndDeclaration = SyntaxFactory.LocalDeclarationStatement(
550546
CommonConversions.CreateVariableDeclarationAndAssignment(loopToVariableName, csToValue));
551547
// Does not do anything about porting newline trivia upwards to maintain spacing above the loop
552548
preLoopStatements.Add(loopEndDeclaration);
549+
} else {
550+
declaration = declaration == null
551+
? SyntaxFactory.VariableDeclaration(controlVarTypeSyntax,
552+
SyntaxFactory.SingletonSeparatedList(loopToAssignment))
553+
: declaration.AddVariables(loopToAssignment).WithType(controlVarTypeSyntax);
553554
}
554555

555556
csToValue = toVariableId;

Tests/CSharp/MissingSemanticModelInfo/StatementTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ internal partial class MissingLoopType
2727
public void Test()
2828
{
2929
Asadf x = default;
30-
var loopTo = x.SomeInteger;
31-
for (int i = 1; i <= loopTo; i++)
30+
for (int i = 1, loopTo = x.SomeInteger; i <= loopTo; i++)
3231
{
3332
}
3433
}

0 commit comments

Comments
 (0)