-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Attribute binding mismatching Roslyn code fixers (#318)
Introduces Roslyn code fixers for the attribute binding mismatching diagnostics. They essentially change the wrong type for the expected type. The code fixers are for `TaskOrchestrationContext`, `TaskEntityDispatcher` and `DurableTaskClient`. Besides those 3 code fixers, I had to do some small refactoring to our test infrastructure to allow the fixers to be tested.
- Loading branch information
1 parent
90bd3fe
commit 85ea79d
Showing
13 changed files
with
237 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
src/Analyzers/Functions/AttributeBinding/DurableClientBindingFixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Functions.AttributeBinding; | ||
|
||
/// <summary> | ||
/// Code fixer for the <see cref="DurableClientBindingAnalyzer"/>. | ||
/// </summary> | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MatchingAttributeBindingFixer))] | ||
[Shared] | ||
public sealed class DurableClientBindingFixer : MatchingAttributeBindingFixer | ||
{ | ||
/// <inheritdoc/> | ||
public override ImmutableArray<string> FixableDiagnosticIds => [DurableClientBindingAnalyzer.DiagnosticId]; | ||
|
||
/// <inheritdoc/> | ||
public override string ExpectedType => "DurableTaskClient"; | ||
} |
23 changes: 23 additions & 0 deletions
23
src/Analyzers/Functions/AttributeBinding/EntityTriggerBindingFixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Functions.AttributeBinding; | ||
|
||
/// <summary> | ||
/// Code fixer for the <see cref="EntityTriggerBindingAnalyzer"/>. | ||
/// </summary> | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MatchingAttributeBindingFixer))] | ||
[Shared] | ||
public sealed class EntityTriggerBindingFixer : MatchingAttributeBindingFixer | ||
{ | ||
/// <inheritdoc/> | ||
public override ImmutableArray<string> FixableDiagnosticIds => [EntityTriggerBindingAnalyzer.DiagnosticId]; | ||
|
||
/// <inheritdoc/> | ||
public override string ExpectedType => "TaskEntityDispatcher"; | ||
} |
73 changes: 73 additions & 0 deletions
73
src/Analyzers/Functions/AttributeBinding/MatchingAttributeBindingFixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System.Globalization; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Functions.AttributeBinding; | ||
|
||
/// <summary> | ||
/// Base class for code fixers that fix the type of a parameter to match the expected type. | ||
/// </summary> | ||
public abstract class MatchingAttributeBindingFixer : CodeFixProvider | ||
{ | ||
/// <summary> | ||
/// Gets the expected type to be used during the code fix. | ||
/// </summary> | ||
public abstract string ExpectedType { get; } | ||
|
||
/// <inheritdoc/> | ||
public override FixAllProvider? GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
/// <inheritdoc/> | ||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken); | ||
if (root == null) | ||
{ | ||
return; | ||
} | ||
|
||
// Find the parameter syntax node that is causing the diagnostic. | ||
if (root.FindNode(context.Span) is not ParameterSyntax parameterSyntax) | ||
{ | ||
return; | ||
} | ||
|
||
TypeSyntax? incorrectTypeSyntax = parameterSyntax.Type; | ||
if (incorrectTypeSyntax == null) | ||
{ | ||
return; | ||
} | ||
|
||
// e.g: "Use 'TaskOrchestrationContext' instead of 'string'" | ||
string title = string.Format( | ||
CultureInfo.InvariantCulture, | ||
Resources.UseInsteadFixerTitle, | ||
this.ExpectedType, | ||
incorrectTypeSyntax.ToString()); | ||
|
||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
title: title, | ||
createChangedDocument: _ => ReplaceMismatchedType(context.Document, root, incorrectTypeSyntax, this.ExpectedType), | ||
equivalenceKey: title), // This key is used to prevent duplicate code fixes. | ||
context.Diagnostics); | ||
} | ||
|
||
static Task<Document> ReplaceMismatchedType(Document document, SyntaxNode oldRoot, TypeSyntax incorrectTypeSyntax, string expectedType) | ||
{ | ||
// Create the correct type syntax by using the identifier name provided by the derived class. | ||
TypeSyntax correctTypeSyntax = SyntaxFactory.IdentifierName(expectedType); | ||
|
||
// Replace the old local declaration with the new local declaration. | ||
SyntaxNode newRoot = oldRoot.ReplaceNode(incorrectTypeSyntax, correctTypeSyntax); | ||
Document newDocument = document.WithSyntaxRoot(newRoot); | ||
|
||
return Task.FromResult(newDocument); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/Analyzers/Functions/AttributeBinding/OrchestrationTriggerBindingFixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Functions.AttributeBinding; | ||
|
||
/// <summary> | ||
/// Code fixer for the <see cref="OrchestrationTriggerBindingAnalyzer"/>. | ||
/// </summary> | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MatchingAttributeBindingFixer))] | ||
[Shared] | ||
public sealed class OrchestrationTriggerBindingFixer : MatchingAttributeBindingFixer | ||
{ | ||
/// <inheritdoc/> | ||
public override ImmutableArray<string> FixableDiagnosticIds => [OrchestrationTriggerBindingAnalyzer.DiagnosticId]; | ||
|
||
/// <inheritdoc/> | ||
public override string ExpectedType => "TaskOrchestrationContext"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
test/Analyzers.Tests/Verifiers/CSharpCodeFixVerifier.Durable.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Testing; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Tests.Verifiers; | ||
|
||
public static partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix> | ||
where TAnalyzer : DiagnosticAnalyzer, new() | ||
where TCodeFix : CodeFixProvider, new() | ||
{ | ||
public static Task VerifyDurableTaskAnalyzerAsync(string source, params DiagnosticResult[] expected) | ||
{ | ||
return VerifyDurableTaskAnalyzerAsync(source, null, expected); | ||
} | ||
|
||
public static async Task VerifyDurableTaskAnalyzerAsync(string source, Action<Test>? configureTest = null, params DiagnosticResult[] expected) | ||
{ | ||
await RunAsync(expected, new Test() | ||
{ | ||
TestCode = source, | ||
}, configureTest); | ||
} | ||
|
||
public static Task VerifyDurableTaskCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) | ||
{ | ||
return VerifyDurableTaskCodeFixAsync(source, [expected], fixedSource); | ||
} | ||
|
||
public static async Task VerifyDurableTaskCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) | ||
{ | ||
await RunAsync(expected, new Test() | ||
{ | ||
TestCode = source, | ||
FixedCode = fixedSource, | ||
}); | ||
} | ||
|
||
static async Task RunAsync(DiagnosticResult[] expected, Test test, Action<Test>? configureTest = null) | ||
{ | ||
test.ReferenceAssemblies = References.CommonAssemblies; | ||
test.ExpectedDiagnostics.AddRange(expected); | ||
|
||
configureTest?.Invoke(test); | ||
|
||
await test.RunAsync(CancellationToken.None); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.CodeAnalysis.Testing; | ||
|
||
namespace Microsoft.DurableTask.Analyzers.Tests.Verifiers; | ||
|
||
public static class References | ||
{ | ||
static readonly Lazy<ReferenceAssemblies> durableAssemblyReferences = new(() => BuildReferenceAssemblies()); | ||
|
||
public static ReferenceAssemblies CommonAssemblies => durableAssemblyReferences.Value; | ||
|
||
static ReferenceAssemblies BuildReferenceAssemblies() => ReferenceAssemblies.Net.Net60.AddPackages([ | ||
new PackageIdentity("Azure.Storage.Blobs", "12.17.0"), | ||
new PackageIdentity("Azure.Storage.Queues", "12.17.0"), | ||
new PackageIdentity("Azure.Data.Tables", "12.8.3"), | ||
new PackageIdentity("Microsoft.Azure.Cosmos", "3.39.1"), | ||
new PackageIdentity("Microsoft.Azure.Functions.Worker", "1.21.0"), | ||
new PackageIdentity("Microsoft.Azure.Functions.Worker.Extensions.DurableTask", "1.1.1"), | ||
new PackageIdentity("Microsoft.Data.SqlClient", "5.2.0"), | ||
]); | ||
} |