Skip to content

Commit acd3108

Browse files
Add AsEnumerable in linq "in" clause - fixes #544
1 parent 179f71a commit acd3108

File tree

7 files changed

+52
-7
lines changed

7 files changed

+52
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1010
### VB -> C#
1111
* Optimize away some redundant casts and conversions with strings/chars - [#388](https://github.com/icsharpcode/CodeConverter/issues/388)
1212
* Improve performance of single file conversion
13+
* Add AsEnumerable where needed in linq "in" clause - [#544](https://github.com/icsharpcode/CodeConverter/issues/544)
1314

1415
### C# -> VB
1516

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
5252
_lambdaConverter = new LambdaConverter(commonConversions, semanticModel);
5353
_visualBasicEqualityComparison = visualBasicEqualityComparison;
5454
TriviaConvertingExpressionVisitor = new CommentConvertingVisitorWrapper(this, _semanticModel.SyntaxTree);
55-
_queryConverter = new QueryConverter(commonConversions, TriviaConvertingExpressionVisitor);
55+
_queryConverter = new QueryConverter(commonConversions, _semanticModel, TriviaConvertingExpressionVisitor);
5656
_csCompilation = csCompilation;
5757
_typeContext = typeContext;
5858
_extraUsingDirectives = extraUsingDirectives;

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,8 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForEachBlock(VBSynt
598598
id = declaration.Variables.Single().Identifier;
599599
} else if (_semanticModel.GetSymbolInfo(stmt.ControlVariable).Symbol is ISymbol varSymbol) {
600600
var variableType = varSymbol.GetSymbolType();
601-
var useVar = variableType?.SpecialType == SpecialType.System_Object || _semanticModel.GetTypeInfo(stmt.Expression).ConvertedType.IsEnumerableOfExactType(variableType);
602-
type = CommonConversions.GetTypeSyntax(varSymbol.GetSymbolType(), useVar);
601+
var explicitCastWouldHaveNoEffect = variableType?.SpecialType == SpecialType.System_Object || _semanticModel.GetTypeInfo(stmt.Expression).ConvertedType.IsEnumerableOfExactType(variableType);
602+
type = CommonConversions.GetTypeSyntax(varSymbol.GetSymbolType(), explicitCastWouldHaveNoEffect);
603603
var v = (IdentifierNameSyntax)await stmt.ControlVariable.AcceptAsync(_expressionVisitor);
604604
if (_localsToInlineInLoop.Contains(varSymbol)) {
605605
id = v.Identifier;

CodeConverter/CSharp/QueryConverter.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using ICSharpCode.CodeConverter.Util;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.Operations;
910
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
1011
using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax;
1112
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
@@ -18,11 +19,13 @@ namespace ICSharpCode.CodeConverter.CSharp
1819
internal class QueryConverter
1920
{
2021
private readonly CommentConvertingVisitorWrapper _triviaConvertingVisitor;
22+
private SemanticModel _semanticModel;
2123

22-
public QueryConverter(CommonConversions commonConversions, CommentConvertingVisitorWrapper triviaConvertingVisitor)
24+
public QueryConverter(CommonConversions commonConversions, SemanticModel semanticModel, CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor)
2325
{
2426
CommonConversions = commonConversions;
25-
_triviaConvertingVisitor = triviaConvertingVisitor;
27+
_semanticModel = semanticModel;
28+
_triviaConvertingVisitor = triviaConvertingExpressionVisitor;
2629
}
2730

2831
private CommonConversions CommonConversions { get; }
@@ -239,9 +242,15 @@ private static bool RequiredContinuation(VBSyntax.QueryClauseSyntax queryClauseS
239242
private async Task<CSSyntax.FromClauseSyntax> ConvertFromClauseSyntaxAsync(VBSyntax.FromClauseSyntax vbFromClause)
240243
{
241244
var collectionRangeVariableSyntax = vbFromClause.Variables.Single();
245+
var expression = (CSSyntax.ExpressionSyntax)await collectionRangeVariableSyntax.Expression.AcceptAsync(_triviaConvertingVisitor);
246+
var parentOperation = _semanticModel.GetOperation(collectionRangeVariableSyntax.Expression)?.Parent;
247+
if (parentOperation.IsImplicit && parentOperation is IInvocationOperation io &&
248+
io.TargetMethod.MethodKind == MethodKind.ReducedExtension && io.TargetMethod.Name == nameof(Enumerable.AsEnumerable)) {
249+
expression = SyntaxFactory.InvocationExpression(ValidSyntaxFactory.MemberAccess(expression, io.TargetMethod.Name), SyntaxFactory.ArgumentList());
250+
}
242251
var fromClauseSyntax = SyntaxFactory.FromClause(
243252
CommonConversions.ConvertIdentifier(collectionRangeVariableSyntax.Identifier.Identifier),
244-
(CSSyntax.ExpressionSyntax) await collectionRangeVariableSyntax.Expression.AcceptAsync(_triviaConvertingVisitor));
253+
expression);
245254
return fromClauseSyntax;
246255
}
247256

CodeConverter/Shared/DefaultReferences.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class DefaultReferences
2424
typeof(System.ComponentModel.BrowsableAttribute),
2525
typeof(System.Dynamic.DynamicObject),
2626
typeof(System.Data.DataRow),
27+
typeof(System.Data.DataTableExtensions),
2728
typeof(System.Net.Http.HttpClient),
2829
typeof(System.Web.HttpUtility),
2930
typeof(System.Xml.XmlElement),

Tests/CSharp/ExpressionTests/LinqExpressionTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,35 @@ Return String.Empty
188188
}");
189189
}
190190

191+
[Fact]
192+
public async Task LinqAsEnumerable()
193+
{
194+
await TestConversionVisualBasicToCSharpAsync(@"Imports System.Data
195+
196+
Public Class AsEnumerableTest
197+
Public Sub FillImgColor()
198+
Dim dtsMain As New DataSet
199+
For Each i_ColCode As Integer In
200+
From CurRow In dtsMain.Tables(""tb_Color"") Select CInt(CurRow.Item(""i_ColCode""))
201+
Next
202+
End Sub
203+
End Class", @"using System.Data;
204+
using System.Linq;
205+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
206+
207+
public partial class AsEnumerableTest
208+
{
209+
public void FillImgColor()
210+
{
211+
var dtsMain = new DataSet();
212+
foreach (int i_ColCode in from CurRow in dtsMain.Tables[""tb_Color""].AsEnumerable()
213+
select Conversions.ToInteger(CurRow[""i_ColCode""]))
214+
{
215+
}
216+
}
217+
}");
218+
}
219+
191220
[Fact]
192221
public async Task LinqMultipleFromsAsync()
193222
{

Vsix/Vsix.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6060
<PrivateAssets>all</PrivateAssets>
6161
</PackageReference>
62+
<PackageReference Include="System.Data.Common">
63+
<Version>4.3.0</Version>
64+
</PackageReference>
65+
<PackageReference Include="System.Data.DataSetExtensions">
66+
<Version>4.5.0</Version>
67+
</PackageReference>
6268
<PackageReference Include="System.Data.SqlClient">
6369
<Version>4.8.1</Version>
6470
</PackageReference>
@@ -95,7 +101,6 @@
95101
<Reference Include="PresentationCore" />
96102
<Reference Include="PresentationFramework" />
97103
<Reference Include="System" />
98-
<Reference Include="System.Data" />
99104
<Reference Include="System.Design" />
100105
<Reference Include="System.Web" />
101106
<Reference Include="System.Windows.Forms" />

0 commit comments

Comments
 (0)