提交 f077ea1e 编写于 作者: C Cyrus Najmabadi

Initial work to support converting a for-statement to a foreach.

上级 86067f23
......@@ -287,6 +287,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Convert &apos;for&apos; to &apos;foreach&apos;.
/// </summary>
internal static string Convert_for_to_foreach {
get {
return ResourceManager.GetString("Convert_for_to_foreach", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Convert &apos;if&apos; to &apos;switch&apos;.
/// </summary>
......@@ -467,15 +476,6 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to &lt;implicit array creation&gt;.
/// </summary>
internal static string implicit_array_creation {
get {
return ResourceManager.GetString("implicit_array_creation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to indexer.
/// </summary>
......
......@@ -521,4 +521,7 @@
<data name="Add_parentheses_around_conditional_expression_in_interpolated_string" xml:space="preserve">
<value>Add parentheses</value>
</data>
<data name="Convert_for_to_foreach" xml:space="preserve">
<value>Convert 'for' to 'foreach'</value>
</data>
</root>
\ No newline at end of file
// 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;
using System.Collections.Generic;
using System.Composition;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.ConvertForToForEach;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.ConvertForToForEach
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpConvertForToForEachCodeRefactoringProvider)), Shared]
internal class CSharpConvertForToForEachCodeRefactoringProvider :
AbstractConvertForToForEachCodeRefactoringProvider<
StatementSyntax,
ForStatementSyntax,
ExpressionSyntax,
MemberAccessExpressionSyntax,
TypeSyntax,
VariableDeclaratorSyntax>
{
protected override string GetTitle()
=> CSharpFeaturesResources.Convert_for_to_foreach;
protected override SyntaxList<StatementSyntax> GetBodyStatements(ForStatementSyntax forStatement)
{
if (forStatement.Statement is BlockSyntax block)
{
return block.Statements;
}
return SyntaxFactory.SingletonList(forStatement.Statement);
}
protected override bool TryGetForStatementComponents(
ForStatementSyntax forStatement,
out SyntaxToken iterationVariable, out ExpressionSyntax initializer,
out MemberAccessExpressionSyntax memberAccess, out ExpressionSyntax stepValue,
CancellationToken cancellationToken)
{
if (forStatement.Declaration != null &&
forStatement.Condition is BinaryExpressionSyntax binaryExpression &&
forStatement.Incrementors.Count == 1)
{
var declaration = forStatement.Declaration;
if (declaration.Variables.Count == 1)
{
var declarator = declaration.Variables[0];
if (declarator.Initializer != null)
{
iterationVariable = declarator.Identifier;
initializer = declarator.Initializer.Value;
// Look for: i < expr.Length
if (binaryExpression.Kind() == SyntaxKind.LessThanExpression &&
binaryExpression.Left is IdentifierNameSyntax identifierName &&
identifierName.Identifier.ValueText == iterationVariable.ValueText &&
binaryExpression.Right is MemberAccessExpressionSyntax)
{
memberAccess = (MemberAccessExpressionSyntax)binaryExpression.Right;
var incrementor = forStatement.Incrementors[0];
if (TryGetStepValue(iterationVariable, incrementor, out stepValue, cancellationToken))
{
return true;
}
}
}
}
}
iterationVariable = default;
memberAccess = default;
initializer = default;
stepValue = default;
return false;
}
private static bool TryGetStepValue(
SyntaxToken iterationVariable, ExpressionSyntax incrementor,
out ExpressionSyntax stepValue, CancellationToken cancellationToken)
{
// support
// x++
// ++x
// x += constant_1
ExpressionSyntax operand;
if (incrementor.Kind() == SyntaxKind.PostIncrementExpression)
{
operand = ((PostfixUnaryExpressionSyntax)incrementor).Operand;
stepValue = default;
}
else if (incrementor.Kind() == SyntaxKind.PreIncrementExpression)
{
operand = ((PrefixUnaryExpressionSyntax)incrementor).Operand;
stepValue = default;
}
else if (incrementor.Kind() == SyntaxKind.AddAssignmentExpression)
{
var assignment = (AssignmentExpressionSyntax)incrementor;
operand = assignment.Left;
stepValue = assignment.Right;
}
else
{
stepValue = default;
return false;
}
return operand is IdentifierNameSyntax identifierName &&
identifierName.Identifier.ValueText == iterationVariable.ValueText;
}
protected override SyntaxNode ConvertForNode(
ForStatementSyntax forStatement, TypeSyntax typeNode,
SyntaxToken foreachIdentifier, ExpressionSyntax collectionExpression,
ITypeSymbol iterationVariableType, OptionSet optionSet)
{
if (typeNode == null)
{
// types are not apperant in foreach statements.
var isBuiltInTypeContext = TypeStyleHelper.IsBuiltInType(iterationVariableType);
if (TypeStyleHelper.IsImplicitStylePreferred(
optionSet, isBuiltInTypeContext, isTypeApparentContext: false))
{
typeNode = SyntaxFactory.IdentifierName("var");
}
else
{
typeNode = (TypeSyntax)CSharpSyntaxGenerator.Instance.TypeExpression(iterationVariableType);
}
}
return SyntaxFactory.ForEachStatement(
SyntaxFactory.Token(SyntaxKind.ForEachKeyword).WithTriviaFrom(forStatement.ForKeyword),
forStatement.OpenParenToken,
typeNode,
foreachIdentifier,
SyntaxFactory.Token(SyntaxKind.InKeyword),
collectionExpression,
forStatement.CloseParenToken,
forStatement.Statement);
}
protected override bool IsValidVariableDeclarator(VariableDeclaratorSyntax firstVariable)
=> true;
}
}
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -632,6 +632,11 @@
<target state="new">Add parentheses</target>
<note />
</trans-unit>
<trans-unit id="Convert_for_to_foreach">
<source>Convert 'for' to 'foreach'</source>
<target state="new">Convert 'for' to 'foreach'</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
// 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;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.ConvertForToForEach
{
internal abstract class AbstractConvertForToForEachCodeRefactoringProvider<
TStatementSyntax,
TForStatementSyntax,
TExpressionSyntax,
TMemberAccessExpressionSyntax,
TTypeNode,
TVariableDeclaratorSyntax> : CodeRefactoringProvider
where TStatementSyntax : SyntaxNode
where TForStatementSyntax : TStatementSyntax
where TExpressionSyntax : SyntaxNode
where TMemberAccessExpressionSyntax : SyntaxNode
where TTypeNode : SyntaxNode
where TVariableDeclaratorSyntax : SyntaxNode
{
protected abstract string GetTitle();
protected abstract SyntaxNode ConvertForNode(
TForStatementSyntax currentFor, TTypeNode typeNode, SyntaxToken foreachIdentifier,
TExpressionSyntax collectionExpression, ITypeSymbol iterationVariableType, OptionSet options);
protected abstract SyntaxList<TStatementSyntax> GetBodyStatements(TForStatementSyntax forStatement);
protected abstract bool IsValidVariableDeclarator(TVariableDeclaratorSyntax firstVariable);
protected abstract bool TryGetForStatementComponents(
TForStatementSyntax forStatement,
out SyntaxToken iterationVariable, out TExpressionSyntax initializer,
out TMemberAccessExpressionSyntax memberAccess, out TExpressionSyntax stepValue,
CancellationToken cancellationToken);
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var cancellationToken = context.CancellationToken;
var document = context.Document;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(context.Span.Start);
// 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))
{
return;
}
if (!context.Span.IsEmpty && context.Span != token.Span)
{
return;
}
var forStatement = token.Parent as TForStatementSyntax;
if (forStatement == null)
{
return;
}
if (!TryGetForStatementComponents(forStatement,
out var iterationVariable, out var initializer,
out var memberAccess, out var stepValueOpt, cancellationToken))
{
return;
}
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess,
out var collectionExpressionNode, out var memberAccessNameNode);
var collectionExpression = (TExpressionSyntax)collectionExpressionNode;
syntaxFacts.GetNameAndArityOfSimpleName(memberAccessNameNode, out var memberAccessName, out _);
if (memberAccessName != nameof(Array.Length) && memberAccessName != nameof(IList.Count))
{
return;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
// Make sure we're starting at 0.
var initializerValue = semanticModel.GetConstantValue(initializer, cancellationToken);
if (!(initializerValue.HasValue && initializerValue.Value is 0))
{
return;
}
// Make sure we're incrementing by 1.
if (stepValueOpt != null)
{
var stepValue = semanticModel.GetConstantValue(stepValueOpt);
if (!(stepValue.HasValue && stepValue.Value is 1))
{
return;
}
}
var collectionType = semanticModel.GetTypeInfo(collectionExpression, cancellationToken);
if (collectionType.Type == null && collectionType.Type.TypeKind == TypeKind.Error)
{
return;
}
var containingType = semanticModel.GetEnclosingNamedType(context.Span.Start, cancellationToken);
if (containingType == null)
{
return;
}
var ienumerableType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T);
var ienumeratorType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerator_T);
// make sure the collection can be iterated.
if (TryGetIterationElementType(
containingType, collectionType.Type,
ienumerableType, ienumeratorType,
out var iterationType))
{
return;
}
// If the user uses the iteration variable for any other reason, we can't convert this.
var bodyStatements = GetBodyStatements(forStatement);
foreach (var statement in bodyStatements)
{
if (IterationVariableIsUsedForMoreThanCollectionIndex(statement))
{
return;
}
}
// Looks good. We can conver this.
context.RegisterRefactoring(new MyCodeAction(GetTitle(),
c => ConvertForToForEachAsync(
document, forStatement, iterationVariable,
collectionExpression, iterationType, c)));
// local functions
bool IterationVariableIsUsedForMoreThanCollectionIndex(SyntaxNode current)
{
if (syntaxFacts.IsIdentifierName(current))
{
syntaxFacts.GetNameAndArityOfSimpleName(current, out var name, out _);
if (name == iterationVariable.ValueText)
{
// found a reference. make sure it's only used inside something like
// list[i]
if (!syntaxFacts.IsElementAccessExpression(current.Parent))
{
// used in something other than accessing into a collection.
// can't convert this for-loop.
return true;
}
if (!syntaxFacts.AreEquivalent(current.Parent, collectionExpression))
{
// was indexing into something other than the collection.
// can't convert this for-loop.
return true;
}
// this usage of the for-variable is fine.
}
}
foreach (var child in current.ChildNodesAndTokens())
{
if (child.IsNode)
{
if (IterationVariableIsUsedForMoreThanCollectionIndex(child.AsNode()))
{
return true;
}
}
}
return false;
}
}
private bool TryGetIterationElementType(
INamedTypeSymbol containingType, ITypeSymbol collectionType,
INamedTypeSymbol ienumerableType, INamedTypeSymbol ienumeratorType,
out ITypeSymbol iterationType)
{
// Check in the class/struct hierarchy first.
var methods = collectionType.GetAccessibleMembersInThisAndBaseTypes<IMethodSymbol>(containingType);
var getEnumeratorMethod = methods.FirstOrDefault(m => m.Name == nameof(IEnumerable.GetEnumerator));
if (getEnumeratorMethod != null)
{
return TryGetIterationElementTypeFromGetEnumerator(
containingType, getEnumeratorMethod, ienumeratorType, out iterationType);
}
// couldn't find .GetEnumerator on the class/struct. Check the interface hierarchy.
var instantiatedIEnumerableType = collectionType.GetAllInterfacesIncludingThis().FirstOrDefault(
t => Equals(t.OriginalDefinition, ienumerableType));
if (instantiatedIEnumerableType != null)
{
iterationType = instantiatedIEnumerableType.TypeArguments[0];
return true;
}
iterationType = default;
return false;
}
private bool TryGetIterationElementTypeFromGetEnumerator(
INamedTypeSymbol containingType, IMethodSymbol getEnumeratorMethod,
INamedTypeSymbol ienumeratorType, out ITypeSymbol iterationType)
{
var getEnumeratorReturnType = getEnumeratorMethod.ReturnType;
// Check in the class/struct hierarchy first.
var properties = getEnumeratorReturnType.GetAccessibleMembersInThisAndBaseTypes<IPropertySymbol>(containingType);
var currentProperty = properties.FirstOrDefault(m => m.Name == nameof(IEnumerator.Current));
if (currentProperty != null)
{
iterationType = currentProperty.Type;
return true;
}
// couldn't find .Current on the class/struct. Check the interface hierarchy.
var instantiatedIEnumeratorType = getEnumeratorReturnType.GetAllInterfacesIncludingThis().FirstOrDefault(
t => Equals(t.OriginalDefinition, ienumeratorType));
if (instantiatedIEnumeratorType != null)
{
iterationType = instantiatedIEnumeratorType.TypeArguments[0];
return true;
}
iterationType = default;
return false;
}
private async Task<Document> ConvertForToForEachAsync(
Document document, TForStatementSyntax forStatement,
SyntaxToken iterationVariable, TExpressionSyntax collectionExpression,
ITypeSymbol iterationType, CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var semanticFacts = document.GetLanguageService<ISemanticFactsService>();
var generator = SyntaxGenerator.GetGenerator(document);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, generator);
// create a dummy "list[i]" expression. We'll use this to find all places to replace
// in the current for statement.
var indexExpression = generator.ElementAccessExpression(
collectionExpression, generator.IdentifierName(iterationVariable));
// See if the first statement in the for loop is of the form:
// var x = list[i] or
//
// If so, we'll use those as the iteration variables for the new foreach statement.
var bodyStatements = GetBodyStatements(forStatement);
TTypeNode typeNode = default;
SyntaxToken foreachIdentifier = default;
SyntaxNode ignoreStatement = default;
if (bodyStatements.Count >= 1)
{
var firstStatement = bodyStatements[0];
if (syntaxFacts.IsLocalDeclarationStatement(firstStatement))
{
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(firstStatement);
if (variables.Count == 1)
{
var firstVariable = (TVariableDeclaratorSyntax)variables[0];
if (IsValidVariableDeclarator(firstVariable))
{
var firstVariableInitializer = syntaxFacts.GetInitializerOfVariableDeclarator(firstVariable);
if (syntaxFacts.AreEquivalent(firstVariableInitializer, indexExpression))
{
typeNode = (TTypeNode)syntaxFacts.GetTypeOfVariableDeclarator(firstVariable);
foreachIdentifier = syntaxFacts.GetIdentifierOfVariableDeclarator(firstVariable);
editor.RemoveNode(firstStatement);
ignoreStatement = firstStatement;
}
}
}
}
}
if (foreachIdentifier.RawKind == 0)
{
foreachIdentifier = generator.Identifier("v");
}
var foreachIdentifierName = generator.IdentifierName(foreachIdentifier).WithoutTrivia();
foreachIdentifier = foreachIdentifier.WithAdditionalAnnotations(RenameAnnotation.Create());
// Walk the for statement, replacing any matches we find.
recurse(forStatement);
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
editor.ReplaceNode(
forStatement,
(currentFor, _) => this.ConvertForNode(
(TForStatementSyntax)currentFor, typeNode, foreachIdentifier,
collectionExpression, iterationType, options));
return document.WithSyntaxRoot(editor.GetChangedRoot());
// local functions
void recurse(SyntaxNode current)
{
// Do not replace in the first statement if we're just going to remove it
// anyways.
if (current == ignoreStatement)
{
return;
}
if (syntaxFacts.AreEquivalent(current, indexExpression))
{
// Found a match. replace with iteration variable.
if (semanticFacts.IsWrittenTo(semanticModel, current, cancellationToken))
{
editor.ReplaceNode(current, foreachIdentifierName.WithAdditionalAnnotations(
WarningAnnotation.Create(FeaturesResources.Warning_colon_Collection_was_modified_during_iteration)));
}
else
{
editor.ReplaceNode(current, foreachIdentifierName);
}
}
foreach (var child in current.ChildNodesAndTokens())
{
if (child.IsNode)
{
recurse(child.AsNode());
}
}
}
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument)
: base(title, createChangedDocument, title)
{
}
}
}
}
......@@ -428,6 +428,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Alias ambiguous type &apos;{0}&apos;.
/// </summary>
internal static string Alias_ambiguous_type_0 {
get {
return ResourceManager.GetString("Alias_ambiguous_type_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to All lowercase.
/// </summary>
......@@ -3635,15 +3644,6 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Variable declaration can be deconstructed.
/// </summary>
internal static string Variable_declaration_can_be_deconstructed {
get {
return ResourceManager.GetString("Variable_declaration_can_be_deconstructed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Using readonly references will prevent the debug session from continuing..
/// </summary>
......@@ -3671,6 +3671,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Variable declaration can be deconstructed.
/// </summary>
internal static string Variable_declaration_can_be_deconstructed {
get {
return ResourceManager.GetString("Variable_declaration_can_be_deconstructed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Variable declaration can be inlined.
/// </summary>
......@@ -3689,6 +3698,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Warning: Collection was modified during iteration..
/// </summary>
internal static string Warning_colon_Collection_was_modified_during_iteration {
get {
return ResourceManager.GetString("Warning_colon_Collection_was_modified_during_iteration", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Warning: Declaration changes scope and may change meaning..
/// </summary>
......
......@@ -1334,4 +1334,10 @@ This version used in: {2}</value>
<data name="indexer_" xml:space="preserve">
<value>indexer</value>
</data>
<data name="Alias_ambiguous_type_0" xml:space="preserve">
<value>Alias ambiguous type '{0}'</value>
</data>
<data name="Warning_colon_Collection_was_modified_during_iteration" xml:space="preserve">
<value>Warning: Collection was modified during iteration.</value>
</data>
</root>
......@@ -1985,6 +1985,16 @@ Tato verze se používá zde: {2}.</target>
<target state="translated">indexer</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Diese Version wird verwendet in: {2}</target>
<target state="translated">Indexer</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Esta versión se utiliza en: {2}</target>
<target state="translated">indizador</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Version utilisée dans : {2}</target>
<target state="translated">indexeur</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Questa versione è usata {2}</target>
<target state="translated">indicizzatore</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ This version used in: {2}</source>
<target state="translated">インデクサー</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ This version used in: {2}</source>
<target state="translated">인덱서</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Ta wersja jest używana wersja: {2}</target>
<target state="translated">indeksator</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Essa versão é usada no: {2}</target>
<target state="translated">indexador</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ This version used in: {2}</source>
<target state="translated">индексатор</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ Bu sürüm şurada kullanılır: {2}</target>
<target state="translated">dizin oluşturucu</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ This version used in: {2}</source>
<target state="translated">索引器</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1985,6 +1985,16 @@ This version used in: {2}</source>
<target state="translated">索引子</target>
<note />
</trans-unit>
<trans-unit id="Alias_ambiguous_type_0">
<source>Alias ambiguous type '{0}'</source>
<target state="new">Alias ambiguous type '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Collection_was_modified_during_iteration">
<source>Warning: Collection was modified during iteration.</source>
<target state="new">Warning: Collection was modified during iteration.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -46,13 +46,26 @@ internal static class TypeStyleHelper
ExpressionSyntax initializerExpression,
SemanticModel semanticModel,
CancellationToken cancellationToken) =>
semanticModel.GetTypeInfo(initializerExpression, cancellationToken).Type?.IsSpecialType() == true;
IsBuiltInType(semanticModel.GetTypeInfo(initializerExpression, cancellationToken).Type);
private static bool IsImplicitStylePreferred(TypeStylePreference stylePreferences,
public static bool IsBuiltInType(ITypeSymbol type)
=> type?.IsSpecialType() == true;
public static bool IsImplicitStylePreferred(
OptionSet optionSet,
bool isBuiltInTypeContext,
bool isTypeApparentContext)
{
return IsImplicitStylePreferred(
GetCurrentTypeStylePreferences(optionSet),
isBuiltInTypeContext,
isTypeApparentContext);
}
private static bool IsImplicitStylePreferred(TypeStylePreference stylePreferences,
bool isBuiltInTypeContext,
bool isTypeApparentContext)
{
return isBuiltInTypeContext
? stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeForIntrinsicTypes)
: isTypeApparentContext
......
......@@ -1528,14 +1528,10 @@ public void GetPartsOfMemberAccessExpression(SyntaxNode node, out SyntaxNode exp
}
public SyntaxToken GetIdentifierOfSimpleName(SyntaxNode node)
{
return ((SimpleNameSyntax)node).Identifier;
}
=> ((SimpleNameSyntax)node).Identifier;
public SyntaxToken GetIdentifierOfVariableDeclarator(SyntaxNode node)
{
return ((VariableDeclaratorSyntax)node).Identifier;
}
=> ((VariableDeclaratorSyntax)node).Identifier;
public bool IsIdentifierName(SyntaxNode node)
=> node.IsKind(SyntaxKind.IdentifierName);
......@@ -1808,6 +1804,9 @@ public SeparatedSyntaxList<SyntaxNode> GetVariablesOfLocalDeclarationStatement(S
public SyntaxNode GetInitializerOfVariableDeclarator(SyntaxNode node)
=> ((VariableDeclaratorSyntax)node).Initializer;
public SyntaxNode GetTypeOfVariableDeclarator(SyntaxNode node)
=> ((VariableDeclarationSyntax)((VariableDeclaratorSyntax)node).Parent).Type;
public SyntaxNode GetValueOfEqualsValueClause(SyntaxNode node)
=> ((EqualsValueClauseSyntax)node).Value;
......
......@@ -159,6 +159,7 @@ internal interface ISyntaxFactsService : ILanguageService
SyntaxToken GetIdentifierOfGenericName(SyntaxNode node);
SyntaxToken GetIdentifierOfSimpleName(SyntaxNode node);
SyntaxToken GetIdentifierOfVariableDeclarator(SyntaxNode node);
SyntaxNode GetTypeOfVariableDeclarator(SyntaxNode node);
/// <summary>
/// True if this is an argument with just an expression and nothing else (i.e. no ref/out,
......
......@@ -1715,6 +1715,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return DirectCast(node, VariableDeclaratorSyntax).Initializer
End Function
Public Function GetTypeOfVariableDeclarator(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.GetTypeOfVariableDeclarator
Dim declarator = DirectCast(node, VariableDeclaratorSyntax)
Return If(TypeOf declarator.AsClause Is SimpleAsClauseSyntax,
DirectCast(declarator.AsClause, SimpleAsClauseSyntax).Type,
Nothing)
End Function
Public Function GetValueOfEqualsValueClause(node As SyntaxNode) As SyntaxNode Implements ISyntaxFactsService.GetValueOfEqualsValueClause
Return DirectCast(node, EqualsValueSyntax).Value
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册