My first Roslyn code analyzer refuses to run

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:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1ConsoleApp1.vbproj.
Restoring packages for D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1ConsoleApp1.vbproj...
Restoring packages for .NETCoreApp,Version=v8.0...
Resolving conflicts for net8.0...
Acquiring lock for the installation of Intexx.EmailValidator 1.0.69
Acquired lock for the installation of Intexx.EmailValidator 1.0.69
Installed Intexx.EmailValidator 1.0.69 from D:DevProjectsCommonLibrariesIntexx.EmailValidatorIntexx.EmailValidatorbinDebug to D:DevPackagesintexx.emailvalidator1.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.json
All packages and projects are compatible with net8.0.
Installing NuGet package Intexx.EmailValidator 1.0.69.
Committing restore...
Generating MSBuild file D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objConsoleApp1.vbproj.nuget.g.props.
Generating MSBuild file D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objConsoleApp1.vbproj.nuget.g.targets.
Writing assets file to disk. Path: D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objproject.assets.json
Writing cache file to disk. Path: D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objproject.nuget.cache
Persisting dg to D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objConsoleApp1.vbproj.nuget.dgspec.json
Restored D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1ConsoleApp1.vbproj (in 49 ms).
Successfully uninstalled 'Intexx.EmailValidator 1.0.67' from ConsoleApp1
Successfully installed 'Intexx.EmailValidator 1.0.69' to ConsoleApp1
Executing nuget actions took 41 ms
Time Elapsed: 00:00:00.2669689
========== Finished ==========

Restoring NuGet packages...
Running restore with 20 concurrent jobs.
Reading project file D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1ConsoleApp1.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:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objproject.assets.json
No-Op restore. The cache will not be updated. Path: D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1objproject.nuget.cache
Restored D:DevProjectsCommonLibrariesIntexx.EmailValidatorConsoleApp1ConsoleApp1.vbproj (in 0.5 ms).

NuGet Config files used:
    C:UsersWorkAppDataRoamingNuGetNuGet.Config
    C:Program Files (x86)NuGetConfigMicrosoft.VisualStudio.FallbackLocation.config
    C:Program Files (x86)NuGetConfigMicrosoft.VisualStudio.Offline.config

Feeds used:
    https://api.nuget.org/v3/index.json
    https://server5/InteXX/_packaging/Packages/nuget/v3/index.json
    C:Program Files (x86)Microsoft SDKsNuGetPackages
    D:DevProjectsCommonLibrariesIntexx.EmailValidatorIntexx.EmailValidatorbinDebug
    C:Program Filesdotnetlibrary-packs
Time 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 System
Imports System.Collections.Immutable
Imports System.Linq
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace 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 Class
End 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>HexillionHexDns.NET.dll</HintPath>
    </Reference>
    <Reference Include="HexNetwork.NET">
      <HintPath>HexillionHexNetwork.NET.dll</HintPath>
    </Reference>
    <Reference Include="HexValidEmail.NET">
      <HintPath>HexillionHexValidEmail.NET.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup>
    <None Include="HexillionHexDns.NET.dll">
      <Pack>true</Pack>
      <PackagePath>libnetstandard2.0HexDns.NET.dll</PackagePath>
    </None>
    <None Include="HexillionHexNetwork.NET.dll">
      <Pack>true</Pack>
      <PackagePath>libnetstandard2.0HexNetwork.NET.dll</PackagePath>
    </None>
    <None Include="HexillionHexValidEmail.NET.dll">
      <Pack>true</Pack>
      <PackagePath>libnetstandard2.0HexValidEmail.NET.dll</PackagePath>
    </None>
    <Content Include="HexValidEmail.NET.lic">
      <Pack>true</Pack>
      <PackagePath>contentFilesanyany;content</PackagePath>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <None Update="buildIntexx.EmailValidator.targets">
      <Pack>True</Pack>
      <BuildAction>Content</BuildAction>
      <PackagePath>buildIntexx.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="analyzersdotnetvb" 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="..AnalyzersAnalyzers.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

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật