Skip to content
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

Add Roslyn Analyzer project #349

Merged
merged 7 commits into from
May 24, 2024
Merged

Conversation

CollinAlpert
Copy link
Contributor

As promised, I have created a Roslyn Analyzer which emits a warning when a type implementing ICarterModule has a constructor with parameters. Feel free to look over the test cases and see if you agree with the behavior.

Copy link
Member

@jchannon jchannon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff! Thanks


{{type}} MyCarterModule : ICarterModule
{
internal {|#0:MyCarterModule|}(string s) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest what is the \#0: stuff?

Copy link
Contributor Author

@CollinAlpert CollinAlpert May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is Roslyn's markup syntax, allowing to specify where a diagnostic is supposed to be emitted, without having to specify the exact line and column span at which the diagnostic occurs in the DiagnosticResult. Hence the .WithLocation(0);. If the analyzer emitted diagnostics at multiple locations, I would use {|#1: and then call .WithLocation(1);.

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be net8.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be, however I'd advise against it if you want maximum compatibility. Roslyn components are supposed to target netstandard2.0. I've noticed targeting higher versions works too, however not for everybody. Rider always works fine, it might just not work for some Visual Studio users. For most Visual Studio users it will also be fine.

Considering you want to ship this with the Carter package, I say go for it and if we notice that the analyzer does not kick in for the majority of users, we can still separate this into a separate package. I will make the necessary adjustments.

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to ship the analyser with Carter so should Carter reference this project that is then embedded in Carter's nuget package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look into this, I'm not sure if analyzers work as transitive dependencies.

@CollinAlpert
Copy link
Contributor Author

I managed to pack the analyzer with the Carter package and still keep the analyzer project netstandard2.0. This will ensure maximum compatibility across Visual Studio versions.

Also since the analyzer is now also added to the Carter project, we got a hit. The CarterModule class has a constructor with a parameter and thus the analyzer flags this. I've disabled the warning for now, however let me know if you want me to tweak the analyzer in this regard.

@jchannon
Copy link
Member

Looking good - excuse my ignorance but any reason why we can't delete the analyzer project and just put the analyzer classes inside Carter?

@CollinAlpert
Copy link
Contributor Author

No worries! We can do that, however the analyzer might not work for a very small amount of users due to the targeting of net8.0 instead of netstandard2.0 as recommended. But like I said, I see only minimal risk so I am fine with it. I will make the changes.

</PropertyGroup>
<ItemGroup>
<None Include="..\..\media\carterlogo.png" Pack="true" PackagePath="\" />
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse my ignorance again - what does this do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't hold back with questions – it's important you understand the code I added :)

To answer your question: It's not enough to add the code to the project. To make analyzers work inside a NuGet package, they must be placed in a specific directory inside the package, in this case the analyzers/dotnet/cs directory, since it is a C# analyzer.

@@ -89,7 +89,9 @@ private static Assembly SafeLoadAssembly(AssemblyName assemblyName)
{
try
{
#pragma warning disable RS1035
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this to the NoWarn csproj list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since this warning would be valid if DependencyContextAssemblyCatalog were an analyzer. Since it is not, the warning can be suppressed. Just one small caveat of not splitting up library code and analyzers.

@@ -88,12 +88,14 @@ public static async Task BindAndSaveFile(this HttpRequest request, string saveLo

private static async Task SaveFileInternal(IFormFile file, string saveLocation, string fileName = "")
{
#pragma warning disable RS1035
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this to the NoWarn csproj list?

@jchannon
Copy link
Member

If I put in a ctor dependency in the Carter.Sample project that has a project ref to Carter will I still get a warning?

@CollinAlpert
Copy link
Contributor Author

Yes, you will, however the project reference must look like this to include analyzer behavior:
<ProjectReference Include="../../src/Carter/Carter.csproj" OutputItemType="Analyzer" />

OutputItemType is not necessary for PackageReference elements, since we pack the DLL in the analyzer directory, something that is not possible to ProjectReference elements.

@jchannon
Copy link
Member

Should rename it to Carter from CARTER1?
Screenshot 2024-05-24 at 12 06 37

@CollinAlpert
Copy link
Contributor Author

We could, however I wouldn't recommend it. The guidelines for diagnostic IDs recommend using the project name + a number. If you decide you want another analyzer to warn about something else in the future, you'd need to distinguish it from this one, e.g. by naming it CARTER2. The compiler diagnostics (CSXXXX), code analysis diagnostics (CAXXXX) and system library diagnostics (SYSLIBXXXX) all follow this pattern.

@jchannon
Copy link
Member

Can you rebase, I've added some permissions to the actions workflow file :)

@jchannon
Copy link
Member

Actually GH has a button to do it :)

@jchannon jchannon merged commit 8456dd0 into CarterCommunity:main May 24, 2024
@jchannon jchannon added this to the vNext milestone May 24, 2024
@jchannon
Copy link
Member

Thanks for this!! Appreciate it 👍 👍

@CollinAlpert CollinAlpert deleted the analyzers branch May 24, 2024 14:12
@CollinAlpert
Copy link
Contributor Author

Happy to be of assistance! Ping me if you want any other rules implemented 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants