diff --git a/src/EditorFeatures/CSharpTest/ConvertAnonymousTypeToTuple/ConvertAnonymousTypeToTupleTests.cs b/src/EditorFeatures/CSharpTest/ConvertAnonymousTypeToTuple/ConvertAnonymousTypeToTupleTests.cs index 99b77d742f091ff1e0e2beb59521ce7bf8b0b992..87030a7a80428b8b00e09c9aab3dbfe329069b49 100644 --- a/src/EditorFeatures/CSharpTest/ConvertAnonymousTypeToTuple/ConvertAnonymousTypeToTupleTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertAnonymousTypeToTuple/ConvertAnonymousTypeToTupleTests.cs @@ -46,6 +46,34 @@ void Method() 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() { @@ -160,6 +188,74 @@ void Method(int b) 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 TestFixAllAcrossMehtods() + { + 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); } diff --git a/src/Features/CSharp/Portable/ConvertAnonymousTypeToTuple/CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertAnonymousTypeToTuple/CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs index fa1ccc6940b9ced96e215bb16dabad8aeebca78e..7b6c870f15197b675f58d418ddb96b97733936e1 100644 --- a/src/Features/CSharp/Portable/ConvertAnonymousTypeToTuple/CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertAnonymousTypeToTuple/CSharpConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs @@ -1,15 +1,21 @@ // 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 + : AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer< + SyntaxKind, + AnonymousObjectCreationExpressionSyntax> { protected override SyntaxKind GetAnonymousObjectCreationExpressionSyntaxKind() => SyntaxKind.AnonymousObjectCreationExpression; + + protected override int GetInitializerCount(AnonymousObjectCreationExpressionSyntax anonymousType) + => anonymousType.Initializers.Count; } } diff --git a/src/Features/Core/Portable/ConvertAnonymousTypeToTuple/AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs b/src/Features/Core/Portable/ConvertAnonymousTypeToTuple/AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs index 3cc4c1566390a85d4dfccceabcf7f72080bbac14..72c876946b4d46c2ade9ddbddd132942b1df44f2 100644 --- a/src/Features/Core/Portable/ConvertAnonymousTypeToTuple/AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/ConvertAnonymousTypeToTuple/AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer.cs @@ -6,9 +6,12 @@ namespace Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple { - internal abstract class AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer + internal abstract class AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer< + TSyntaxKind, + TAnonymousObjectCreationExpressionSyntax> : AbstractCodeStyleDiagnosticAnalyzer where TSyntaxKind : struct + where TAnonymousObjectCreationExpressionSyntax : SyntaxNode { protected AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer() : base(IDEDiagnosticIds.ConvertAnonymousTypeToTupleDiagnosticId, @@ -18,6 +21,7 @@ protected AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer() } protected abstract TSyntaxKind GetAnonymousObjectCreationExpressionSyntaxKind(); + protected abstract int GetInitializerCount(TAnonymousObjectCreationExpressionSyntax anonymousType); public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -30,11 +34,20 @@ protected override void InitializeWorker(AnalysisContext context) AnalyzeSyntax, GetAnonymousObjectCreationExpressionSyntaxKind()); - // Analysis is trivial. All anonymous types are marked as being convertible to a tuple. + // Analysis is trivial. All anonymous types with more than two fields are marked as being + // convertible to a tuple. private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) - => context.ReportDiagnostic( + { + 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)); + } } } diff --git a/src/Features/VisualBasic/Portable/ConvertAnonymousTypeToTuple/VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer.vb b/src/Features/VisualBasic/Portable/ConvertAnonymousTypeToTuple/VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer.vb index f623cf51a1274e2020223cd3cf79781b105be447..560eea8a5fa428b72df0a570ffd83952729e2582 100644 --- a/src/Features/VisualBasic/Portable/ConvertAnonymousTypeToTuple/VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/ConvertAnonymousTypeToTuple/VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer.vb @@ -2,14 +2,20 @@ Imports Microsoft.CodeAnalysis.ConvertAnonymousTypeToTuple Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple Friend Class VisualBasicConvertAnonymousTypeToTupleDiagnosticAnalyzer - Inherits AbstractConvertAnonymousTypeToTupleDiagnosticAnalyzer(Of SyntaxKind) + 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