未验证 提交 27c3412d 编写于 作者: J Joey Robichaud 提交者: GitHub

Merge pull request #40232 from CyrusNajmabadi/populateSwitch

Update 'Populate switch' to support switch-expressions.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.PopulateSwitch
{
public partial class PopulateSwitchExpressionTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInDocument()
{
var input = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum.Fizz;
_ = e {|FixAllInDocument:|}switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
class MyClass2
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
class MyClass3
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
class MyClass2
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
class MyClass3
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
</Workspace>";
await TestInRegularAndScriptAsync(input, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInProject()
{
var input = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum.Fizz;
_ = e {|FixAllInProject:|}switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
class MyClass2
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
class MyClass3
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
class MyClass2
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
class MyClass3
{
void Method()
{
var e = MyEnum.Fizz;
_ = e switch
{
MyEnum.Fizz => 1,
MyEnum.Buzz => 2,
MyEnum.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
</Workspace>";
await TestInRegularAndScriptAsync(input, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInSolution()
{
var input = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum1
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum1.Fizz;
_ = e {|FixAllInSolution:|}switch
{
MyEnum1.Fizz => 1,
MyEnum1.Buzz => 2,
MyEnum1.FizzBuzz => 3,
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
enum MyEnum2
{
Fizz, Buzz, FizzBuzz
}
class MyClass2
{
void Method()
{
var e = MyEnum2.Fizz;
_ = e switch
{
MyEnum2.Fizz => 1,
MyEnum2.Buzz => 2,
MyEnum2.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication2
{
enum MyEnum3
{
Fizz, Buzz, FizzBuzz
}
class MyClass3
{
void Method()
{
var e = MyEnum3.Fizz;
_ = e switch
{
MyEnum3.Fizz => 1,
MyEnum3.Buzz => 2,
MyEnum3.FizzBuzz => 3,
};
}
}
}
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication1
{
enum MyEnum1
{
Fizz, Buzz, FizzBuzz
}
class MyClass1
{
void Method()
{
var e = MyEnum1.Fizz;
_ = e switch
{
MyEnum1.Fizz => 1,
MyEnum1.Buzz => 2,
MyEnum1.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
<Document>
namespace ConsoleApplication1
{
enum MyEnum2
{
Fizz, Buzz, FizzBuzz
}
class MyClass2
{
void Method()
{
var e = MyEnum2.Fizz;
_ = e switch
{
MyEnum2.Fizz => 1,
MyEnum2.Buzz => 2,
MyEnum2.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
namespace ConsoleApplication2
{
enum MyEnum3
{
Fizz, Buzz, FizzBuzz
}
class MyClass3
{
void Method()
{
var e = MyEnum3.Fizz;
_ = e switch
{
MyEnum3.Fizz => 1,
MyEnum3.Buzz => 2,
MyEnum3.FizzBuzz => 3,
_ => throw new System.NotImplementedException(),
};
}
}
}
</Document>
</Project>
</Workspace>";
await TestInRegularAndScriptAsync(input, expected);
}
}
}
......@@ -2,18 +2,18 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.PopulateSwitch;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.PopulateSwitch;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.PopulateSwitch
{
public partial class PopulateSwitchTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
public partial class PopulateSwitchStatementTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new PopulateSwitchDiagnosticAnalyzer(), new PopulateSwitchCodeFixProvider());
=> (new CSharpPopulateSwitchStatementDiagnosticAnalyzer(), new CSharpPopulateSwitchStatementCodeFixProvider());
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)]
public async Task OnlyOnFirstToken()
......
......@@ -6,7 +6,7 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.PopulateSwitch
{
public partial class PopulateSwitchTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
public partial class PopulateSwitchStatementTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)]
......
......@@ -353,6 +353,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable()
# IDE0070
dotnet_diagnostic.IDE0070.severity = %value%
# IDE0072
dotnet_diagnostic.IDE0072.severity = %value%
# IDE1005
csharp_style_conditional_delegate_call = true:suggestion
......@@ -832,6 +835,9 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable()
# IDE0070
No editorconfig based code style option
# IDE0072
No editorconfig based code style option
# IDE1005, PreferConditionalDelegateCall
csharp_style_conditional_delegate_call = true:suggestion
......
......@@ -2,14 +2,14 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.PopulateSwitch
Imports Microsoft.CodeAnalysis.VisualBasic.PopulateSwitch
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.PopulateSwitch
Partial Public Class PopulateSwitchTests
Partial Public Class PopulateSwitchStatementTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (New PopulateSwitchDiagnosticAnalyzer(), New PopulateSwitchCodeFixProvider())
Return (New VisualBasicPopulateSwitchStatementDiagnosticAnalyzer(), New VisualBasicPopulateSwitchStatementCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
......
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.PopulateSwitch
Partial Public Class PopulateSwitchTests
Partial Public Class PopulateSwitchStatementTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
<Fact>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.PopulateSwitch;
namespace Microsoft.CodeAnalysis.CSharp.PopulateSwitch
{
using static SyntaxFactory;
[ExportCodeFixProvider(LanguageNames.CSharp,
Name = PredefinedCodeFixProviderNames.PopulateSwitch), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal class CSharpPopulateSwitchExpressionCodeFixProvider
: AbstractPopulateSwitchExpressionCodeFixProvider<
ExpressionSyntax,
SwitchExpressionSyntax,
SwitchExpressionArmSyntax,
MemberAccessExpressionSyntax>
{
protected override SwitchExpressionArmSyntax CreateDefaultSwitchArm(SyntaxGenerator generator, Compilation compilation)
=> SwitchExpressionArm(DiscardPattern(), Exception(generator, compilation));
protected override SwitchExpressionArmSyntax CreateSwitchArm(SyntaxGenerator generator, Compilation compilation, MemberAccessExpressionSyntax caseLabel)
=> SwitchExpressionArm(ConstantPattern(caseLabel), Exception(generator, compilation));
protected override SwitchExpressionSyntax InsertSwitchArms(SyntaxGenerator generator, SwitchExpressionSyntax switchNode, int insertLocation, List<SwitchExpressionArmSyntax> newArms)
{
// If the existing switch expression ends with a comma, then ensure that we preserve
// that. Also do this for an empty switch statement.
if (switchNode.Arms.Count == 0 ||
!switchNode.Arms.GetWithSeparators().LastOrDefault().IsNode)
{
return switchNode.WithArms(switchNode.Arms.InsertRangeWithTrailingSeparator(
insertLocation, newArms, SyntaxKind.CommaToken));
}
return switchNode.WithArms(switchNode.Arms.InsertRange(insertLocation, newArms));
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Composition;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.PopulateSwitch;
namespace Microsoft.CodeAnalysis.CSharp.PopulateSwitch
{
[DiagnosticAnalyzer(LanguageNames.CSharp), Shared]
internal sealed class CSharpPopulateSwitchExpressionDiagnosticAnalyzer :
AbstractPopulateSwitchExpressionDiagnosticAnalyzer<SwitchExpressionSyntax>
{
protected override Location GetDiagnosticLocation(SwitchExpressionSyntax switchBlock)
=> switchBlock.SwitchKeyword.GetLocation();
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PopulateSwitch;
namespace Microsoft.CodeAnalysis.CSharp.PopulateSwitch
{
[ExportCodeFixProvider(LanguageNames.CSharp,
Name = PredefinedCodeFixProviderNames.PopulateSwitch), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal class CSharpPopulateSwitchStatementCodeFixProvider : AbstractPopulateSwitchStatementCodeFixProvider<
SwitchStatementSyntax, SwitchSectionSyntax, MemberAccessExpressionSyntax>
{
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.PopulateSwitch;
namespace Microsoft.CodeAnalysis.CSharp.PopulateSwitch
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpPopulateSwitchStatementDiagnosticAnalyzer :
AbstractPopulateSwitchStatementDiagnosticAnalyzer<SwitchStatementSyntax>
{
}
}
......@@ -15,7 +15,7 @@ internal static class IDEDiagnosticIds
public const string UseImplicitTypeDiagnosticId = "IDE0007";
public const string UseExplicitTypeDiagnosticId = "IDE0008";
public const string AddQualificationDiagnosticId = "IDE0009";
public const string PopulateSwitchDiagnosticId = "IDE0010";
public const string PopulateSwitchStatementDiagnosticId = "IDE0010";
public const string AddBracesDiagnosticId = "IDE0011";
// IDE0012-IDE0015 deprecated and replaced with PreferBuiltInOrFrameworkTypeDiagnosticId (IDE0049)
......@@ -115,6 +115,8 @@ internal static class IDEDiagnosticIds
public const string UseSystemHashCode = "IDE0070";
public const string PopulateSwitchExpressionDiagnosticId = "IDE0072";
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
public const string AnalyzerDependencyConflictId = "IDE1002";
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
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.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic,
Name = PredefinedCodeFixProviderNames.PopulateSwitch), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal class PopulateSwitchCodeFixProvider : SyntaxEditorBasedCodeFixProvider
internal abstract class AbstractPopulateSwitchCodeFixProvider<
TSwitchOperation,
TSwitchSyntax,
TSwitchArmSyntax,
TMemberAccessExpression>
: SyntaxEditorBasedCodeFixProvider
where TSwitchOperation : IOperation
where TSwitchSyntax : SyntaxNode
where TSwitchArmSyntax : SyntaxNode
where TMemberAccessExpression : SyntaxNode
{
[ImportingConstructor]
public PopulateSwitchCodeFixProvider()
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; }
protected AbstractPopulateSwitchCodeFixProvider(string diagnosticId)
{
FixableDiagnosticIds = ImmutableArray.Create(diagnosticId);
}
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.PopulateSwitchDiagnosticId);
protected abstract ITypeSymbol GetSwitchType(TSwitchOperation switchStatement);
protected abstract ICollection<ISymbol> GetMissingEnumMembers(TSwitchOperation switchOperation);
protected abstract TSwitchArmSyntax CreateSwitchArm(SyntaxGenerator generator, Compilation compilation, TMemberAccessExpression caseLabel);
protected abstract TSwitchArmSyntax CreateDefaultSwitchArm(SyntaxGenerator generator, Compilation compilation);
protected abstract int InsertPosition(TSwitchOperation switchOperation);
protected abstract TSwitchSyntax InsertSwitchArms(SyntaxGenerator generator, TSwitchSyntax switchNode, int insertLocation, List<TSwitchArmSyntax> newArms);
protected abstract void FixOneDiagnostic(
Document document, SyntaxEditor editor, SemanticModel semanticModel,
bool addCases, bool addDefaultCase, bool onlyOneDiagnostic,
bool hasMissingCases, bool hasMissingDefaultCase,
TSwitchSyntax switchNode, TSwitchOperation switchOperation);
internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.Custom;
......@@ -40,8 +58,8 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var properties = diagnostic.Properties;
var missingCases = bool.Parse(properties[PopulateSwitchHelpers.MissingCases]);
var missingDefaultCase = bool.Parse(properties[PopulateSwitchHelpers.MissingDefaultCase]);
var missingCases = bool.Parse(properties[PopulateSwitchStatementHelpers.MissingCases]);
var missingDefaultCase = bool.Parse(properties[PopulateSwitchStatementHelpers.MissingDefaultCase]);
Debug.Assert(missingCases || missingDefaultCase);
......@@ -106,89 +124,79 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
bool addCases, bool addDefaultCase,
CancellationToken cancellationToken)
{
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
foreach (var diagnostic in diagnostics)
{
FixOneDiagnostic(
document, editor, model, diagnostic,
addCases, addDefaultCase,
diagnostics.Length == 1,
cancellationToken);
await FixOneDiagnostic(
document, editor, diagnostic, addCases, addDefaultCase,
diagnostics.Length == 1, cancellationToken).ConfigureAwait(false);
}
}
private void FixOneDiagnostic(
Document document, SyntaxEditor editor,
SemanticModel model, Diagnostic diagnostic,
bool addCases, bool addDefaultCase,
bool onlyOneDiagnostic,
private async Task FixOneDiagnostic(
Document document, SyntaxEditor editor, Diagnostic diagnostic,
bool addCases, bool addDefaultCase, bool onlyOneDiagnostic,
CancellationToken cancellationToken)
{
var hasMissingCases = bool.Parse(diagnostic.Properties[PopulateSwitchHelpers.MissingCases]);
var hasMissingDefaultCase = bool.Parse(diagnostic.Properties[PopulateSwitchHelpers.MissingDefaultCase]);
var hasMissingCases = bool.Parse(diagnostic.Properties[PopulateSwitchStatementHelpers.MissingCases]);
var hasMissingDefaultCase = bool.Parse(diagnostic.Properties[PopulateSwitchStatementHelpers.MissingDefaultCase]);
var switchLocation = diagnostic.AdditionalLocations[0];
var switchNode = switchLocation.FindNode(cancellationToken);
var switchStatement = (ISwitchOperation)model.GetOperation(switchNode, cancellationToken);
var enumType = switchStatement.Value.Type;
var switchNode = switchLocation.FindNode(getInnermostNodeForTie: true, cancellationToken) as TSwitchSyntax;
if (switchNode == null)
return;
var generator = editor.Generator;
var model = await document.RequireSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var switchStatement = (TSwitchOperation)model.GetOperation(switchNode, cancellationToken);
var sectionStatements = new[] { generator.ExitSwitchStatement() };
FixOneDiagnostic(
document, editor, model, addCases, addDefaultCase, onlyOneDiagnostic,
hasMissingCases, hasMissingDefaultCase, switchNode, switchStatement);
}
protected TSwitchSyntax UpdateSwitchNode(
SyntaxEditor editor, SemanticModel semanticModel,
bool addCases, bool addDefaultCase,
bool hasMissingCases, bool hasMissingDefaultCase,
TSwitchSyntax switchNode, TSwitchOperation switchOperation)
{
var enumType = GetSwitchType(switchOperation);
var generator = editor.Generator;
var newSections = new List<SyntaxNode>();
var newArms = new List<TSwitchArmSyntax>();
if (hasMissingCases && addCases)
{
var missingEnumMembers = PopulateSwitchHelpers.GetMissingEnumMembers(switchStatement);
var missingSections =
from e in missingEnumMembers
let caseLabel = generator.MemberAccessExpression(generator.TypeExpression(enumType), e.Name).WithAdditionalAnnotations(Simplifier.Annotation)
let section = generator.SwitchSection(caseLabel, sectionStatements)
select section;
newSections.AddRange(missingSections);
var missingArms =
from e in GetMissingEnumMembers(switchOperation)
let caseLabel = (TMemberAccessExpression)generator.MemberAccessExpression(generator.TypeExpression(enumType), e.Name).WithAdditionalAnnotations(Simplifier.Annotation)
select CreateSwitchArm(generator, semanticModel.Compilation, caseLabel);
newArms.AddRange(missingArms);
}
if (hasMissingDefaultCase && addDefaultCase)
{
// Always add the default clause at the end.
newSections.Add(generator.DefaultSwitchSection(sectionStatements));
newArms.Add(CreateDefaultSwitchArm(generator, semanticModel.Compilation));
}
var insertLocation = InsertPosition(switchStatement);
var insertLocation = InsertPosition(switchOperation);
var newSwitchNode = generator.InsertSwitchSections(switchNode, insertLocation, newSections)
var newSwitchNode = InsertSwitchArms(generator, switchNode, insertLocation, newArms)
.WithAdditionalAnnotations(Formatter.Annotation);
if (onlyOneDiagnostic)
{
// If we're only fixing up one issue in this document, then also make sure we
// didn't cause any braces to be imbalanced when we added members to the switch.
// Note: i'm only doing this for the single case because it feels too complex
// to try to support this during fix-all.
var root = editor.OriginalRoot;
AddMissingBraces(document, ref root, ref switchNode);
var newRoot = root.ReplaceNode(switchNode, newSwitchNode);
editor.ReplaceNode(editor.OriginalRoot, newRoot);
}
else
{
editor.ReplaceNode(switchNode, newSwitchNode);
}
return newSwitchNode;
}
private void AddMissingBraces(
protected static void AddMissingBraces(
Document document,
ref SyntaxNode root,
ref SyntaxNode switchNode)
ref TSwitchSyntax switchNode)
{
// Parsing of the switch may have caused imbalanced braces. i.e. the switch
// may have consumed a brace that was intended for a higher level construct.
// So balance the tree first, then do the switch replacement.
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
syntaxFacts.AddFirstMissingCloseBrace(
root, switchNode, out var newRoot, out var newSwitchNode);
......@@ -196,24 +204,6 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
switchNode = newSwitchNode;
}
private int InsertPosition(ISwitchOperation switchStatement)
{
// If the last section has a default label, then we want to be above that.
// Otherwise, we just get inserted at the end.
var cases = switchStatement.Cases;
if (cases.Length > 0)
{
var lastCase = cases.Last();
if (lastCase.Clauses.Any(c => c.CaseKind == CaseKind.Default))
{
return cases.Length - 1;
}
}
return cases.Length;
}
protected override Task FixAllAsync(
Document document,
ImmutableArray<Diagnostic> diagnostics,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
internal sealed class PopulateSwitchDiagnosticAnalyzer :
internal abstract class AbstractPopulateSwitchDiagnosticAnalyzer<TSwitchOperation, TSwitchSyntax> :
AbstractBuiltInCodeStyleDiagnosticAnalyzer
where TSwitchOperation : IOperation
where TSwitchSyntax : SyntaxNode
{
private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(FeaturesResources.Add_missing_cases), FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(WorkspacesResources.Populate_switch), WorkspacesResources.ResourceManager, typeof(WorkspacesResources));
public PopulateSwitchDiagnosticAnalyzer()
: base(IDEDiagnosticIds.PopulateSwitchDiagnosticId,
protected AbstractPopulateSwitchDiagnosticAnalyzer(string diagnosticId)
: base(diagnosticId,
option: null,
s_localizableTitle, s_localizableMessage)
{
......@@ -25,13 +28,23 @@ public PopulateSwitchDiagnosticAnalyzer()
#region Interface methods
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterOperationAction(AnalyzeOperation, OperationKind.Switch);
protected abstract OperationKind OperationKind { get; }
protected abstract ICollection<ISymbol> GetMissingEnumMembers(TSwitchOperation operation);
protected abstract bool HasDefaultCase(TSwitchOperation operation);
protected abstract Location GetDiagnosticLocation(TSwitchSyntax switchBlock);
public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
protected sealed override void InitializeWorker(AnalysisContext context)
=> context.RegisterOperationAction(AnalyzeOperation, this.OperationKind);
private void AnalyzeOperation(OperationAnalysisContext context)
{
var switchOperation = (ISwitchOperation)context.Operation;
var switchBlock = switchOperation.Syntax;
var switchOperation = (TSwitchOperation)context.Operation;
if (!(switchOperation.Syntax is TSwitchSyntax switchBlock))
return;
var tree = switchBlock.SyntaxTree;
if (SwitchIsIncomplete(switchOperation, out var missingCases, out var missingDefaultCase) &&
......@@ -39,12 +52,12 @@ private void AnalyzeOperation(OperationAnalysisContext context)
{
Debug.Assert(missingCases || missingDefaultCase);
var properties = ImmutableDictionary<string, string>.Empty
.Add(PopulateSwitchHelpers.MissingCases, missingCases.ToString())
.Add(PopulateSwitchHelpers.MissingDefaultCase, missingDefaultCase.ToString());
.Add(PopulateSwitchStatementHelpers.MissingCases, missingCases.ToString())
.Add(PopulateSwitchStatementHelpers.MissingDefaultCase, missingDefaultCase.ToString());
var diagnostic = Diagnostic.Create(
Descriptor,
switchBlock.GetFirstToken().GetLocation(),
GetDiagnosticLocation(switchBlock),
properties: properties,
additionalLocations: new[] { switchBlock.GetLocation() });
context.ReportDiagnostic(diagnostic);
......@@ -54,18 +67,16 @@ private void AnalyzeOperation(OperationAnalysisContext context)
#endregion
private bool SwitchIsIncomplete(
ISwitchOperation switchStatement,
TSwitchOperation operation,
out bool missingCases, out bool missingDefaultCase)
{
var missingEnumMembers = PopulateSwitchHelpers.GetMissingEnumMembers(switchStatement);
var missingEnumMembers = GetMissingEnumMembers(operation);
missingCases = missingEnumMembers.Count > 0;
missingDefaultCase = !PopulateSwitchHelpers.HasDefaultCase(switchStatement);
missingDefaultCase = !HasDefaultCase(operation);
// The switch is incomplete if we're missing any cases or we're missing a default case.
return missingDefaultCase || missingCases;
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal abstract class AbstractPopulateSwitchExpressionCodeFixProvider<
TExpressionSyntax, TSwitchSyntax, TSwitchArmSyntax, TMemberAccessExpressionSyntax>
: AbstractPopulateSwitchCodeFixProvider<
ISwitchExpressionOperation, TSwitchSyntax, TSwitchArmSyntax, TMemberAccessExpressionSyntax>
where TExpressionSyntax : SyntaxNode
where TSwitchSyntax : TExpressionSyntax
where TSwitchArmSyntax : SyntaxNode
where TMemberAccessExpressionSyntax : TExpressionSyntax
{
protected AbstractPopulateSwitchExpressionCodeFixProvider()
: base(IDEDiagnosticIds.PopulateSwitchExpressionDiagnosticId)
{
}
protected sealed override void FixOneDiagnostic(
Document document, SyntaxEditor editor, SemanticModel semanticModel,
bool addCases, bool addDefaultCase, bool onlyOneDiagnostic,
bool hasMissingCases, bool hasMissingDefaultCase,
TSwitchSyntax switchNode, ISwitchExpressionOperation switchExpression)
{
var newSwitchNode = UpdateSwitchNode(
editor, semanticModel, addCases, addDefaultCase,
hasMissingCases, hasMissingDefaultCase,
switchNode, switchExpression).WithAdditionalAnnotations(Formatter.Annotation);
editor.ReplaceNode(switchNode, newSwitchNode);
}
protected sealed override ITypeSymbol GetSwitchType(ISwitchExpressionOperation switchExpression)
=> switchExpression.Value.Type;
protected sealed override ICollection<ISymbol> GetMissingEnumMembers(ISwitchExpressionOperation switchOperation)
=> PopulateSwitchExpressionHelpers.GetMissingEnumMembers(switchOperation);
protected static TExpressionSyntax Exception(SyntaxGenerator generator, Compilation compilation)
=> (TExpressionSyntax)generator.CreateThrowNotImplementedExpression(compilation);
protected sealed override int InsertPosition(ISwitchExpressionOperation switchExpression)
{
// If the last section has a default label, then we want to be above that.
// Otherwise, we just get inserted at the end.
var arms = switchExpression.Arms;
return arms.Length > 0 && PopulateSwitchExpressionHelpers.IsDefault(arms[arms.Length - 1])
? arms.Length - 1
: arms.Length;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal abstract class AbstractPopulateSwitchExpressionDiagnosticAnalyzer<TSwitchSyntax> :
AbstractPopulateSwitchDiagnosticAnalyzer<ISwitchExpressionOperation, TSwitchSyntax>
where TSwitchSyntax : SyntaxNode
{
protected AbstractPopulateSwitchExpressionDiagnosticAnalyzer()
: base(IDEDiagnosticIds.PopulateSwitchExpressionDiagnosticId)
{
}
protected sealed override OperationKind OperationKind => OperationKind.SwitchExpression;
protected sealed override ICollection<ISymbol> GetMissingEnumMembers(ISwitchExpressionOperation operation)
=> PopulateSwitchExpressionHelpers.GetMissingEnumMembers(operation);
protected sealed override bool HasDefaultCase(ISwitchExpressionOperation operation)
=> PopulateSwitchExpressionHelpers.HasDefaultCase(operation);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal abstract class AbstractPopulateSwitchStatementCodeFixProvider<
TSwitchSyntax, TSwitchArmSyntax, TMemberAccessExpression> :
AbstractPopulateSwitchCodeFixProvider<
ISwitchOperation, TSwitchSyntax, TSwitchArmSyntax, TMemberAccessExpression>
where TSwitchSyntax : SyntaxNode
where TSwitchArmSyntax : SyntaxNode
where TMemberAccessExpression : SyntaxNode
{
protected AbstractPopulateSwitchStatementCodeFixProvider()
: base(IDEDiagnosticIds.PopulateSwitchStatementDiagnosticId)
{
}
protected sealed override void FixOneDiagnostic(
Document document, SyntaxEditor editor, SemanticModel semanticModel,
bool addCases, bool addDefaultCase, bool onlyOneDiagnostic,
bool hasMissingCases, bool hasMissingDefaultCase,
TSwitchSyntax switchNode, ISwitchOperation switchOperation)
{
var newSwitchNode = UpdateSwitchNode(
editor, semanticModel, addCases, addDefaultCase,
hasMissingCases, hasMissingDefaultCase,
switchNode, switchOperation).WithAdditionalAnnotations(Formatter.Annotation);
if (onlyOneDiagnostic)
{
// If we're only fixing up one issue in this document, then also make sure we
// didn't cause any braces to be imbalanced when we added members to the switch.
// Note: i'm only doing this for the single case because it feels too complex
// to try to support this during fix-all.
var root = editor.OriginalRoot;
AddMissingBraces(document, ref root, ref switchNode);
var newRoot = root.ReplaceNode(switchNode, newSwitchNode);
editor.ReplaceNode(editor.OriginalRoot, newRoot);
}
else
{
editor.ReplaceNode(switchNode, newSwitchNode);
}
}
protected sealed override ITypeSymbol GetSwitchType(ISwitchOperation switchOperation)
=> switchOperation.Value.Type;
protected sealed override ICollection<ISymbol> GetMissingEnumMembers(ISwitchOperation switchOperation)
=> PopulateSwitchStatementHelpers.GetMissingEnumMembers(switchOperation);
protected sealed override TSwitchSyntax InsertSwitchArms(SyntaxGenerator generator, TSwitchSyntax switchNode, int insertLocation, List<TSwitchArmSyntax> newArms)
=> (TSwitchSyntax)generator.InsertSwitchSections(switchNode, insertLocation, newArms);
protected sealed override TSwitchArmSyntax CreateDefaultSwitchArm(SyntaxGenerator generator, Compilation compilation)
=> (TSwitchArmSyntax)generator.DefaultSwitchSection(new[] { generator.ExitSwitchStatement() });
protected sealed override TSwitchArmSyntax CreateSwitchArm(SyntaxGenerator generator, Compilation compilation, TMemberAccessExpression caseLabel)
=> (TSwitchArmSyntax)generator.SwitchSection(caseLabel, new[] { generator.ExitSwitchStatement() });
protected sealed override int InsertPosition(ISwitchOperation switchStatement)
{
// If the last section has a default label, then we want to be above that.
// Otherwise, we just get inserted at the end.
var cases = switchStatement.Cases;
if (cases.Length > 0)
{
var lastCase = cases.Last();
if (lastCase.Clauses.Any(c => c.CaseKind == CaseKind.Default))
{
return cases.Length - 1;
}
}
return cases.Length;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal abstract class AbstractPopulateSwitchStatementDiagnosticAnalyzer<TSwitchSyntax> :
AbstractPopulateSwitchDiagnosticAnalyzer<ISwitchOperation, TSwitchSyntax>
where TSwitchSyntax : SyntaxNode
{
protected AbstractPopulateSwitchStatementDiagnosticAnalyzer()
: base(IDEDiagnosticIds.PopulateSwitchStatementDiagnosticId)
{
}
protected sealed override OperationKind OperationKind => OperationKind.Switch;
protected sealed override ICollection<ISymbol> GetMissingEnumMembers(ISwitchOperation operation)
=> PopulateSwitchStatementHelpers.GetMissingEnumMembers(operation);
protected sealed override bool HasDefaultCase(ISwitchOperation operation)
=> PopulateSwitchStatementHelpers.HasDefaultCase(operation);
protected sealed override Location GetDiagnosticLocation(TSwitchSyntax switchBlock)
=> switchBlock.GetFirstToken().GetLocation();
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PopulateSwitch;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal static class PopulateSwitchExpressionHelpers
{
public static ICollection<ISymbol> GetMissingEnumMembers(ISwitchExpressionOperation operation)
{
var switchExpression = operation.Value;
var switchExpressionType = switchExpression?.Type;
var enumMembers = new Dictionary<long, ISymbol>();
if (switchExpressionType?.TypeKind == TypeKind.Enum)
{
if (!PopulateSwitchStatementHelpers.TryGetAllEnumMembers(switchExpressionType, enumMembers) ||
!TryRemoveExistingEnumMembers(operation, enumMembers))
{
return SpecializedCollections.EmptyCollection<ISymbol>();
}
}
return enumMembers.Values;
}
private static bool TryRemoveExistingEnumMembers(
ISwitchExpressionOperation operation, Dictionary<long, ISymbol> enumMembers)
{
foreach (var arm in operation.Arms)
{
if (arm.Pattern is IConstantPatternOperation constantPattern)
{
var constantValue = constantPattern.Value.ConstantValue;
if (!constantValue.HasValue)
{
// We had a case which didn't resolve properly.
// Assume the switch is complete.
return false;
}
enumMembers.Remove(IntegerUtilities.ToInt64(constantValue.Value));
}
}
return true;
}
public static bool HasDefaultCase(ISwitchExpressionOperation operation)
=> operation.Arms.Any(a => IsDefault(a));
public static bool IsDefault(ISwitchExpressionArmOperation arm)
{
if (arm.Pattern.Kind == OperationKind.DiscardPattern)
return true;
if (arm.Pattern is IDeclarationPatternOperation declarationPattern)
return declarationPattern.MatchesNull;
return false;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Shared.Utilities;
......@@ -7,7 +9,7 @@
namespace Microsoft.CodeAnalysis.PopulateSwitch
{
internal static class PopulateSwitchHelpers
internal static class PopulateSwitchStatementHelpers
{
public const string MissingCases = nameof(MissingCases);
public const string MissingDefaultCase = nameof(MissingDefaultCase);
......@@ -96,7 +98,7 @@ private static bool TryRemoveExistingEnumMembers(ISwitchOperation switchStatemen
return true;
}
private static bool TryGetAllEnumMembers(
public static bool TryGetAllEnumMembers(
ITypeSymbol enumType,
Dictionary<long, ISymbol> enumValues)
{
......
' 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.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.PopulateSwitch
Namespace Microsoft.CodeAnalysis.VisualBasic.PopulateSwitch
<ExportCodeFixProvider(LanguageNames.VisualBasic,
Name:=PredefinedCodeFixProviderNames.PopulateSwitch), [Shared]>
<ExtensionOrder(After:=PredefinedCodeFixProviderNames.ImplementInterface)>
Friend Class VisualBasicPopulateSwitchStatementCodeFixProvider
Inherits AbstractPopulateSwitchStatementCodeFixProvider(Of
SelectBlockSyntax, CaseBlockSyntax, MemberAccessExpressionSyntax)
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.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.PopulateSwitch
Namespace Microsoft.CodeAnalysis.VisualBasic.PopulateSwitch
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicPopulateSwitchStatementDiagnosticAnalyzer
Inherits AbstractPopulateSwitchStatementDiagnosticAnalyzer(Of SelectBlockSyntax)
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;
#nullable enable
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
internal static class SeparatedSyntaxListExtensions
{
private static Tuple<List<T>, List<SyntaxToken>> GetNodesAndSeparators<T>(this SeparatedSyntaxList<T> separatedList) where T : SyntaxNode
public static SeparatedSyntaxList<T> InsertRangeWithTrailingSeparator<T>(
this SeparatedSyntaxList<T> separatedList, int index, IEnumerable<T> nodes, SyntaxKind separator)
where T : SyntaxNode
{
Debug.Assert(separatedList.Count == separatedList.SeparatorCount ||
separatedList.Count == separatedList.SeparatorCount + 1);
var nodes = new List<T>(separatedList.Count);
var separators = new List<SyntaxToken>(separatedList.SeparatorCount);
for (var i = 0; i < separatedList.Count; i++)
{
nodes.Add(separatedList[i]);
var newList = separatedList.InsertRange(index, nodes);
if (index < separatedList.Count)
return newList;
if (i < separatedList.SeparatorCount)
{
separators.Add(separatedList.GetSeparator(i));
}
}
var nodesAndTokens = newList.GetWithSeparators();
if (!nodesAndTokens.Last().IsNode)
return newList;
return Tuple.Create(nodes, separators);
return SyntaxFactory.SeparatedList<T>(nodesAndTokens.Add(SyntaxFactory.Token(separator)));
}
}
}
......@@ -1434,15 +1434,12 @@ public bool IsTypeDeclaration(SyntaxNode node)
private static readonly SyntaxAnnotation s_annotation = new SyntaxAnnotation();
public void AddFirstMissingCloseBrace(
SyntaxNode root, SyntaxNode contextNode,
out SyntaxNode newRoot, out SyntaxNode newContextNode)
public void AddFirstMissingCloseBrace<TContextNode>(
SyntaxNode root, TContextNode contextNode,
out SyntaxNode newRoot, out TContextNode newContextNode) where TContextNode : SyntaxNode
{
// First, annotate the context node in the tree so that we can find it again
// after we've done all the rewriting.
// var currentRoot = root.ReplaceNode(contextNode, contextNode.WithAdditionalAnnotations(s_annotation));
newRoot = new AddFirstMissingCloseBraceRewriter(contextNode).Visit(root);
newContextNode = newRoot.GetAnnotatedNodes(s_annotation).Single();
newContextNode = (TContextNode)newRoot.GetAnnotatedNodes(s_annotation).Single();
}
public SyntaxNode GetObjectCreationInitializer(SyntaxNode node)
......
......@@ -446,9 +446,9 @@ internal interface ISyntaxFactsService : ILanguageService
// with a missing close brace. If found, the close brace will be added and the
// updates root will be returned. The context node in that new tree will also
// be returned.
void AddFirstMissingCloseBrace(
SyntaxNode root, SyntaxNode contextNode,
out SyntaxNode newRoot, out SyntaxNode newContextNode);
void AddFirstMissingCloseBrace<TContextNode>(
SyntaxNode root, TContextNode contextNode,
out SyntaxNode newRoot, out TContextNode newContextNode) where TContextNode : SyntaxNode;
SyntaxNode GetNextExecutableStatement(SyntaxNode statement);
......
......@@ -5,6 +5,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
......@@ -17,15 +18,24 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions
internal static partial class SyntaxGeneratorExtensions
{
public static SyntaxNode CreateThrowNotImplementedStatement(
this SyntaxGenerator codeDefinitionFactory,
Compilation compilation)
this SyntaxGenerator codeDefinitionFactory, Compilation compilation)
{
return codeDefinitionFactory.ThrowStatement(
codeDefinitionFactory.ObjectCreationExpression(
codeDefinitionFactory.TypeExpression(compilation.NotImplementedExceptionType(), addImport: false),
SpecializedCollections.EmptyList<SyntaxNode>()));
CreateNotImplementedException(codeDefinitionFactory, compilation));
}
public static SyntaxNode CreateThrowNotImplementedExpression(
this SyntaxGenerator codeDefinitionFactory, Compilation compilation)
{
return codeDefinitionFactory.ThrowExpression(
CreateNotImplementedException(codeDefinitionFactory, compilation));
}
private static SyntaxNode CreateNotImplementedException(SyntaxGenerator codeDefinitionFactory, Compilation compilation)
=> codeDefinitionFactory.ObjectCreationExpression(
codeDefinitionFactory.TypeExpression(compilation.NotImplementedExceptionType(), addImport: false),
SpecializedCollections.EmptyList<SyntaxNode>());
public static ImmutableArray<SyntaxNode> CreateThrowNotImplementedStatementBlock(
this SyntaxGenerator codeDefinitionFactory, Compilation compilation)
=> ImmutableArray.Create(CreateThrowNotImplementedStatement(codeDefinitionFactory, compilation));
......
......@@ -1513,7 +1513,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return False
End Function
Public Sub AddFirstMissingCloseBrace(root As SyntaxNode, contextNode As SyntaxNode, ByRef newRoot As SyntaxNode, ByRef newContextNode As SyntaxNode) Implements ISyntaxFactsService.AddFirstMissingCloseBrace
Public Sub AddFirstMissingCloseBrace(Of TContextNode As SyntaxNode)(
root As SyntaxNode, contextNode As TContextNode,
ByRef newRoot As SyntaxNode, ByRef newContextNode As TContextNode) Implements ISyntaxFactsService.AddFirstMissingCloseBrace
' Nothing to be done. VB doesn't have close braces
newRoot = root
newContextNode = contextNode
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册