提交 134daeab 编写于 作者: C Cyrus Najmabadi

Merge remote-tracking branch 'upstream/master' into convertTupleToStruct

......@@ -33,10 +33,10 @@ RUN apt-get install -y libunwind8 \
# Install Mono
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
(echo "deb http://download.mono-project.com/repo/ubuntu stable-xenial/snapshots/5.8.0.108 main" | \
(echo "deb http://download.mono-project.com/repo/ubuntu stable-xenial main" | \
tee /etc/apt/sources.list.d/mono-official.list) && \
apt-get update && \
apt-get install -y mono-devel=5.8.0.108-0xamarin1+ubuntu1604b1 && \
apt-get install -y mono-devel && \
apt-get clean
# Setup User to match Host User, and give superuser permissions
......
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.ConvertAnonymousTypeToTuple;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAnonymousTypeToTuple
{
public partial class ConvertAnonymousTypeToTupleTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer(), new CSharpConvertAnonymousTypeToTupleCodeFixProvider());
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertSingleAnonymousType()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = 2 };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task NotOnEmptyAnonymousType()
{
await TestMissingInRegularAndScriptAsync(@"
class Test
{
void Method()
{
var t1 = [||]new { };
}
}
");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task NotOnSingleFieldAnonymousType()
{
await TestMissingInRegularAndScriptAsync(@"
class Test
{
void Method()
{
var t1 = [||]new { a = 1 };
}
}
");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertSingleAnonymousTypeWithInferredName()
{
var text = @"
class Test
{
void Method(int b)
{
var t1 = [||]new { a = 1, b };
}
}
";
var expected = @"
class Test
{
void Method(int b)
{
var t1 = (a: 1, b);
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertMultipleInstancesInSameMethod()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b: 4);
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertMultipleInstancesAcrossMethods()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
void Method2()
{
var t1 = new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b: 4);
}
void Method2()
{
var t1 = new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task OnlyConvertMatchingTypesInSameMethod()
{
var text = @"
class Test
{
void Method(int b)
{
var t1 = [||]new { a = 1, b = 2 };
var t2 = new { a = 3, b };
var t3 = new { a = 4 };
var t4 = new { b = 5, a = 6 };
}
}
";
var expected = @"
class Test
{
void Method(int b)
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b);
var t3 = new { a = 4 };
var t4 = new { b = 5, a = 6 };
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task TestFixAllInSingleMethod()
{
var text = @"
class Test
{
void Method(int b)
{
var t1 = {|FixAllInDocument:|}new { a = 1, b = 2 };
var t2 = new { a = 3, b };
var t3 = new { a = 4 };
var t4 = new { b = 5, a = 6 };
}
}
";
var expected = @"
class Test
{
void Method(int b)
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b);
var t3 = new { a = 4 };
var t4 = (b: 5, a: 6);
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task TestFixAllAcrossMethods()
{
var text = @"
class Test
{
void Method()
{
var t1 = {|FixAllInDocument:|}new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
void Method2()
{
var t1 = new { a = 1, b = 2 };
var t2 = new { a = 3, b = 4 };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b: 4);
}
void Method2()
{
var t1 = (a: 1, b: 2);
var t2 = (a: 3, b: 4);
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task TestTrivia()
{
var text = @"
class Test
{
void Method()
{
var t1 = /*1*/ [||]new /*2*/ { /*3*/ a /*4*/ = /*5*/ 1 /*7*/ , /*8*/ b /*9*/ = /*10*/ 2 /*11*/ } /*12*/ ;
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = /*1*/ ( /*3*/ a /*4*/ : /*5*/ 1 /*7*/ , /*8*/ b /*9*/ : /*10*/ 2 /*11*/ ) /*12*/ ;
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task TestFixAllNestedTypes()
{
var text = @"
class Test
{
void Method()
{
var t1 = {|FixAllInDocument:|}new { a = 1, b = new { c = 1, d = 2 } };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: (c: 1, d: 2));
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertMultipleNestedInstancesInSameMethod()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = (object)new { a = 1, b = default(object) } };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: (object)(a: 1, b: default(object)));
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertWithLambda1()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = 2 };
Action a = () =>
{
var t2 = new { a = 3, b = 4 };
};
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
Action a = () =>
{
var t2 = (a: 3, b: 4);
};
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertWithLambda2()
{
var text = @"
class Test
{
void Method()
{
var t1 = new { a = 1, b = 2 };
Action a = () =>
{
var t2 = [||]new { a = 3, b = 4 };
};
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
Action a = () =>
{
var t2 = (a: 3, b: 4);
};
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertWithLocalFunction1()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = 1, b = 2 };
void func()
{
var t2 = new { a = 3, b = 4 };
}
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
void func()
{
var t2 = (a: 3, b: 4);
}
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task ConvertWithLocalFunction2()
{
var text = @"
class Test
{
void Method()
{
var t1 = new { a = 1, b = 2 };
void func()
{
var t2 = [||]new { a = 3, b = 4 };
}
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: 1, b: 2);
void func()
{
var t2 = (a: 3, b: 4);
}
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)]
public async Task TestIncompleteAnonymousType()
{
var text = @"
class Test
{
void Method()
{
var t1 = [||]new { a = , b = };
}
}
";
var expected = @"
class Test
{
void Method()
{
var t1 = (a: , b: );
}
}
";
await TestInRegularAndScriptAsync(text, expected);
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertAnonymousTypeToTuple
Partial Public Class ConvertAnonymousTypeToTupleTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (New VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer(), New VisualBasicConvertAnonymousTypeToTupleCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function ConvertSingleAnonymousType() As Task
Dim text = "
class Test
sub Method()
dim t1 = [||]new with { .a = 1, .b = 2 }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=2)
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function NotOnEmptyAnonymousType() As Task
Await TestMissingInRegularAndScriptAsync("
class Test
sub Method()
dim t1 = [||]new with { }
end sub
end class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function NotOnSingleFieldAnonymousType() As Task
Await TestMissingInRegularAndScriptAsync("
class Test
sub Method()
dim t1 = [||]new with { .a = 1 }
end sub
end class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function ConvertSingleAnonymousTypeWithInferredName() As Task
Dim text = "
class Test
sub Method(b as integer)
dim t1 = [||]new with { .a = 1, b }
end sub
end class
"
Dim expected = "
class Test
sub Method(b as integer)
dim t1 = (a:=1, b)
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function ConvertMultipleInstancesInSameMethod() As Task
Dim text = "
class Test
sub Method()
dim t1 = [||]new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b:=4)
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function ConvertMultipleInstancesAcrossMethods() As Task
Dim text = "
class Test
sub Method()
dim t1 = [||]new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
sub Method2()
dim t1 = new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b:=4)
end sub
sub Method2()
dim t1 = new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function OnlyConvertMatchingTypesInSameMethod() As Task
Dim text = "
class Test
sub Method(b as integer)
dim t1 = [||]new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, b }
dim t3 = new with { .a = 4 }
dim t4 = new with { .b = 5, .a = 6 }
end sub
end class
"
Dim expected = "
class Test
sub Method(b as integer)
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b)
dim t3 = new with { .a = 4 }
dim t4 = new with { .b = 5, .a = 6 }
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestFixAllInSingleMethod() As Task
Dim text = "
class Test
sub Method(b as integer)
dim t1 = {|FixAllInDocument:|}new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, b }
dim t3 = new with { .a = 4 }
dim t4 = new with { .b = 5, .a = 6 }
end sub
end class
"
Dim expected = "
class Test
sub Method(b as integer)
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b)
dim t3 = new with { .a = 4 }
dim t4 = (b:=5, a:=6)
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestFixAllAcrossMethods() As Task
Dim text = "
class Test
sub Method()
dim t1 = {|FixAllInDocument:|}new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
sub Method2()
dim t1 = new with { .a = 1, .b = 2 }
dim t2 = new with { .a = 3, .b = 4 }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b:=4)
end sub
sub Method2()
dim t1 = (a:=1, b:=2)
dim t2 = (a:=3, b:=4)
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestFixAllNestedTypes() As Task
Dim text = "
class Test
sub Method()
dim t1 = {|FixAllInDocument:|}new with { .a = 1, .b = new with { .c = 1, .d = 2 } }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=(c:=1, d:=2))
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function ConvertMultipleNestedInstancesInSameMethod() As Task
Dim text = "
class Test
sub Method()
dim t1 = [||]new with { .a = 1, .b = directcast(new with { .a = 1, .b = directcast(nothing, object) }, object) }
end sub
end class
"
Dim expected = "
class Test
sub Method()
dim t1 = (a:=1, b:=directcast((a:=1, b:=directcast(nothing, object)), object))
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestInLambda1() As Task
Dim text = "
Imports System
class Test
sub Method()
dim t1 = [||]new with { .a = 1, .b = 2 }
dim a as Action =
sub()
dim t2 = new with { .a = 3, .b = 4 }
end sub
end sub
end class
"
Dim expected = "
Imports System
class Test
sub Method()
dim t1 = (a:=1, b:=2)
dim a as Action =
sub()
dim t2 = (a:=3, b:=4)
end sub
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestInLambda2() As Task
Dim text = "
Imports System
class Test
sub Method()
dim t1 = new with { .a = 1, .b = 2 }
dim a as Action =
sub()
dim t2 = [||]new with { .a = 3, .b = 4 }
end sub
end sub
end class
"
Dim expected = "
Imports System
class Test
sub Method()
dim t1 = (a:=1, b:=2)
dim a as Action =
sub()
dim t2 = (a:=3, b:=4)
end sub
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)>
Public Async Function TestIncomplete() As Task
Dim text = "
Imports System
class Test
sub Method()
dim t1 = [||]new with { .a = , .b = }
end sub
end class
"
Dim expected = "
Imports System
class Test
sub Method()
dim t1 = (a:= , b:= )
end sub
end class
"
Await TestInRegularAndScriptAsync(text, expected)
End Function
End Class
End Namespace
// 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.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.ConvertAnonymousTypeToTuple
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpConvertAnonymousTypeToTupleCodeFixProvider)), Shared]
internal class CSharpConvertAnonymousTypeToTupleCodeFixProvider
: AbstractConvertAnonymousTypeToTupleCodeFixProvider<
ExpressionSyntax,
TupleExpressionSyntax,
AnonymousObjectCreationExpressionSyntax>
{
protected override TupleExpressionSyntax ConvertToTuple(AnonymousObjectCreationExpressionSyntax anonCreation)
=> SyntaxFactory.TupleExpression(
SyntaxFactory.Token(SyntaxKind.OpenParenToken).WithTriviaFrom(anonCreation.OpenBraceToken),
ConvertInitializers(anonCreation.Initializers),
SyntaxFactory.Token(SyntaxKind.CloseParenToken).WithTriviaFrom(anonCreation.CloseBraceToken))
.WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia());
private static SeparatedSyntaxList<ArgumentSyntax> ConvertInitializers(SeparatedSyntaxList<AnonymousObjectMemberDeclaratorSyntax> initializers)
=> SyntaxFactory.SeparatedList(initializers.Select(ConvertInitializer), initializers.GetSeparators());
private static ArgumentSyntax ConvertInitializer(AnonymousObjectMemberDeclaratorSyntax declarator)
=> SyntaxFactory.Argument(ConvertName(declarator.NameEquals), default, declarator.Expression)
.WithTriviaFrom(declarator);
private static NameColonSyntax ConvertName(NameEqualsSyntax nameEquals)
=> nameEquals == null
? null
: SyntaxFactory.NameColon(
nameEquals.Name,
SyntaxFactory.Token(SyntaxKind.ColonToken).WithTriviaFrom(nameEquals.EqualsToken));
}
}
// 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 Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.ConvertAnonymousTypeToTuple
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer
: AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer<
SyntaxKind,
AnonymousObjectCreationExpressionSyntax>
{
protected override SyntaxKind GetAnonymousObjectCreationExpressionSyntaxKind()
=> SyntaxKind.AnonymousObjectCreationExpression;
protected override int GetInitializerCount(AnonymousObjectCreationExpressionSyntax anonymousType)
=> anonymousType.Initializers.Count;
}
}
// 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.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple
{
internal abstract class AbstractConvertAnonymousTypeToTupleCodeFixProvider<
TExpressionSyntax,
TTupleExpressionSyntax,
TAnonymousObjectCreationExpressionSyntax>
: SyntaxEditorBasedCodeFixProvider
where TExpressionSyntax : SyntaxNode
where TTupleExpressionSyntax : TExpressionSyntax
where TAnonymousObjectCreationExpressionSyntax : TExpressionSyntax
{
protected abstract TTupleExpressionSyntax ConvertToTuple(TAnonymousObjectCreationExpressionSyntax anonCreation);
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(IDEDiagnosticIds.ConvertAnonymousTypeToTupleDiagnosticId);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixAllWithEditorAsync(context.Document,
e => FixInCurrentMember(context.Document, e, context.Diagnostics[0], c), c)),
context.Diagnostics);
return Task.CompletedTask;
}
private async Task FixInCurrentMember(
Document document, SyntaxEditor editor,
Diagnostic diagnostic, CancellationToken cancellationToken)
{
// For the standard invocation of the code-fix, we want to fixup all creations of the
// "same" anonymous type within the containing method. We define same-ness as meaning
// "they have the type symbol". This means both have the same member names, in the same
// order, with the same member types. We fix all these up in the method because the
// user may be creating several instances of this anonymous type in that method and
// then combining them in interesting ways (i.e. checking them for equality, using them
// in collections, etc.). The language guarantees within a method boundary that these
// will be the same type and can be used together in this fashion.
var creationNode = TryGetCreationNode(diagnostic, cancellationToken);
if (creationNode == null)
{
Debug.Fail("We should always be able to find the anonymous creation we were invoked from.");
return;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var anonymousType = semanticModel.GetTypeInfo(creationNode, cancellationToken).Type;
if (anonymousType == null)
{
Debug.Fail("We should always be able to get an anonymous type for any anonymous creation node.");
return;
}
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var containingMember = creationNode.FirstAncestorOrSelf<SyntaxNode>(syntaxFacts.IsMethodLevelMember) ?? creationNode;
var childCreationNodes = containingMember.DescendantNodesAndSelf()
.OfType<TAnonymousObjectCreationExpressionSyntax>();
foreach (var childCreation in childCreationNodes)
{
var childType = semanticModel.GetTypeInfo(childCreation, cancellationToken).Type;
if (childType == null)
{
Debug.Fail("We should always be able to get an anonymous type for any anonymous creation node.");
continue;
}
if (anonymousType.Equals(childType))
{
ReplaceWithTuple(editor, childCreation);
}
}
}
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
foreach (var diagnostic in diagnostics)
{
// During a fix-all we don't need to bother with the work to go to the containing
// method. Because it's a fix-all, by definition, we'll always be processing all
// the anon-creation nodes for any given method that is within our scope.
var node = TryGetCreationNode(diagnostic, cancellationToken);
if (node == null)
{
Debug.Fail("We should always be able to find the anonymous creation we were invoked from.");
continue;
}
ReplaceWithTuple(editor, node);
}
return Task.CompletedTask;
}
private void ReplaceWithTuple(SyntaxEditor editor, TAnonymousObjectCreationExpressionSyntax node)
=> editor.ReplaceNode(
node, (current, _) =>
{
// Use the callback form as anonymous types may be nested, and we want to
// properly replace them even in that case.
var anonCreation = current as TAnonymousObjectCreationExpressionSyntax;
if (anonCreation == null)
{
return current;
}
return ConvertToTuple(anonCreation).WithAdditionalAnnotations(Formatter.Annotation);
});
private static TAnonymousObjectCreationExpressionSyntax TryGetCreationNode(Diagnostic diagnostic, CancellationToken cancellationToken)
=> diagnostic.Location.FindToken(cancellationToken).Parent as TAnonymousObjectCreationExpressionSyntax;
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Convert_to_tuple, createChangedDocument, FeaturesResources.Convert_to_tuple)
{
}
}
}
}
// 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 Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple
{
internal abstract class AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer<
TSyntaxKind,
TAnonymousObjectCreationExpressionSyntax>
: AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TAnonymousObjectCreationExpressionSyntax : SyntaxNode
{
protected AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer()
: base(IDEDiagnosticIds.ConvertAnonymousTypeToTupleDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Convert_to_tuple), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Convert_to_tuple), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
protected abstract TSyntaxKind GetAnonymousObjectCreationExpressionSyntaxKind();
protected abstract int GetInitializerCount(TAnonymousObjectCreationExpressionSyntax anonymousType);
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SyntaxAnalysis;
public override bool OpenFileOnly(Workspace workspace)
=> false;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(
AnalyzeSyntax,
GetAnonymousObjectCreationExpressionSyntaxKind());
// Analysis is trivial. All anonymous types with more than two fields are marked as being
// convertible to a tuple.
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
var anonymousType = (TAnonymousObjectCreationExpressionSyntax)context.Node;
if (GetInitializerCount(anonymousType) < 2)
{
return;
}
context.ReportDiagnostic(
DiagnosticHelper.Create(
Descriptor, context.Node.GetFirstToken().GetLocation(), ReportDiagnostic.Hidden,
additionalLocations: null, properties: null));
}
}
}
......@@ -77,6 +77,7 @@ internal static class IDEDiagnosticIds
public const string PreferBuiltInOrFrameworkTypeDiagnosticId = "IDE0049";
public const string ConvertAnonymousTypeToTupleDiagnosticId = "IDE0050";
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
......
......@@ -977,6 +977,15 @@ internal class FeaturesResources {
return ResourceManager.GetString("Convert_to_struct", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Convert to tuple.
/// </summary>
internal static string Convert_to_tuple {
get {
return ResourceManager.GetString("Convert_to_tuple", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not extract interface: The selection is not inside a class/interface/struct..
......
......@@ -1376,6 +1376,9 @@ 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_tuple" xml:space="preserve">
<value>Convert to tuple</value>
</data>
<data name="Convert_to_class" xml:space="preserve">
<value>Convert to class</value>
</data>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
......@@ -37,6 +37,11 @@
<target state="new">Convert to struct</target>
<note />
</trans-unit>
<trans-unit id="Convert_to_tuple">
<source>Convert to tuple</source>
<target state="new">Convert to tuple</target>
<note />
</trans-unit>
<trans-unit id="Fix_typo_0">
<source>Fix typo '{0}'</source>
<target state="new">Fix typo '{0}'</target>
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=NameOf(VisualBasicConvertAnonymousTypeToTupleCodeFixProvider)), [Shared]>
Friend Class VisualBasicConvertAnonymousTypeToTupleCodeFixProvider
Inherits AbstractConvertAnonymousTypeToTupleCodeFixProvider(Of
ExpressionSyntax,
TupleExpressionSyntax,
AnonymousObjectCreationExpressionSyntax)
Protected Overrides Function ConvertToTuple(anonCreation As AnonymousObjectCreationExpressionSyntax) As TupleExpressionSyntax
Return SyntaxFactory.TupleExpression(
SyntaxFactory.Token(SyntaxKind.OpenParenToken).WithTriviaFrom(anonCreation.Initializer.OpenBraceToken),
ConvertInitializers(anonCreation.Initializer.Initializers),
SyntaxFactory.Token(SyntaxKind.CloseParenToken).WithTriviaFrom(anonCreation.Initializer.CloseBraceToken)).
WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia())
End Function
Private Function ConvertInitializers(initializers As SeparatedSyntaxList(Of FieldInitializerSyntax)) As SeparatedSyntaxList(Of SimpleArgumentSyntax)
Return SyntaxFactory.SeparatedList(initializers.Select(AddressOf ConvertInitializer), initializers.GetSeparators())
End Function
Private Function ConvertInitializer(field As FieldInitializerSyntax) As SimpleArgumentSyntax
Return SyntaxFactory.SimpleArgument(
GetNameEquals(field),
GetExpression(field)).WithTriviaFrom(field)
End Function
Private Function GetNameEquals(field As FieldInitializerSyntax) As NameColonEqualsSyntax
Dim namedField = TryCast(field, NamedFieldInitializerSyntax)
If namedField Is Nothing Then
Return Nothing
End If
Return SyntaxFactory.NameColonEquals(
namedField.Name,
SyntaxFactory.Token(SyntaxKind.ColonEqualsToken).WithTriviaFrom(namedField.EqualsToken))
End Function
Private Function GetExpression(field As FieldInitializerSyntax) As ExpressionSyntax
Return If(TryCast(field, InferredFieldInitializerSyntax)?.Expression,
TryCast(field, NamedFieldInitializerSyntax)?.Expression)
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer
Inherits AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer(Of
SyntaxKind, AnonymousObjectCreationExpressionSyntax)
Protected Overrides Function GetAnonymousObjectCreationExpressionSyntaxKind() As SyntaxKind
Return SyntaxKind.AnonymousObjectCreationExpression
End Function
Protected Overrides Function GetInitializerCount(anonymousType As AnonymousObjectCreationExpressionSyntax) As Integer
Return anonymousType.Initializer.Initializers.Count
End Function
End Class
End Namespace
......@@ -49,6 +49,7 @@ public static class Features
public const string CodeActionsChangeToIEnumerable = "CodeActions.ChangeToIEnumerable";
public const string CodeActionsChangeToYield = "CodeActions.ChangeToYield";
public const string CodeActionsConvertAnonymousTypeToClass = "CodeActions.ConvertAnonymousTypeToClass";
public const string CodeActionsConvertAnonymousTypeToTuple = "CodeActions.ConvertAnonymousTypeToTuple";
public const string CodeActionsConvertNumericLiteral = "CodeActions.ConvertNumericLiteral";
public const string CodeActionsConvertToInterpolatedString = "CodeActions.ConvertToInterpolatedString";
public const string CodeActionsConvertToIterator = "CodeActions.ConvertToIterator";
......
......@@ -79,13 +79,11 @@
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\..\Compilers\Core\Portable\CodeAnalysis.csproj" />
<ProjectReference Include="..\..\..\Workspaces\Core\Desktop\Workspaces.Desktop.csproj" />
<ProjectReference Include="..\..\..\Workspaces\CSharp\Portable\CSharpWorkspace.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Core\EditorFeatures.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Core.Wpf\EditorFeatures.Wpf.csproj" />
<ProjectReference Include="..\..\..\Workspaces\Core\Portable\Workspaces.csproj" />
<ProjectReference Include="..\..\..\Features\Core\Portable\Features.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Text\TextEditorFeatures.csproj" />
<ProjectReference Include="..\..\..\Workspaces\VisualBasic\Portable\BasicWorkspace.vbproj" />
</ItemGroup>
<ItemGroup Label="File References">
<Reference Include="System.ComponentModel.Composition" />
......
......@@ -4,12 +4,10 @@
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\..\Compilers\Core\Portable\CodeAnalysis.csproj" />
<ProjectReference Include="..\..\..\Workspaces\Core\Desktop\Workspaces.Desktop.csproj" />
<ProjectReference Include="..\..\..\Workspaces\CSharp\Portable\CSharpWorkspace.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Core\EditorFeatures.csproj" />
<ProjectReference Include="..\..\..\Workspaces\Core\Portable\Workspaces.csproj" />
<ProjectReference Include="..\..\..\Features\Core\Portable\Features.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Text\TextEditorFeatures.csproj" />
<ProjectReference Include="..\..\..\Workspaces\VisualBasic\Portable\BasicWorkspace.vbproj" />
<ProjectReference Include="..\Def\ServicesVisualStudio.csproj" />
</ItemGroup>
<PropertyGroup>
......
......@@ -14,6 +14,7 @@ Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.VisualStudio.Commanding
Imports Microsoft.VisualStudio.Composition
Imports Microsoft.VisualStudio.Editor
Imports Microsoft.VisualStudio.Language.Intellisense
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Snippets
Imports Microsoft.VisualStudio.Shell
Imports Microsoft.VisualStudio.Text
......@@ -29,7 +30,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets
Inherits AbstractCommandHandlerTestState
Public Sub New(workspaceElement As XElement, languageName As String, startActiveSession As Boolean, extraParts As IEnumerable(Of Type), Optional workspaceKind As String = Nothing)
MyBase.New(workspaceElement, extraParts:=CreatePartCatalog(extraParts), workspaceKind:=workspaceKind)
' Remove the default completion presenters to prevent them from conflicting with the test one
' that we are adding.
MyBase.New(workspaceElement, extraParts:=CreatePartCatalog(extraParts), workspaceKind:=workspaceKind, excludedTypes:={GetType(IIntelliSensePresenter(Of ICompletionPresenterSession, ICompletionSession))})
Workspace.Options = Workspace.Options.WithChangedOption(InternalFeatureOnOffOptions.Snippets, True)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册