‹ 首页

analyzer-development

@fritzandfriends · 收录于 昨天 · 上游提交 1 个月前

Create Roslyn-based analyzers and code-fix providers for detecting Web Forms migration patterns. Covers DiagnosticAnalyzer architecture, diagnostic ID conventions (BWFC001–BWFC099), CodeFixProvider authoring, testing with CSharpAnalyzerTest/CSharpCodeFixTest, and the analyzer release tracking files. Use when adding a new migration rule, creating a code-fix suggestion, or debugging analyzer behavior.

适合你,如果你正在为 Web Forms 迁移编写自定义代码分析规则

/ 通过 npx 安装 校验哈希
npx oh-my-skill add fritzandfriends/blazorwebformscomponents/analyzer-development
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- fritzandfriends/blazorwebformscomponents/analyzer-development
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify fritzandfriends/blazorwebformscomponents/analyzer-development
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
449GitHub stars
~1.5K上下文体积 · 单文件
索引托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · 3154758

Analyzer Development

This skill covers creating Roslyn analyzers and code-fix providers in the BlazorWebFormsComponents.Analyzers project.

Architecture

The analyzer project ships as a NuGet analyzer package alongside the main BWFC library. Analyzers detect Web Forms patterns in user code and suggest BWFC replacements.

Project Structure
src/BlazorWebFormsComponents.Analyzers/
├── *Analyzer.cs              — DiagnosticAnalyzer implementations
├── *CodeFixProvider.cs       — Optional companion code-fix providers
├── SyntaxExtensions.cs       — Shared Roslyn helper methods
├── AnalyzerReleases.Shipped.md    — Released diagnostic IDs
└── AnalyzerReleases.Unshipped.md  — In-progress diagnostic IDs

src/BlazorWebFormsComponents.Analyzers.Test/
├── *AnalyzerTests.cs         — Analyzer unit tests
├── *CodeFixTests.cs          — Code-fix unit tests
└── AllAnalyzersIntegrationTests.cs — Cross-cutting integration tests
Current Analyzers

| Analyzer | Diagnostic | Detects | |----------|------------|---------| | EventHandlerSignatureAnalyzer | BWFC001+ | Web Forms event handler signatures | | FindControlUsageAnalyzer | BWFC0xx | FindControl() calls | | IsPostBackUsageAnalyzer | BWFC0xx | IsPostBack property usage | | ViewStateUsageAnalyzer | BWFC0xx | ViewState["key"] access | | ViewStatePropertyPatternAnalyzer | BWFC0xx | ViewState-backed property patterns | | SessionUsageAnalyzer | BWFC0xx | Session["key"] access | | ResponseObjectUsageAnalyzer | BWFC0xx | Response.Redirect, Response.Write | | ResponseRedirectAnalyzer | BWFC0xx | Response.Redirect specifically | | RequestObjectUsageAnalyzer | BWFC0xx | Request.QueryString, Request.Form | | PageClientScriptUsageAnalyzer | BWFC0xx | ClientScript.Register* | | ScriptManagerUsageAnalyzer | BWFC0xx | ScriptManager usage | | RunatServerAnalyzer | BWFC0xx | runat="server" in markup | | NonSerializableViewStateAnalyzer | BWFC0xx | Non-serializable types in ViewState | | MissingParameterAttributeAnalyzer | BWFC0xx | Missing [Parameter] on public props | | RequiredAttributeAnalyzer | BWFC0xx | Required HTML attributes | | IPostBackEventHandlerUsageAnalyzer | BWFC0xx | IPostBackEventHandler implementation |

Creating a New Analyzer
Step 1: Define the Diagnostic Descriptor
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;

namespace BlazorWebFormsComponents.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyPatternAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "BWFC0XX";  // Pick next available ID

    private static readonly DiagnosticDescriptor Rule = new(
        id: DiagnosticId,
        title: "Web Forms pattern detected",
        messageFormat: "'{0}' is a Web Forms pattern — use '{1}' instead",
        category: "Migration",
        defaultSeverity: DiagnosticSeverity.Info,  // Info, Warning, or Error
        isEnabledByDefault: true,
        description: "Detects a Web Forms pattern that should be replaced with a BWFC equivalent.");

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
        ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();

        // Register for the syntax node types you want to analyze
        context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression);
    }

    private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
        var invocation = (InvocationExpressionSyntax)context.Node;

        // Check if this matches the Web Forms pattern
        if (invocation.Expression is MemberAccessExpressionSyntax memberAccess
            && memberAccess.Name.Identifier.Text == "TargetMethod")
        {
            var diagnostic = Diagnostic.Create(
                Rule,
                invocation.GetLocation(),
                "TargetMethod",     // {0} in messageFormat
                "BwfcAlternative"); // {1} in messageFormat

            context.ReportDiagnostic(diagnostic);
        }
    }
}
Step 2: Create a Code-Fix Provider (Optional)
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Immutable;
using System.Composition;

