未验证 提交 4598a0c6 编写于 作者: M Manish Vasani 提交者: GitHub

Merge pull request #29402 from mavasani/ForEachToLinqMethod

Add refactoring to convert foreach loop into linq invocations (Select…
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
......@@ -22,7 +23,27 @@ public AbstractConverter(ForEachInfo<ForEachStatementSyntax, StatementSyntax> fo
{
ForEachInfo = forEachInfo;
}
public abstract void Convert(SyntaxEditor editor, CancellationToken cancellationToken);
public abstract void Convert(SyntaxEditor editor, bool convertToQuery, CancellationToken cancellationToken);
/// <summary>
/// Creates a query expression or a linq invocation expression.
/// </summary>
/// <param name="selectExpression">expression to be used into the last Select in the query expression or linq invocation.</param>
/// <param name="leadingTokensForSelect">extra leading tokens to be added to the select clause</param>
/// <param name="trailingTokensForSelect">extra trailing tokens to be added to the select clause</param>
/// <param name="convertToQuery">Flag indicating if a query expression should be generated</param>
/// <returns></returns>
protected ExpressionSyntax CreateQueryExpressionOrLinqInvocation(
ExpressionSyntax selectExpression,
IEnumerable<SyntaxToken> leadingTokensForSelect,
IEnumerable<SyntaxToken> trailingTokensForSelect,
bool convertToQuery)
{
return convertToQuery
? CreateQueryExpression(selectExpression, leadingTokensForSelect, trailingTokensForSelect)
: (ExpressionSyntax)CreateLinqInvocation(selectExpression, leadingTokensForSelect, trailingTokensForSelect);
}
/// <summary>
/// Creates a query expression.
......@@ -31,7 +52,7 @@ public AbstractConverter(ForEachInfo<ForEachStatementSyntax, StatementSyntax> fo
/// <param name="leadingTokensForSelect">extra leading tokens to be added to the select clause</param>
/// <param name="trailingTokensForSelect">extra trailing tokens to be added to the select clause</param>
/// <returns></returns>
protected QueryExpressionSyntax CreateQueryExpression(
private QueryExpressionSyntax CreateQueryExpression(
ExpressionSyntax selectExpression,
IEnumerable<SyntaxToken> leadingTokensForSelect,
IEnumerable<SyntaxToken> trailingTokensForSelect)
......@@ -73,8 +94,8 @@ private static QueryClauseSyntax CreateQueryClause(ExtendedSyntaxNode node)
}
private static FromClauseSyntax CreateFromClause(
ForEachStatementSyntax forEachStatement,
IEnumerable<SyntaxTrivia> extraLeadingTrivia,
ForEachStatementSyntax forEachStatement,
IEnumerable<SyntaxTrivia> extraLeadingTrivia,
IEnumerable<SyntaxTrivia> extraTrailingTrivia)
=> SyntaxFactory.FromClause(
fromKeyword: SyntaxFactory.Token(SyntaxKind.FromKeyword)
......@@ -93,5 +114,166 @@ private static QueryClauseSyntax CreateQueryClause(ExtendedSyntaxNode node)
expression: forEachStatement.Expression)
.WithCommentsFrom(extraLeadingTrivia, extraTrailingTrivia, forEachStatement.CloseParenToken);
/// <summary>
/// Creates a linq invocation expression.
/// </summary>
/// <param name="selectExpression">expression to be used in the last 'Select' invocation</param>
/// <param name="leadingTokensForSelect">extra leading tokens to be added to the select clause</param>
/// <param name="trailingTokensForSelect">extra trailing tokens to be added to the select clause</param>
/// <returns></returns>
private InvocationExpressionSyntax CreateLinqInvocation(
ExpressionSyntax selectExpression,
IEnumerable<SyntaxToken> leadingTokensForSelect,
IEnumerable<SyntaxToken> trailingTokensForSelect)
{
var foreachStatement = ForEachInfo.ForEachStatement;
selectExpression = selectExpression.WithCommentsFrom(leadingTokensForSelect, ForEachInfo.TrailingTokens.Concat(trailingTokensForSelect));
var currentExtendedNodeIndex = 0;
return CreateLinqInvocation(
foreachStatement,
receiverForInvocation: foreachStatement.Expression,
selectExpression: selectExpression,
leadingCommentsTrivia: ForEachInfo.LeadingTokens.GetTrivia(),
trailingCommentsTrivia: Enumerable.Empty<SyntaxTrivia>(),
currentExtendedNodeIndex: ref currentExtendedNodeIndex)
.WithAdditionalAnnotations(Formatter.Annotation);
}
private InvocationExpressionSyntax CreateLinqInvocation(
ForEachStatementSyntax forEachStatement,
ExpressionSyntax receiverForInvocation,
IEnumerable<SyntaxTrivia> leadingCommentsTrivia,
IEnumerable<SyntaxTrivia> trailingCommentsTrivia,
ExpressionSyntax selectExpression,
ref int currentExtendedNodeIndex)
{
leadingCommentsTrivia = forEachStatement.ForEachKeyword.GetAllTrivia().Concat(leadingCommentsTrivia);
// Recursively create linq invocations, possibly updating the receiver (Where invocations), to get the inner expression for
// the lambda body for the linq invocation to be created for this foreach statement. For example:
//
// INPUT:
// foreach (var n1 in c1)
// foreach (var n2 in c2)
// if (n1 > n2)
// yield return n1 + n2;
//
// OUTPUT:
// c1.SelectMany(n1 => c2.Where(n2 => n1 > n2).Select(n2 => n1 + n2))
//
var hasForEachChild = false;
var lambdaBody = CreateLinqInvocationForExtendedNode(selectExpression, ref currentExtendedNodeIndex, ref receiverForInvocation, ref hasForEachChild);
var lambda = SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
forEachStatement.Identifier.WithPrependedLeadingTrivia(
SyntaxNodeOrTokenExtensions.GetTrivia(forEachStatement.Type.GetFirstToken())
.FilterComments(addElasticMarker: false))),
lambdaBody)
.WithCommentsFrom(leadingCommentsTrivia, trailingCommentsTrivia,
forEachStatement.OpenParenToken, forEachStatement.InKeyword, forEachStatement.CloseParenToken);
// Create Select or SelectMany linq invocation for this foreach statement. For example:
//
// INPUT:
// foreach (var n1 in c1)
// ...
//
// OUTPUT:
// c1.Select(n1 => ...
// OR
// c1.SelectMany(n1 => ...
//
var invokedMethodName = !hasForEachChild ? nameof(Enumerable.Select) : nameof(Enumerable.SelectMany);
return SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
receiverForInvocation.Parenthesize(),
SyntaxFactory.IdentifierName(invokedMethodName)),
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(lambda))));
}
/// <summary>
/// Creates a linq invocation expression for the <see cref="ForEachInfo{ForEachStatementSyntax, StatementSyntax}.ConvertingExtendedNodes"/> node at the given index <paramref name="extendedNodeIndex"/>
/// or returns the <paramref name="selectExpression"/> if all extended nodes have been processed.
/// </summary>
/// <param name="selectExpression">Innermost select expression</param>
/// <param name="extendedNodeIndex">Index into <see cref="ForEachInfo{ForEachStatementSyntax, StatementSyntax}.ConvertingExtendedNodes"/> to be processed and updated.</param>
/// <param name="receiver">Receiver for the generated linq invocation. Updated when processing an if statement.</param>
/// <param name="hasForEachChild">Flag indicating if any of the processed <see cref="ForEachInfo{ForEachStatementSyntax, StatementSyntax}.ConvertingExtendedNodes"/> is a <see cref="ForEachStatementSyntax"/>.</param>
private ExpressionSyntax CreateLinqInvocationForExtendedNode(
ExpressionSyntax selectExpression,
ref int extendedNodeIndex,
ref ExpressionSyntax receiver,
ref bool hasForEachChild)
{
// Check if we have converted all the descendant foreach/if statements.
// If so, we return the select expression.
if (extendedNodeIndex == ForEachInfo.ConvertingExtendedNodes.Length)
{
return selectExpression;
}
// Otherwise, convert the next foreach/if statement into a linq invocation.
var node = ForEachInfo.ConvertingExtendedNodes[extendedNodeIndex];
switch (node.Node.Kind())
{
// Nested ForEach statement is converted into a nested Select or SelectMany linq invocation. For example:
//
// INPUT:
// foreach (var n1 in c1)
// foreach (var n2 in c2)
// ...
//
// OUTPUT:
// c1.SelectMany(n1 => c2.Select(n2 => ...
//
case SyntaxKind.ForEachStatement:
hasForEachChild = true;
var foreachStatement = (ForEachStatementSyntax)node.Node;
++extendedNodeIndex;
return CreateLinqInvocation(
foreachStatement,
receiverForInvocation: foreachStatement.Expression,
selectExpression: selectExpression,
leadingCommentsTrivia: node.ExtraLeadingComments,
trailingCommentsTrivia: node.ExtraTrailingComments,
currentExtendedNodeIndex: ref extendedNodeIndex);
// Nested If statement is converted into a Where method invocation on the current receiver. For example:
//
// INPUT:
// foreach (var n1 in c1)
// if (n1 > 0)
// ...
//
// OUTPUT:
// c1.Where(n1 => n1 > 0).Select(n1 => ...
//
case SyntaxKind.IfStatement:
var ifStatement = (IfStatementSyntax)node.Node;
var parentForEachStatement = ifStatement.GetAncestor<ForEachStatementSyntax>();
var lambdaParameter = SyntaxFactory.Parameter(SyntaxFactory.Identifier(parentForEachStatement.Identifier.ValueText));
var lambda = SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier(parentForEachStatement.Identifier.ValueText)),
ifStatement.Condition.WithCommentsFrom(ifStatement.OpenParenToken, ifStatement.CloseParenToken))
.WithCommentsFrom(ifStatement.IfKeyword.GetAllTrivia().Concat(node.ExtraLeadingComments), node.ExtraTrailingComments);
receiver = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
receiver.Parenthesize(),
SyntaxFactory.IdentifierName(nameof(Enumerable.Where))),
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(lambda))));
++extendedNodeIndex;
return CreateLinqInvocationForExtendedNode(selectExpression, ref extendedNodeIndex, ref receiver, ref hasForEachChild);
}
throw ExceptionUtilities.Unreachable;
}
}
}
......@@ -45,12 +45,12 @@ internal abstract class AbstractToMethodConverter : AbstractConverter
protected abstract bool CanReplaceInitialization(ExpressionSyntax expressionSyntax, CancellationToken cancellationToken);
protected abstract StatementSyntax CreateDefaultStatement(QueryExpressionSyntax queryExpression, ExpressionSyntax expression);
protected abstract StatementSyntax CreateDefaultStatement(ExpressionSyntax queryOrLinqInvocationExpression, ExpressionSyntax expression);
public override void Convert(SyntaxEditor editor, CancellationToken cancellationToken)
public override void Convert(SyntaxEditor editor, bool convertToQuery, CancellationToken cancellationToken)
{
var queryExpression = CreateQueryExpression(
_selectExpression, Enumerable.Empty<SyntaxToken>(), Enumerable.Empty<SyntaxToken>());
var queryOrLinqInvocationExpression = CreateQueryExpressionOrLinqInvocation(
_selectExpression, Enumerable.Empty<SyntaxToken>(), Enumerable.Empty<SyntaxToken>(), convertToQuery);
var previous = ForEachInfo.ForEachStatement.GetPreviousStatement();
......@@ -98,7 +98,7 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
// list.AddRange(query) or counter += query.Count();
editor.ReplaceNode(
ForEachInfo.ForEachStatement,
CreateDefaultStatement(queryExpression, _modifyingExpression).WithAdditionalAnnotations(Formatter.Annotation));
CreateDefaultStatement(queryOrLinqInvocationExpression, _modifyingExpression).WithAdditionalAnnotations(Formatter.Annotation));
return;
......@@ -138,7 +138,7 @@ void Convert(ExpressionSyntax replacingExpression, SyntaxNode nodeToRemoveIfFoll
editor.ReplaceNode(
replacingExpression,
CreateInvocationExpression(queryExpression)
CreateInvocationExpression(queryOrLinqInvocationExpression)
.WithCommentsFrom(leadingTrivia, _trivia).KeepCommentsAndAddElasticMarkers());
editor.RemoveNode(ForEachInfo.ForEachStatement);
}
......@@ -182,11 +182,11 @@ SyntaxTrivia[] GetTriviaFromNode(SyntaxNode node)
// query => query.Method()
// like query.Count() or query.ToList()
protected InvocationExpressionSyntax CreateInvocationExpression(QueryExpressionSyntax queryExpression)
protected InvocationExpressionSyntax CreateInvocationExpression(ExpressionSyntax queryOrLinqInvocationExpression)
=> SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.ParenthesizedExpression(queryExpression),
SyntaxFactory.ParenthesizedExpression(queryOrLinqInvocationExpression),
SyntaxFactory.IdentifierName(MethodName))).WithAdditionalAnnotations(Formatter.Annotation);
}
}
......@@ -27,7 +27,8 @@ internal sealed class CSharpConvertForEachToLinqQueryProvider
protected override ForEachInfo<ForEachStatementSyntax, StatementSyntax> CreateForEachInfo(
ForEachStatementSyntax forEachStatement,
SemanticModel semanticModel)
SemanticModel semanticModel,
bool convertLocalDeclarations)
{
var identifiersBuilder = ArrayBuilder<SyntaxToken>.GetInstance();
identifiersBuilder.Add(forEachStatement.Identifier);
......@@ -161,6 +162,11 @@ internal sealed class CSharpConvertForEachToLinqQueryProvider
// Try to prepare variable declarations to be converted into separate let clauses.
bool TryProcessLocalDeclarationStatement(LocalDeclarationStatementSyntax localDeclarationStatement)
{
if (!convertLocalDeclarations)
{
return false;
}
// Do not support declarations without initialization.
// int a = 0, b, c = 0;
if (localDeclarationStatement.Declaration.Variables.All(variable => variable.Initializer != null))
......
......@@ -20,7 +20,7 @@ public DefaultConverter(ForEachInfo<ForEachStatementSyntax, StatementSyntax> for
{
}
public override void Convert(SyntaxEditor editor, CancellationToken cancellationToken)
public override void Convert(SyntaxEditor editor, bool convertToQuery, CancellationToken cancellationToken)
{
// Filter out identifiers which are not used in statements.
var variableNamesReadInside = new HashSet<string>(ForEachInfo.Statements
......@@ -35,14 +35,15 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
editor.ReplaceNode(
ForEachInfo.ForEachStatement,
CreateDefaultReplacementStatement(ForEachInfo, identifiersUsedInStatements, block)
CreateDefaultReplacementStatement(ForEachInfo, identifiersUsedInStatements, block, convertToQuery)
.WithAdditionalAnnotations(Formatter.Annotation));
}
private StatementSyntax CreateDefaultReplacementStatement(
ForEachInfo<ForEachStatementSyntax, StatementSyntax> forEachInfo,
IEnumerable<SyntaxToken> identifiers,
BlockSyntax block)
BlockSyntax block,
bool convertToQuery)
{
var identifiersCount = identifiers.Count();
if (identifiersCount == 0)
......@@ -51,10 +52,11 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
return SyntaxFactory.ForEachStatement(
VarNameIdentifier,
SyntaxFactory.Identifier("_"),
CreateQueryExpression(
CreateQueryExpressionOrLinqInvocation(
SyntaxFactory.AnonymousObjectCreationExpression(),
Enumerable.Empty<SyntaxToken>(),
Enumerable.Empty<SyntaxToken>()),
Enumerable.Empty<SyntaxToken>(),
convertToQuery),
block);
}
else if (identifiersCount == 1)
......@@ -63,10 +65,11 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
return SyntaxFactory.ForEachStatement(
VarNameIdentifier,
identifiers.Single(),
CreateQueryExpression(
CreateQueryExpressionOrLinqInvocation(
SyntaxFactory.IdentifierName(identifiers.Single()),
Enumerable.Empty<SyntaxToken>(),
Enumerable.Empty<SyntaxToken>()),
Enumerable.Empty<SyntaxToken>(),
convertToQuery),
block);
}
else
......@@ -83,10 +86,11 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
// Generate foreach(var (a,b) ... select (a, b))
return SyntaxFactory.ForEachVariableStatement(
declaration,
CreateQueryExpression(
CreateQueryExpressionOrLinqInvocation(
tupleForSelectExpression,
Enumerable.Empty<SyntaxToken>(),
Enumerable.Empty<SyntaxToken>()),
Enumerable.Empty<SyntaxToken>(),
convertToQuery),
block);
}
}
......
......@@ -39,11 +39,11 @@ internal sealed class ToCountConverter : AbstractToMethodConverter
///
/// Output:
/// counter += queryGenerated.Count();
protected override StatementSyntax CreateDefaultStatement(QueryExpressionSyntax queryExpression, ExpressionSyntax expression)
protected override StatementSyntax CreateDefaultStatement(ExpressionSyntax queryOrLinqInvocationExpression, ExpressionSyntax expression)
=> SyntaxFactory.ExpressionStatement(
SyntaxFactory.AssignmentExpression(
SyntaxKind.AddAssignmentExpression,
expression,
CreateInvocationExpression(queryExpression)));
CreateInvocationExpression(queryOrLinqInvocationExpression)));
}
}
......@@ -44,13 +44,13 @@ internal sealed class ToToListConverter : AbstractToMethodConverter
///
/// Output:
/// list.AddRange(queryGenerated);
protected override StatementSyntax CreateDefaultStatement(QueryExpressionSyntax queryExpression, ExpressionSyntax expression)
protected override StatementSyntax CreateDefaultStatement(ExpressionSyntax queryOrLinqInvocationExpression, ExpressionSyntax expression)
=> SyntaxFactory.ExpressionStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
expression,
SyntaxFactory.IdentifierName(nameof(List<object>.AddRange))),
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(queryExpression)))));
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(queryOrLinqInvocationExpression)))));
}
}
......@@ -23,9 +23,9 @@ internal sealed class YieldReturnConverter : AbstractConverter
_yieldBreakStatement = yieldBreakStatement;
}
public override void Convert(SyntaxEditor editor, CancellationToken cancellationToken)
public override void Convert(SyntaxEditor editor, bool convertToQuery, CancellationToken cancellationToken)
{
var queryExpression = CreateQueryExpression(
var queryOrLinqInvocationExpression = CreateQueryExpressionOrLinqInvocation(
selectExpression: _yieldReturnStatement.Expression,
leadingTokensForSelect: new[] { _yieldReturnStatement.YieldKeyword, _yieldReturnStatement.ReturnOrBreakKeyword },
trailingTokensForSelect: _yieldBreakStatement != null
......@@ -33,11 +33,12 @@ public override void Convert(SyntaxEditor editor, CancellationToken cancellation
_yieldBreakStatement.YieldKeyword,
_yieldBreakStatement.ReturnOrBreakKeyword,
_yieldBreakStatement.SemicolonToken }
: new[] { _yieldReturnStatement.SemicolonToken });
: new[] { _yieldReturnStatement.SemicolonToken },
convertToQuery: convertToQuery);
editor.ReplaceNode(
ForEachInfo.ForEachStatement,
SyntaxFactory.ReturnStatement(queryExpression).WithAdditionalAnnotations(Formatter.Annotation));
SyntaxFactory.ReturnStatement(queryOrLinqInvocationExpression).WithAdditionalAnnotations(Formatter.Annotation));
// Delete the yield break just after the loop.
if (_yieldBreakStatement != null)
......
......@@ -25,7 +25,7 @@ internal abstract class AbstractConvertForEachToLinqQueryProvider<TForEachStatem
/// 4) trailing comments to be added to the end
/// </returns>
protected abstract ForEachInfo<TForEachStatement, TStatement> CreateForEachInfo(
TForEachStatement forEachStatement, SemanticModel semanticModel);
TForEachStatement forEachStatement, SemanticModel semanticModel, bool convertLocalDeclarations);
/// <summary>
/// Tries to build a specific converter that covers e.g. Count, ToList, yield return or other similar cases.
......@@ -84,7 +84,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (!TryBuildConverter(forEachStatement, semanticModel, cancellationToken, out var converter))
if (!TryBuildConverter(forEachStatement, semanticModel, convertLocalDeclarations: true, cancellationToken, out var queryConverter))
{
return;
}
......@@ -95,19 +95,56 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return;
}
// Offer refactoring to convert foreach to LINQ query expression. For example:
//
// INPUT:
// foreach (var n1 in c1)
// foreach (var n2 in c2)
// if (n1 > n2)
// yield return n1 + n2;
//
// OUTPUT:
// from n1 in c1
// from n2 in c2
// where n1 > n2
// select n1 + n2
//
context.RegisterRefactoring(
new ForEachToLinqQueryCodeAction(
FeaturesResources.Convert_to_query,
c => ApplyConversion(converter, document, c)));
FeaturesResources.Convert_to_linq,
c => ApplyConversion(queryConverter, document, convertToQuery: true, c)));
// Offer refactoring to convert foreach to LINQ invocation expression. For example:
//
// INPUT:
// foreach (var n1 in c1)
// foreach (var n2 in c2)
// if (n1 > n2)
// yield return n1 + n2;
//
// OUTPUT:
// c1.SelectMany(n1 => c2.Where(n2 => n1 > n2).Select(n2 => n1 + n2))
//
// CONSIDER: Currently, we do not handle foreach statements with a local declaration statement for Convert_to_linq refactoring,
// though we do handle them for the Convert_to_query refactoring above.
// In future, we can consider supporting them by creating anonymous objects/tuples wrapping the local declarations.
if (TryBuildConverter(forEachStatement, semanticModel, convertLocalDeclarations: false, cancellationToken, out var linqConverter))
{
context.RegisterRefactoring(
new ForEachToLinqQueryCodeAction(
FeaturesResources.Convert_to_linq_call_form,
c => ApplyConversion(linqConverter, document, convertToQuery: false, c)));
}
}
private Task<Document> ApplyConversion(
IConverter<TForEachStatement, TStatement> converter,
Document document,
IConverter<TForEachStatement, TStatement> converter,
Document document,
bool convertToQuery,
CancellationToken cancellationToken)
{
var editor = new SyntaxEditor(converter.ForEachInfo.SemanticModel.SyntaxTree.GetRoot(cancellationToken), document.Project.Solution.Workspace);
converter.Convert(editor, cancellationToken);
converter.Convert(editor, convertToQuery, cancellationToken);
var newRoot = editor.GetChangedRoot();
var rootWithLinqUsing = AddLinqUsing(converter, converter.ForEachInfo.SemanticModel, newRoot);
return Task.FromResult(document.WithSyntaxRoot(rootWithLinqUsing));
......@@ -119,10 +156,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
private bool TryBuildConverter(
TForEachStatement forEachStatement,
SemanticModel semanticModel,
bool convertLocalDeclarations,
CancellationToken cancellationToken,
out IConverter<TForEachStatement, TStatement> converter)
{
var forEachInfo = CreateForEachInfo(forEachStatement, semanticModel);
var forEachInfo = CreateForEachInfo(forEachStatement, semanticModel, convertLocalDeclarations);
if (forEachInfo.Statements.Length == 1 &&
TryBuildSpecificConverter(forEachInfo, semanticModel, forEachInfo.Statements.Single(), cancellationToken, out converter))
......
......@@ -9,6 +9,6 @@ internal interface IConverter<TForEachStatement, TStatement>
{
ForEachInfo<TForEachStatement, TStatement> ForEachInfo { get; }
void Convert(SyntaxEditor editor, CancellationToken cancellationToken);
void Convert(SyntaxEditor editor, bool convertToQuery, CancellationToken cancellationToken);
}
}
......@@ -934,14 +934,23 @@ internal class FeaturesResources {
}
/// <summary>
/// Looks up a localized string similar to Convert to query.
/// Looks up a localized string similar to Convert to LINQ.
/// </summary>
internal static string Convert_to_query {
internal static string Convert_to_linq {
get {
return ResourceManager.GetString("Convert_to_query", resourceCulture);
return ResourceManager.GetString("Convert_to_linq", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Convert to LINQ (call form).
/// </summary>
internal static string Convert_to_linq_call_form {
get {
return ResourceManager.GetString("Convert_to_linq_call_form", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Convert to struct.
/// </summary>
......
......@@ -1376,8 +1376,8 @@ This version used in: {2}</value>
<data name="Convert_to_conditional_expression" xml:space="preserve">
<value>Convert to conditional expression</value>
</data>
<data name="Convert_to_query" xml:space="preserve">
<value>Convert to query</value>
<data name="Convert_to_linq" xml:space="preserve">
<value>Convert to LINQ</value>
</data>
<data name="Convert_to_tuple" xml:space="preserve">
<value>Convert to tuple</value>
......@@ -1403,4 +1403,7 @@ This version used in: {2}</value>
<data name="Formatting_document" xml:space="preserve">
<value>Formatting document</value>
</data>
<data name="Convert_to_linq_call_form" xml:space="preserve">
<value>Convert to LINQ (call form)</value>
</data>
</root>
\ No newline at end of file
......@@ -22,9 +22,9 @@
<target state="translated">Akce nemůžou zůstat prázdné.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Aktionen dürfen nicht leer sein.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Las acciones no pueden estar vacías.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Les actions ne peuvent pas être vides.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Il campo Azioni non può essere vuoto.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">アクションは空にできません。</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">작업은 비워 둘 수 없습니다.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Akcje nie mogą być puste.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Ações não podem ficar vazias.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Действия не могут быть пустыми.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">Eylemler boş olamaz.</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">操作不能为空。</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
......@@ -22,9 +22,9 @@
<target state="translated">動作不可為空。</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_query">
<source>Convert to query</source>
<target state="new">Convert to query</target>
<trans-unit id="Convert_to_linq">
<source>Convert to LINQ</source>
<target state="new">Convert to LINQ</target>
<note />
</trans-unit>
<trans-unit id="Add_to_0">
......@@ -37,6 +37,11 @@
<target state="new">Convert to class</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_linq_call_form">
<source>Convert to LINQ (call form)</source>
<target state="new">Convert to LINQ (call form)</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_struct">
<source>Convert to struct</source>
<target state="new">Convert to struct</target>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册