-
Notifications
You must be signed in to change notification settings - Fork 293
[Feature] Create analyzer #3981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
iancooper
merged 19 commits into
BrighterCommand:master
from
AboubakrNasef:Create-Analyzer
Feb 19, 2026
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
631f276
init brighter analyzer
AboubakrNasef adf1247
add message pump analyzer
AboubakrNasef dd42796
finalize subscription analyzer
AboubakrNasef 164d252
add analyzer for the wrapper Attributes
AboubakrNasef b099800
wip Rules documentation
AboubakrNasef c3f1d32
enhance csproj
AboubakrNasef b5d42a9
merge master
AboubakrNasef d327b6e
clean some files
AboubakrNasef c75b394
resolve comments
AboubakrNasef e8efc4b
add adr
AboubakrNasef daf0119
resolve comments
AboubakrNasef 19094b8
refactor the function of report attributes
AboubakrNasef 546b732
fix pr comments wip
AboubakrNasef 67db77b
remove resources.resx and packageid
AboubakrNasef e4934de
add WrapAttributeAnalyzer to the adr
AboubakrNasef cfc6150
sync with master
AboubakrNasef 4ea2f16
sync with master
AboubakrNasef 2407910
add missing tests project
AboubakrNasef bfadb27
remove package id
AboubakrNasef File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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,59 @@ | ||
| # 37. Provide Roslyn Analyzers for Brighter | ||
|
|
||
| Date: 2026-01-25 | ||
|
|
||
| ## Status | ||
|
|
||
| Proposed | ||
|
|
||
| ## Context | ||
|
|
||
| Using Brighter correctly often involves adhering to specific patterns and conventions that are not strictly enforced by the C# compiler alone. For example, developers must ensure that: | ||
| - Attributes like `[Wrap]` are used correctly to configure the pipeline. | ||
| - `Publication` objects are initialized with a valid `RequestType` that implements `IRequest` for proper mapping. | ||
| - Subscriptions are configured with valid parameters. | ||
|
|
||
| Currently, violations of these rules often result in runtime errors (e.g., `RuntimeBinderException`, `ArgumentException`) or silent failures. identifying these issues requires running the application, which delays the feedback loop and increases the risk of bugs reaching production. | ||
|
|
||
| We want to "shift left" on these checks, providing feedback to developers as they write code. | ||
|
|
||
| ## Decision | ||
|
|
||
| We will introduce a new project, `Paramore.Brighter.Analyzer`, containing custom Roslyn analyzers. | ||
| This project will be distributed as a NuGet package that developers can include in their projects. | ||
|
|
||
| The analyzers will inspect code at compile-time and report diagnostics (warnings or errors) for known invalid usage patterns of Brighter components. | ||
|
|
||
| The initial implementation will include the following analyzers: | ||
|
|
||
| 1. **PublicationRequestTypeAssignmentAnalyzer**: | ||
| - Ensures that when a `Publication` object is created, the `RequestType` property is explicitly assigned. | ||
| - Verifies that the assigned type implements `IRequest`. | ||
|
|
||
| 2. **SubscriptionConstructorAnalyzer**: | ||
| - Validates that `Subscription` objects are instantiated with correct arguments and configuration. | ||
|
|
||
| 3. **WrapAttributeAnalyzer**: | ||
| - Ensures that attributes used for wrapping (e.g., encryption, compression) are applied to the correct mapping method (`MapToMessage` or `MapToRequest`). | ||
| - Prevents incorrect pipeline configuration by verifying that attributes intended for requests are not applied to message mapping and vice versa. | ||
|
|
||
| ## Technical Implementation Strategy | ||
|
|
||
| The logic for the analyzers will be implemented using the **Roslyn API** and the **Visitor Pattern**. | ||
|
|
||
| 1. **Roslyn Syntax & Operation Trees**: We leverage Roslyn to inspect the code structure. We specifically focus on the **Operation Tree (IOperation)**, which provides a semantic view of the code (e.g., understanding that a line is an object creation regardless of syntax). | ||
| 2. **Visitor Pattern**: To efficiently traverse the code, we implement a `Visitor` (e.g., `RequestTypeAssignmentVisitor`). | ||
| - The **Visitor** "walks" through specific nodes of the code tree (like `ObjectCreation`). | ||
| - We utilize **Double Dispatch** (via the `Accept` method) to ensure the correct `Visit` method is called for each node type. This avoids the need for explicit type casting and massive `if-else` or `switch` statements to determine node types, making the code cleaner and more maintainable. | ||
| - It maintains state as it visits nodes (e.g., "I am currently inside a Publication object creation"). | ||
|
|
||
| ## Consequences | ||
|
|
||
| ### Positive | ||
| - **Immediate Feedback**: Developers receive feedback on incorrect usage within the IDE and at build time. | ||
| - **Reduced Runtime Errors**: Prevents a class of configuration and usage errors from occurring at runtime. | ||
| - **Better Discovery**: Helps new users learn Brighter's constraints and requirements through compiler messages rather than documentation lookup or trial-and-error. | ||
|
|
||
| ### Negative | ||
| - **Maintenance**: The analyzer project must be maintained and updated as Brighter's API and patterns evolve. | ||
|
|
||
This file contains hidden or 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,17 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\..\src\Paramore.Brighter.Analyzer\Paramore.Brighter.Analyzer.csproj" PrivateAssets="all" ReferenceOutputAssembly="true" OutputItemType="Analyzer" SetTargetFramework="TargetFramework=netstandard2.0" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\..\src\Paramore.Brighter\Paramore.Brighter.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or 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,48 @@ | ||
| using Paramore.Brighter; | ||
|
|
||
| namespace AnalyzerSamples | ||
| { | ||
| public static class PublicationCreator | ||
| { | ||
| public static Publication GetPublicationNoRequestType() | ||
| { | ||
| return new PublicationSample() { | ||
| Subject="test" | ||
| }; | ||
| } | ||
| public static Publication GetPublicationWrongRequestType() | ||
| { | ||
| return new PublicationSample() | ||
| { | ||
| Subject = "test", | ||
| RequestType = typeof(EventSample2) | ||
| }; | ||
| } | ||
| public static Publication GetPublicationWithRequestType() | ||
| { | ||
| return new PublicationSample() | ||
| { | ||
| Subject = "test", | ||
| RequestType = typeof(EventSample) | ||
| }; | ||
| } | ||
| public static AnyClass GetAnyOtherClass() | ||
| { | ||
| return new AnyClass(); | ||
| } | ||
| } | ||
| public class PublicationSample : Publication | ||
| { | ||
| } | ||
| public class EventSample(Id id) : Event(id) | ||
| { | ||
| } | ||
| public class EventSample2 | ||
| { | ||
| } | ||
|
|
||
| public class AnyClass | ||
| { | ||
|
|
||
| } | ||
| } |
This file contains hidden or 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,37 @@ | ||
| | ||
|
|
||
| using Paramore.Brighter; | ||
|
|
||
| namespace AnalyzerSamples | ||
| { | ||
| internal class SubscriptionSample | ||
| { | ||
| } | ||
| public static class SubscriptionCreator | ||
| { | ||
| public static Subscription GetSubscription_MessagePumpSpecified() | ||
| { | ||
| return new SubscriptionTest("name", "name", "key", messagePumpType: MessagePumpType.Reactor); | ||
| } | ||
| public static Subscription GetSubscription_NoMessagePumpSpecified() | ||
| { | ||
| return new SubscriptionTest("name", "name", "key"); | ||
| } | ||
| public static Subscription GetSubscriptionNested_NoMessagePumpSpecified() | ||
| { | ||
| return new SubscriptionTestNested("name", "name", "key"); | ||
| } | ||
| public class SubscriptionTest : Subscription | ||
| { | ||
| public SubscriptionTest(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func<Message, Type>? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay) | ||
| { | ||
| } | ||
| } | ||
| public class SubscriptionTestNested : SubscriptionTest | ||
| { | ||
| public SubscriptionTestNested(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func<Message, Type>? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay) | ||
| { | ||
| } | ||
| } | ||
| } | ||
| } |
This file contains hidden or 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 Paramore.Brighter; | ||
| using Paramore.Brighter.Transforms.Attributes; | ||
|
|
||
| namespace AnalyzerSamples | ||
| { | ||
| public class WrapWithMapperAsyncSample: IAmAMessageMapperAsync<SampleEvent> | ||
| { | ||
| public IRequestContext? Context { get; set ; } | ||
|
|
||
| [Compress(0)] | ||
| [ClaimCheck(0)] | ||
| public async Task<SampleEvent> MapToRequestAsync(Message message, CancellationToken cancellationToken = default) | ||
| { | ||
| await Task.Yield(); | ||
| return new SampleEvent(message.Id); | ||
| } | ||
|
|
||
| [Decompress(0)] | ||
| public async Task<Message> MapToMessageAsync(SampleEvent request, Publication publication, CancellationToken cancellationToken = default) | ||
| { | ||
| await Task.Yield(); | ||
| throw new NotImplementedException(); | ||
| } | ||
| } | ||
| public class WrapWithMapperSample: IAmAMessageMapper<SampleEvent> | ||
| { | ||
| public IRequestContext? Context { get; set ; } | ||
|
|
||
| [Compress(0)] | ||
| [ClaimCheck(0)] | ||
| public SampleEvent MapToRequest(Message message) | ||
| { | ||
| return new SampleEvent(message.Id); | ||
| } | ||
|
|
||
| [Decompress(0)] | ||
| public Message MapToMessage(SampleEvent request, Publication publication) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| public class SampleEvent(Id id) : Event(id) | ||
| { | ||
| } | ||
| } |
This file contains hidden or 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
36 changes: 36 additions & 0 deletions
36
src/Paramore.Brighter.Analyzer.Package/Paramore.Brighter.Analyzer.Package.csproj
This file contains hidden or 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,36 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFrameworks>$(BrighterNetStandardTargetFrameworks)</TargetFrameworks> | ||
| <IncludeBuildOutput>false</IncludeBuildOutput> | ||
| <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> | ||
| <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup> | ||
| <Authors>AboubakrNasef</Authors> | ||
| <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> | ||
| <Description>Paramore.Brighter.Analyzer</Description> | ||
| <PackageReleaseNotes>Analyzer For usage of Brighter Command</PackageReleaseNotes> | ||
| <PackageTags>Paramore.Brighter,Paramore.Brighter.Analyzer, analyzers</PackageTags> | ||
| <DevelopmentDependency>true</DevelopmentDependency> | ||
| <NoPackageAnalysis>true</NoPackageAnalysis> | ||
|
|
||
| <TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput</TargetsForTfmSpecificContentInPackage> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Paramore.Brighter.Analyzer\Paramore.Brighter.Analyzer.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <None Update="tools\*.ps1" CopyToOutputDirectory="PreserveNewest" Pack="true" PackagePath="" /> | ||
| </ItemGroup> | ||
|
|
||
| <Target Name="_AddAnalyzersToOutput"> | ||
| <ItemGroup> | ||
| <TfmSpecificPackageFile Include="$(OutputPath)\Paramore.Brighter.Analyzer.dll" PackagePath="analyzers/dotnet/cs" /> | ||
| </ItemGroup> | ||
| </Target> | ||
|
|
||
| </Project> |
6 changes: 6 additions & 0 deletions
6
src/Paramore.Brighter.Analyzer.Package/build/Paramore.Brighter.Analyzer.props
This file contains hidden or 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,6 @@ | ||
| <Project> | ||
| <ItemGroup> | ||
| <GlobalAnalyzerConfigFiles Condition="'$(BrighterAnalysisMode)' == 'None'" Include="$(MSBuildThisFileDirectory)\..\configuration\none.editorconfig" /> | ||
| <GlobalAnalyzerConfigFiles Condition="'$(BrighterAnalysisMode)' == 'Default'" Include="$(MSBuildThisFileDirectory)\..\configuration\default.editorconfig" /> | ||
| </ItemGroup> | ||
| </Project> |
5 changes: 5 additions & 0 deletions
5
src/Paramore.Brighter.Analyzer.Package/configuration/default.editorconfig
This file contains hidden or 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,5 @@ | ||
| dotnet_diagnostic.BRT001.severity = warning | ||
| dotnet_diagnostic.BRT002.severity = warning | ||
| dotnet_diagnostic.BRT003.severity = warning | ||
| dotnet_diagnostic.BRT004.severity = warning | ||
| dotnet_diagnostic.BRT005.severity = warning |
5 changes: 5 additions & 0 deletions
5
src/Paramore.Brighter.Analyzer.Package/configuration/none.editorconfig
This file contains hidden or 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,5 @@ | ||
| dotnet_diagnostic.BRT001.severity = none | ||
| dotnet_diagnostic.BRT002.severity = none | ||
| dotnet_diagnostic.BRT003.severity = none | ||
| dotnet_diagnostic.BRT004.severity = none | ||
| dotnet_diagnostic.BRT005.severity = none |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.