namespace BlazorWebFormsComponents.Analyzers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MyPatternCodeFixProvider))]
[Shared]
public class MyPatternCodeFixProvider : CodeFixProvider
{
    public override ImmutableArray<string> FixableDiagnosticIds =>
        ImmutableArray.Create(MyPatternAnalyzer.DiagnosticId);

    public override FixAllProvider GetFixAllProvider() =>
        WellKnownFixAllProviders.BatchFixer;

    public override async Task RegisterCodeFixesAsync(CodeFixContext context)
    {
        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
        var diagnostic = context.Diagnostics[0];
        var diagnosticSpan = diagnostic.Location.SourceSpan;

        var node = root?.FindNode(diagnosticSpan);
        if (node == null) return;

        context.RegisterCodeFix(
            CodeAction.Create(
                title: "Replace with BWFC equivalent",
                createChangedDocument: c => FixAsync(context.Document, node, c),
                equivalenceKey: nameof(MyPatternCodeFixProvider)),
            diagnostic);
    }

    private static async Task<Document> FixAsync(
        Document document, SyntaxNode node, CancellationToken cancellationToken)
    {
        var root = await document.GetSyntaxRootAsync(cancellationToken);
        // Create the replacement node
        var newNode = SyntaxFactory.ParseExpression("BwfcAlternative()");
        var newRoot = root!.ReplaceNode(node, newNode);
        return document.WithSyntaxRoot(newRoot);
    }
}
Step 3: Update Release Tracking

Add the new diagnostic ID to AnalyzerReleases.Unshipped.md:

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|------
BWFC0XX | Migration | Info | Detects MyPattern usage
Step 4: Write Tests
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using VerifyAnalyzer = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest<
    BlazorWebFormsComponents.Analyzers.MyPatternAnalyzer,
    Microsoft.CodeAnalysis.Testing.DefaultVerifier>;

namespace BlazorWebFormsComponents.Analyzers.Test;

public class MyPatternAnalyzerTests
{
    // Stub types the analyzer needs to compile against
    private const string ComponentStubs = @"
namespace System.Web.UI
{
    public class Page
    {
        public void TargetMethod() { }
    }
}";

    [Fact]
    public async Task DetectsTargetMethod_ReportsDiagnostic()
    {
        var test = new VerifyAnalyzer
        {
            TestCode = ComponentStubs + @"
class TestPage : System.Web.UI.Page
{
    void Test()
    {
        {|#0:TargetMethod()|};
    }
}",
            ExpectedDiagnostics =
            {
                new DiagnosticResult(MyPatternAnalyzer.DiagnosticId)
                    .WithLocation(0)
                    .WithArguments("TargetMethod", "BwfcAlternative")
            }
        };

        await test.RunAsync();
    }

    [Fact]
    public async Task NoTargetMethod_NoDiagnostic()
    {
        var test = new VerifyAnalyzer
        {
            TestCode = @"
class MyClass
{
    void Test()
    {
        var x = 1 + 2;
    }
}"
        };

        await test.RunAsync();
    }
}
Testing Commands
# Run analyzer tests
dotnet test src/BlazorWebFormsComponents.Analyzers.Test --nologo

# Run a specific test
dotnet test src/BlazorWebFormsComponents.Analyzers.Test --filter "MyPatternAnalyzerTests"
Diagnostic ID Conventions
  • Range: BWFC001 through BWFC099
  • Category: Migration (for Web Forms → Blazor patterns)
  • Severity levels:
  • Info — Pattern detected, BWFC equivalent available (most common)
  • Warning — Pattern that will cause runtime issues if not addressed
  • Error — Pattern that prevents compilation in Blazor
NuGet Packaging

The analyzer DLL is packed into the analyzers/dotnet/cs path automatically via the .csproj:

<None Include="$(OutputPath)\$(AssemblyName).dll"
      Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />

Consumers get analyzers automatically when they reference the BWFC NuGet package.

Checklist
  • [ ] Analyzer class has [DiagnosticAnalyzer(LanguageNames.CSharp)] attribute
  • [ ] Diagnostic ID follows BWFC0XX convention (pick next available)
  • [ ] Initialize calls ConfigureGeneratedCodeAnalysis and EnableConcurrentExecution
  • [ ] Code-fix provider (if any) has [ExportCodeFixProvider] and [Shared] attributes
  • [ ] Diagnostic ID added to AnalyzerReleases.Unshipped.md
  • [ ] Positive tests (pattern detected → diagnostic reported)
  • [ ] Negative tests (no pattern → no diagnostic)
  • [ ] Code-fix tests (if applicable) verify the fix produces correct code
  • [ ] dotnet test src/BlazorWebFormsComponents.Analyzers.Test passes
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。