-
Notifications
You must be signed in to change notification settings - Fork 345
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Dapr.Pubsub.Analyzers and related tests
- Introduced new projects for `Dapr.Pubsub.Analyzers` and `Dapr.Pubsub.Analyzers.Test` in the solution. - Updated solution configuration to include new analyzers for Debug and Release builds. - Added analyzer rule DAPR2001 to enforce calling `MapSubscribeHandler` for Dapr subscriptions. - Implemented `MapSubscribeHandlerCodeFixProvider` for automatic code fixes. - Developed `SubscriptionAnalyzer` to validate subscription method usage. - Added unit tests for the new analyzer and code fix provider. - Updated test utilities and configurations to support new functionality. Signed-off-by: Nils Gruson <[email protected]>
- Loading branch information
Showing
13 changed files
with
720 additions
and
38 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## Release 1.16 | ||
|
||
### New Rules | ||
|
||
Rule ID | Category | Severity | Notes | ||
--------|----------|----------|-------------------- | ||
DAPR2001| Usage | Warning | Call MapSubscribeHandler |
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,2 @@ | ||
; Unshipped analyzer release | ||
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md |
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,38 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<TargetFrameworks></TargetFrameworks> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup> | ||
<!-- Suppress false-positive error NU5128 when packing analyzers with no lib/ref files. --> | ||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> | ||
|
||
<!-- Suppress generation of symbol package (.snupkg). --> | ||
<IncludeSymbols>false</IncludeSymbols> | ||
|
||
<!-- Do not include the generator as a lib dependency --> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
|
||
<!-- Additional NuGet package properties. --> | ||
<Description>This package contains Roslyn analyzers for actors.</Description> | ||
<PackageTags>$(PackageTags)</PackageTags> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<InternalsVisibleTo Include="$(AssemblyName).Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2" /> | ||
</ItemGroup> | ||
|
||
</Project> |
114 changes: 114 additions & 0 deletions
114
src/Dapr.Pubsub.Analyzers/MapSubscribeHandlerCodeFixProvider.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,114 @@ | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace Dapr.Pubsub.Analyzers; | ||
|
||
/// <summary> | ||
/// Provides a code fix for the DAPR2001 diagnostic. | ||
/// </summary> | ||
public class MapSubscribeHandlerCodeFixProvider : CodeFixProvider | ||
{ | ||
/// <summary> | ||
/// Gets the diagnostic IDs that this provider can fix. | ||
/// </summary> | ||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create("DAPR2001"); | ||
|
||
/// <summary> | ||
/// Gets the FixAllProvider for this code fix provider. | ||
/// </summary> | ||
/// <returns>The FixAllProvider.</returns> | ||
public override FixAllProvider? GetFixAllProvider() | ||
{ | ||
return WellKnownFixAllProviders.BatchFixer; | ||
} | ||
|
||
/// <summary> | ||
/// Registers code fixes for the specified diagnostics. | ||
/// </summary> | ||
/// <param name="context">A <see cref="CodeFixContext"/> containing the context in which the code fix is being applied.</param> | ||
public override Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var title = "Call MapSubscribeHandler"; | ||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
title, | ||
createChangedDocument: c => AddMapSubscribeHandlerAsync(context.Document, context.Diagnostics.First(), c), | ||
equivalenceKey: title), | ||
context.Diagnostics); | ||
return Task.CompletedTask; | ||
} | ||
|
||
private async Task<Document> AddMapSubscribeHandlerAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) | ||
{ | ||
var root = await document.GetSyntaxRootAsync(cancellationToken); | ||
var invocationExpressions = root!.DescendantNodes().OfType<InvocationExpressionSyntax>(); | ||
|
||
var createBuilderInvocation = invocationExpressions | ||
.FirstOrDefault(invocation => | ||
{ | ||
return invocation.Expression is MemberAccessExpressionSyntax memberAccess && | ||
memberAccess.Name.Identifier.Text == "CreateBuilder" && | ||
memberAccess.Expression is IdentifierNameSyntax identifier && | ||
identifier.Identifier.Text == "WebApplication"; | ||
}); | ||
|
||
var variableDeclarator = createBuilderInvocation | ||
.AncestorsAndSelf() | ||
.OfType<VariableDeclaratorSyntax>() | ||
.FirstOrDefault(); | ||
|
||
var variableName = variableDeclarator.Identifier.Text; | ||
|
||
var buildInvocation = invocationExpressions | ||
.FirstOrDefault(invocation => | ||
{ | ||
return invocation.Expression is MemberAccessExpressionSyntax memberAccess && | ||
memberAccess.Name.Identifier.Text == "Build" && | ||
memberAccess.Expression is IdentifierNameSyntax identifier && | ||
identifier.Identifier.Text == variableName; | ||
}); | ||
|
||
var buildVariableDeclarator = buildInvocation | ||
.AncestorsAndSelf() | ||
.OfType<VariableDeclaratorSyntax>() | ||
.FirstOrDefault(); | ||
|
||
var buildVariableName = buildVariableDeclarator.Identifier.Text; | ||
|
||
var mapSubscribeHandlerInvocation = SyntaxFactory.ExpressionStatement( | ||
SyntaxFactory.InvocationExpression( | ||
SyntaxFactory.MemberAccessExpression( | ||
SyntaxKind.SimpleMemberAccessExpression, | ||
SyntaxFactory.IdentifierName(buildVariableName), | ||
SyntaxFactory.IdentifierName("MapSubscribeHandler")))); | ||
|
||
if (buildInvocation?.Ancestors().OfType<MethodDeclarationSyntax>().FirstOrDefault() is SyntaxNode parentBlock) | ||
{ | ||
var localDeclaration = buildInvocation | ||
.AncestorsAndSelf() | ||
.OfType<LocalDeclarationStatementSyntax>() | ||
.FirstOrDefault(); | ||
|
||
var newParentBlock = parentBlock.InsertNodesAfter(localDeclaration, new[] { mapSubscribeHandlerInvocation }); | ||
root = root.ReplaceNode(parentBlock, newParentBlock); | ||
} | ||
else | ||
{ | ||
var buildInvocationGlobalStatement = buildInvocation? | ||
.AncestorsAndSelf() | ||
.OfType<GlobalStatementSyntax>() | ||
.FirstOrDefault(); | ||
|
||
var compilationUnitSyntax = createBuilderInvocation.Ancestors().OfType<CompilationUnitSyntax>().FirstOrDefault(); | ||
var newCompilationUnitSyntax = compilationUnitSyntax.InsertNodesAfter(buildInvocationGlobalStatement!, | ||
new[] { SyntaxFactory.GlobalStatement(mapSubscribeHandlerInvocation) }); | ||
root = root.ReplaceNode(compilationUnitSyntax, newCompilationUnitSyntax); | ||
} | ||
|
||
return document.WithSyntaxRoot(root); | ||
} | ||
} |
Oops, something went wrong.