I'm trying my hand at packaging a working Roslyn code analyzer, and I'm afraid it's not going so well. I've run out of things to try.
What I have so far I've largely gleaned from ChatGPT, who of course is supremely confident in the accuracy of his answers, so he's likely the first culprit in this head-scratcher.
I've verified the apparently-important bits against this blog post, to include the PowerShell scripts. According to my Package Manager output, however, the scripts don't appear to be running when I install the package into the consuming project (Console1
):
Running restore with 20 concurrent jobs.Reading project file D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\ConsoleApp1.vbproj.Restoring packages for D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\ConsoleApp1.vbproj...Restoring packages for .NETCoreApp,Version=v8.0...Resolving conflicts for net8.0...Acquiring lock for the installation of Intexx.EmailValidator 1.0.69Acquired lock for the installation of Intexx.EmailValidator 1.0.69Installed Intexx.EmailValidator 1.0.69 from D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\Intexx.EmailValidator\bin\Debug to D:\Dev\Packages\intexx.emailvalidator\1.0.69 with content hash xqaUYidXPgY7yeWTK5NonM8Bch7sYVQgfdBXC/xqJKlOqoBWDS4SDSLzf+gI+4IKSU2C6FN20Xmw1QEnOCHBjg==. CACHE https://api.nuget.org/v3/vulnerabilities/index.json CACHE https://api.nuget.org/v3-vulnerabilities/2024.06.05.23.37.01/vulnerability.base.json CACHE https://api.nuget.org/v3-vulnerabilities/2024.06.05.23.37.01/2024.06.07.11.37.04/vulnerability.update.jsonAll packages and projects are compatible with net8.0.Installing NuGet package Intexx.EmailValidator 1.0.69.Committing restore...Generating MSBuild file D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\ConsoleApp1.vbproj.nuget.g.props.Generating MSBuild file D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\ConsoleApp1.vbproj.nuget.g.targets.Writing assets file to disk. Path: D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\project.assets.jsonWriting cache file to disk. Path: D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\project.nuget.cachePersisting dg to D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\ConsoleApp1.vbproj.nuget.dgspec.jsonRestored D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\ConsoleApp1.vbproj (in 49 ms).Successfully uninstalled 'Intexx.EmailValidator 1.0.67' from ConsoleApp1Successfully installed 'Intexx.EmailValidator 1.0.69' to ConsoleApp1Executing nuget actions took 41 msTime Elapsed: 00:00:00.2669689========== Finished ==========Restoring NuGet packages...Running restore with 20 concurrent jobs.Reading project file D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\ConsoleApp1.vbproj.The restore inputs for 'ConsoleApp1' have not changed. No further actions are required to complete the restore.Committing restore...Assets file has not changed. Skipping assets file writing. Path: D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\project.assets.jsonNo-Op restore. The cache will not be updated. Path: D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\obj\project.nuget.cacheRestored D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\ConsoleApp1\ConsoleApp1.vbproj (in 0.5 ms).NuGet Config files used: C:\Users\Work\AppData\Roaming\NuGet\NuGet.Config C:\Program Files (x86)\NuGet\Config\Microsoft.VisualStudio.FallbackLocation.config C:\Program Files (x86)\NuGet\Config\Microsoft.VisualStudio.Offline.configFeeds used: https://api.nuget.org/v3/index.json https://server5/InteXX/_packaging/Packages/nuget/v3/index.json C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\ D:\Dev\Projects\Common\Libraries\Intexx.EmailValidator\Intexx.EmailValidator\bin\Debug C:\Program Files\dotnet\library-packsTime Elapsed: 00:00:00.0467482========== Finished ==========
Further, I tried manually copying Analyzers.dll
to $(OutputPath)analyzers/dotnet/vb/
, but that didn't make any difference. For reasons unknown to me, my analyzer refuses to run.
Also, I'm not sure what that install.ps1
script is doing with this call:
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
Is the consuming project file supposed to be altered somehow at package installation time? Maybe that's the key; the official documentation doesn't specify one way or another. The sample package found there is quite complex and doesn't offer much help to a beginner.
I did try creating a project based on the Analyer template, as discussed here, but again the complexity of that is too much to grasp for a beginner. I can't escape a nagging feeling that this should be a simple matter of getting a small thing just right. Easy when you know how, right?
Which is what's frustrating about the other post—it provides an extremely simple example to follow. Yet when I replicate it, my version doesn't work.
What am I doing wrong? How can I get my analyzer to run, given the setup that I have so far?
Here're my files:
Analyzer
Imports SystemImports System.Collections.ImmutableImports System.LinqImports Microsoft.CodeAnalysisImports Microsoft.CodeAnalysis.DiagnosticsImports Microsoft.CodeAnalysis.VisualBasicImports Microsoft.CodeAnalysis.VisualBasic.SyntaxNamespace Analyzers<DiagnosticAnalyzer(LanguageNames.VisualBasic)> Public Class ProhibitValidator Inherits DiagnosticAnalyzer Shared Sub New() MessageFormat = "Class 'Intexx.Email.Validation.Server.Validator' is only allowed in projects targeting .NET Framework 4.8 or earlier." Description = "This class is prohibited in projects targeting frameworks later than .NET Framework 4.8." Title = "Prohibited class usage" Rule = New DiagnosticDescriptor( isEnabledByDefault:=True, defaultSeverity:=DiagnosticSeverity.Error, messageFormat:=MessageFormat, description:=Description, category:=CATEGORY, title:=Title, id:=DIAGNOSTIC_ID ) End Sub Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) Get Return ImmutableArray.Create(Rule) End Get End Property Public Overrides Sub Initialize(Context As AnalysisContext) Context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None) Context.EnableConcurrentExecution() Context.RegisterSyntaxNodeAction(Me.AnalyzeNode, SyntaxKind.ObjectCreationExpression) End Sub Private Function AnalyzeNode() As Action(Of SyntaxNodeAnalysisContext) Dim oObjectCreationExpression As ObjectCreationExpressionSyntax Dim oValidatorSymbol As INamedTypeSymbol Dim sTargetFramework As String Dim oUnknownSymbol As INamedTypeSymbol Dim oDiagnostic As Diagnostic Return Sub(Context) oObjectCreationExpression = Context.Node oUnknownSymbol = TryCast(Context.SemanticModel.GetSymbolInfo(oObjectCreationExpression.Type).Symbol, INamedTypeSymbol) If oUnknownSymbol IsNot Nothing Then oValidatorSymbol = Context.Compilation.GetTypeByMetadataName("Intexx.Email.Validation.Server.Validator")' Check whether the symbol matches the prohibited class If oValidatorSymbol IsNot Nothing AndAlso SymbolEqualityComparer.Default.Equals(oUnknownSymbol, oValidatorSymbol) Then sTargetFramework = Context.Options.AdditionalFiles.FirstOrDefault?.Path If sTargetFramework IsNot Nothing AndAlso Not sTargetFramework = "net48" Then oDiagnostic = Diagnostic.Create(Rule, oObjectCreationExpression.GetLocation) Context.ReportDiagnostic(oDiagnostic) End If End If End If End Sub End Function Public Const DIAGNOSTIC_ID As String = "PCU001" Private Const CATEGORY As String = "Usage" Private Shared ReadOnly MessageFormat As LocalizableString Private Shared ReadOnly Description As LocalizableString Private Shared ReadOnly Title As LocalizableString Private Shared ReadOnly Rule As DiagnosticDescriptor End ClassEnd Namespace
Package Project
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><DisableImplicitNamespaceImports>True</DisableImplicitNamespaceImports><GenerateDocumentationFile>True</GenerateDocumentationFile><GeneratePackageOnBuild>True</GeneratePackageOnBuild><AppDesignerFolder>Properties</AppDesignerFolder><TargetFramework>netstandard2.0</TargetFramework><RootNamespace></RootNamespace><OutputType>Library</OutputType><IsPackable>True</IsPackable><Version>1.0.69</Version></PropertyGroup><ItemGroup><Folder Include="Properties\" /></ItemGroup><ItemGroup><Reference Include="HexDns.NET"><HintPath>Hexillion\HexDns.NET.dll</HintPath></Reference><Reference Include="HexNetwork.NET"><HintPath>Hexillion\HexNetwork.NET.dll</HintPath></Reference><Reference Include="HexValidEmail.NET"><HintPath>Hexillion\HexValidEmail.NET.dll</HintPath></Reference></ItemGroup><ItemGroup><None Include="Hexillion\HexDns.NET.dll"><Pack>true</Pack><PackagePath>lib\netstandard2.0\HexDns.NET.dll</PackagePath></None><None Include="Hexillion\HexNetwork.NET.dll"><Pack>true</Pack><PackagePath>lib\netstandard2.0\HexNetwork.NET.dll</PackagePath></None><None Include="Hexillion\HexValidEmail.NET.dll"><Pack>true</Pack><PackagePath>lib\netstandard2.0\HexValidEmail.NET.dll</PackagePath></None><Content Include="HexValidEmail.NET.lic"><Pack>true</Pack><PackagePath>contentFiles\any\any;content</PackagePath><PackageCopyToOutput>true</PackageCopyToOutput><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></Content></ItemGroup><ItemGroup><None Update="build\Intexx.EmailValidator.targets"><Pack>True</Pack><BuildAction>Content</BuildAction><PackagePath>build\Intexx.EmailValidator.targets</PackagePath></None></ItemGroup><Target Name="PrintOutputPath" AfterTargets="Build"><Message Importance="high" Text="OutputPath: $(OutputPath)" /></Target><ItemGroup><None Update="tools\*.ps1" CopyToOutputDirectory="Always" Pack="true" PackagePath="" /><None Include="$(OutputPath)Analyzers.dll" Pack="True" PackagePath="analyzers\dotnet\vb" Visible="False" /></ItemGroup><ItemGroup><PackageReference Include="Intexx.IO" Version="2024.6.3.1" /><PackageReference Include="itxcorlib" Version="2024.6.5.1" /><PackageReference Include="Microsoft.Build.Framework" Version="17.10.4" /><PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.10.4" /><PackageReference Include="Newtonsoft.Json" Version="13.0.3" /></ItemGroup><ItemGroup><ProjectReference Include="..\Analyzers\Analyzers.vbproj" /></ItemGroup></Project>
.targets
File
<?xml version="1.0" encoding="utf-8" ?><Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><UsingTask AssemblyFile="$(ProjectDir)$(OutputPath)Intexx.EmailValidator.dll" TaskName="CheckFrameworkForClassTask" /><Target Name="CheckFrameworkForClass" BeforeTargets="Build"><CheckFrameworkForClassTask TargetFramework="$(TargetFramework)" StartingFolder="$(ProjectDir)" ErrorMessage="The validation server component only supports applications targeting .NET Framework 4.8." /></Target></Project>
Consuming Project
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><RootNamespace>ConsoleApp1</RootNamespace><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Intexx.EmailValidator" Version="1.0.69" /></ItemGroup></Project>
Package Contents