提交 96d2cc3d 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #14925 from CyrusNajmabadi/useCollection2

Support generating dictionary intializers.
......@@ -10860,18 +10860,13 @@ private ExpressionSyntax ParseDictionaryInitializer()
{
var arguments = this.ParseBracketedArgumentList();
var equal = this.EatToken(SyntaxKind.EqualsToken);
ExpressionSyntax expression;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
expression = this.ParseObjectOrCollectionInitializer();
}
else
{
expression = this.ParseExpressionCore();
}
var expression = this.CurrentToken.Kind == SyntaxKind.OpenBraceToken
? this.ParseObjectOrCollectionInitializer()
: this.ParseExpressionCore();
var elementAccess = _syntaxFactory.ImplicitElementAccess(arguments);
return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, elementAccess, equal, expression);
return _syntaxFactory.AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression, elementAccess, equal, expression);
}
private InitializerExpressionSyntax ParseComplexElementInitializer()
......
......@@ -817,6 +817,7 @@ static Microsoft.CodeAnalysis.Semantics.UnaryAndBinaryOperationExtensions.GetUna
static Microsoft.CodeAnalysis.Semantics.UnaryAndBinaryOperationExtensions.GetUnaryOperandKind(this Microsoft.CodeAnalysis.Semantics.IUnaryOperatorExpression unary) -> Microsoft.CodeAnalysis.Semantics.UnaryOperandKind
static Microsoft.CodeAnalysis.SeparatedSyntaxList<TNode>.implicit operator Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.SeparatedSyntaxList<TNode> nodes) -> Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.SyntaxNode>
static Microsoft.CodeAnalysis.SeparatedSyntaxList<TNode>.implicit operator Microsoft.CodeAnalysis.SeparatedSyntaxList<TNode>(Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.SyntaxNode> nodes) -> Microsoft.CodeAnalysis.SeparatedSyntaxList<TNode>
static Microsoft.CodeAnalysis.SyntaxNodeExtensions.WithoutTrivia(this Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.SyntaxToken
static Microsoft.CodeAnalysis.Text.SourceText.From(System.IO.Stream stream, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false, bool canBeEmbedded = false) -> Microsoft.CodeAnalysis.Text.SourceText
static Microsoft.CodeAnalysis.Text.SourceText.From(System.IO.Stream stream, System.Text.Encoding encoding, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm, bool throwIfBinaryDetected) -> Microsoft.CodeAnalysis.Text.SourceText
static Microsoft.CodeAnalysis.Text.SourceText.From(System.IO.TextReader reader, int length, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.Text.SourceText
......
......@@ -331,6 +331,13 @@ public static TSyntax WithoutTrivia<TSyntax>(this TSyntax syntax)
return syntax.WithoutLeadingTrivia().WithoutTrailingTrivia();
}
/// <summary>
/// Creates a new token from this token without leading or trailing trivia.
/// </summary>
public static SyntaxToken WithoutTrivia(this SyntaxToken token)
=> token.WithTrailingTrivia(default(SyntaxTriviaList))
.WithLeadingTrivia(default(SyntaxTriviaList));
/// <summary>
/// Creates a new node from this node with the leading trivia replaced.
/// </summary>
......
......@@ -49,6 +49,200 @@ void M()
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestIndexAccess1()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c[1] = 2;
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = new List<int>
{
[1] = 2
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestIndexAccess1_NotInCSharp5()
{
await TestMissingAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c[1] = 2;
}
}", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestComplexIndexAccess1()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
a.b.c = [||]new List<int>();
a.b.c[1] = 2;
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
a.b.c = new List<int>
{
[1] = 2
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestIndexAccess2()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c[1] = 2;
c[2] = """";
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = new List<int>
{
[1] = 2,
[2] = """"
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestIndexAccess3()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c[1] = 2;
c[2] = """";
c[3, 4] = 5;
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = new List<int>
{
[1] = 2,
[2] = """",
[3, 4] = 5
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestIndexFollowedByInvocation()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c[1] = 2;
c.Add(0);
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = new List<int>
{
[1] = 2
};
c.Add(0);
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestInvocationFollowedByIndex()
{
await TestAsync(
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = [||]new List<int>();
c.Add(0);
c[1] = 2;
}
}",
@"
using System.Collections.Generic;
class C
{
void M()
{
var c = new List<int>
{
0
};
c[1] = 2;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestWithInterimStatement()
{
......
// 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.Collections.Immutable;
using System.Composition;
......@@ -48,26 +49,11 @@ internal class CSharpUseCollectionInitializerCodeFixProvider :
for (int i = 0; i < matches.Length; i++)
{
var expressionStatement = matches[i];
var invocation = (InvocationExpressionSyntax)expressionStatement.Expression;
var arguments = invocation.ArgumentList.Arguments;
ExpressionSyntax newExpression;
if (arguments.Count == 1)
{
newExpression = arguments[0].Expression.WithoutTrivia();
}
else
{
newExpression = SyntaxFactory.InitializerExpression(
SyntaxKind.ComplexElementInitializerExpression,
SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.OpenBraceToken, default(SyntaxTriviaList)),
SyntaxFactory.SeparatedList(
arguments.Select(a => a.Expression),
arguments.GetSeparators()),
SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.CloseBraceToken, default(SyntaxTriviaList)));
}
var newExpression = ConvertExpression(expressionStatement.Expression)
.WithoutTrivia()
.WithLeadingTrivia(expressionStatement.GetLeadingTrivia());
newExpression = newExpression.WithLeadingTrivia(expressionStatement.GetLeadingTrivia());
if (i < matches.Length - 1)
{
nodesAndTokens.Add(newExpression);
......@@ -86,5 +72,44 @@ internal class CSharpUseCollectionInitializerCodeFixProvider :
return SyntaxFactory.SeparatedList<ExpressionSyntax>(nodesAndTokens);
}
private static ExpressionSyntax ConvertExpression(ExpressionSyntax expression)
{
if (expression is InvocationExpressionSyntax)
{
return ConvertInvocation((InvocationExpressionSyntax)expression);
}
else if (expression is AssignmentExpressionSyntax)
{
return ConvertAssignment((AssignmentExpressionSyntax)expression);
}
throw new InvalidOperationException();
}
private static ExpressionSyntax ConvertAssignment(AssignmentExpressionSyntax assignment)
{
var elementAccess = (ElementAccessExpressionSyntax)assignment.Left;
return assignment.WithLeft(
SyntaxFactory.ImplicitElementAccess(elementAccess.ArgumentList));
}
private static ExpressionSyntax ConvertInvocation(InvocationExpressionSyntax invocation)
{
var arguments = invocation.ArgumentList.Arguments;
if (arguments.Count == 1)
{
return arguments[0].Expression;
}
return SyntaxFactory.InitializerExpression(
SyntaxKind.ComplexElementInitializerExpression,
SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithoutTrivia(),
SyntaxFactory.SeparatedList(
arguments.Select(a => a.Expression),
arguments.GetSeparators()),
SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithoutTrivia());
}
}
}
\ No newline at end of file
......@@ -82,9 +82,10 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
}
var syntaxFacts = GetSyntaxFactsService();
var analyzer = new Analyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TInvocationExpressionSyntax, TExpressionStatementSyntax, TVariableDeclarator>(
syntaxFacts,
objectCreationExpression);
var analyzer = new Analyzer<
TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax, TInvocationExpressionSyntax,
TExpressionStatementSyntax, TVariableDeclarator>(syntaxFacts, objectCreationExpression);
var matches = analyzer.Analyze();
if (matches.Length == 0)
{
......@@ -205,6 +206,9 @@ private void AddMatches(ArrayBuilder<TExpressionStatementSyntax> matches)
var containingBlock = _containingStatement.Parent;
var foundStatement = false;
var seenInvocation = false;
var seenIndexAssignment = false;
foreach (var child in containingBlock.ChildNodesAndTokens())
{
if (!foundStatement)
......@@ -228,21 +232,64 @@ private void AddMatches(ArrayBuilder<TExpressionStatementSyntax> matches)
return;
}
SyntaxNode instance;
if (!TryAnalyzeAddInvocation(statement, out instance))
SyntaxNode instance = null;
if (!seenIndexAssignment)
{
if (TryAnalyzeAddInvocation(statement, out instance))
{
seenInvocation = true;
}
}
if (!seenInvocation)
{
if (TryAnalyzeIndexAssignment(statement, out instance))
{
seenIndexAssignment = true;
}
}
if (instance == null)
{
return;
}
if (!ValuePatternMatches((TExpressionSyntax)instance))
{
break;
return;
}
matches.Add(statement);
}
}
private bool TryAnalyzeIndexAssignment(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
{
instance = null;
if (!_syntaxFacts.SupportsIndexingInitializer(statement.SyntaxTree.Options))
{
return false;
}
if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
{
return false;
}
SyntaxNode left, right;
_syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out right);
if (!_syntaxFacts.IsElementAccessExpression(left))
{
return false;
}
instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left);
return true;
}
private bool TryAnalyzeAddInvocation(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
......
......@@ -28,6 +28,11 @@ private CSharpSyntaxFactsService()
{
}
public bool IsCaseSensitive => true;
public bool SupportsIndexingInitializer(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp6;
public bool IsAwaitKeyword(SyntaxToken token)
{
return token.IsKind(SyntaxKind.AwaitKeyword);
......@@ -208,14 +213,6 @@ public SyntaxToken GetIdentifierOfGenericName(SyntaxNode genericName)
: default(SyntaxToken);
}
public bool IsCaseSensitive
{
get
{
return true;
}
}
public bool IsUsingDirectiveName(SyntaxNode node)
{
return
......
......@@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.LanguageServices
internal interface ISyntaxFactsService : ILanguageService
{
bool IsCaseSensitive { get; }
bool SupportsIndexingInitializer(ParseOptions options);
bool IsAwaitKeyword(SyntaxToken token);
bool IsIdentifier(SyntaxToken token);
......
......@@ -35,6 +35,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Private Sub New()
End Sub
Public ReadOnly Property IsCaseSensitive As Boolean Implements ISyntaxFactsService.IsCaseSensitive
Get
Return False
End Get
End Property
Public Function SupportsIndexingInitializer(options As ParseOptions) As Boolean Implements ISyntaxFactsService.SupportsIndexingInitializer
Return False
End Function
Public Function IsAwaitKeyword(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsAwaitKeyword
Return token.Kind = SyntaxKind.AwaitKeyword
End Function
......@@ -181,12 +191,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return If(vbGenericName IsNot Nothing, vbGenericName.Identifier, Nothing)
End Function
Public ReadOnly Property IsCaseSensitive As Boolean Implements ISyntaxFactsService.IsCaseSensitive
Get
Return False
End Get
End Property
Public Function IsUsingDirectiveName(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsUsingDirectiveName
Return node.IsParentKind(SyntaxKind.SimpleImportsClause) AndAlso
DirectCast(node.Parent, SimpleImportsClauseSyntax).Name Is node
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册