Skip to content

Feat/gframework analysis#172

Merged
GeWuYou merged 3 commits into
mainfrom
feat/gframework-analysis
Apr 4, 2026
Merged

Feat/gframework analysis#172
GeWuYou merged 3 commits into
mainfrom
feat/gframework-analysis

Conversation

@GeWuYou

@GeWuYou GeWuYou commented Apr 4, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

添加一个 Roslyn 分析器,用于验证上下文绑定模型、系统和工具的静态注册,并为新的注册可见性诊断和行为编写文档。

Enhancements:

  • 引入 ContextRegistrationAnalyzer,通过检查字段注入特性和 GetX 调用,确保被引用的服务在其架构内具有可静态发现的注册。
  • 为缺失的模型、系统和工具注册提供诊断描述符和 ID,并将这些诊断接入分析器的发布元数据中。

Documentation:

  • 在中文生成器文档和 README 中,记录注册可见性分析器、其支持的使用位置、注册来源,以及新的诊断 ID。

Tests:

  • 添加分析器测试,覆盖从架构和模块中检测注册、集合注入、架构歧义场景,以及正向和负向诊断两类情形。
Original summary in English

Summary by Sourcery

Add a Roslyn analyzer that validates static registrations for context-bound models, systems, and utilities, and document the new registration visibility diagnostics and behavior.

Enhancements:

  • Introduce a ContextRegistrationAnalyzer that inspects field injection attributes and GetX calls to ensure referenced services have statically discoverable registrations within their architecture.
  • Provide diagnostic descriptors and IDs for missing model, system, and utility registrations and wire them into the analyzer release metadata.

Documentation:

  • Document the registration visibility analyzer, its supported usage sites, registration sources, and new diagnostic IDs in both Chinese generator docs and the README.

Tests:

  • Add analyzer tests covering registration detection from architectures and modules, collection injections, ambiguous-architecture cases, and both positive and negative diagnostic scenarios.

GeWuYou added 2 commits April 3, 2026 23:48
- 新增 Context Get 注入生成器详细文档,包含使用示例和诊断信息
- 添加源代码生成器总览文档,涵盖 Log、Config Schema、ContextAware 等功能
- 配置测试项目 GFramework.SourceGenerators.Tests 的项目文件和依赖
- 生成器诊断规则新增至 Unshipped 分析器发布跟踪文件
- 使用属性模式匹配替换条件判断语句
- 简化了方法声明语法的空值检查逻辑
- 优化了构造函数声明语法的表达式体检查
- 提高了代码可读性和维护性
- 减少了冗余的语法树遍历操作
@deepsource-io

deepsource-io Bot commented Apr 4, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in ec5153f...7901a69 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
C# Apr 4, 2026 3:42p.m. Review ↗
Secrets Apr 4, 2026 3:42p.m. Review ↗

@sourcery-ai

sourcery-ai Bot commented Apr 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

添加一个新的 Roslyn 分析器,用于检查 Model/System/Utility 的 context-get 使用点,在其架构中是否存在对应的、可静态发现的注册;并将其诊断接入现有包中、编写文档说明其行为,并通过分析器测试和一个小型的分析器测试工具辅助类进行覆盖。

ContextRegistrationAnalyzer 字段使用分析的时序图

sequenceDiagram
    participant Compiler as Roslyn_Compiler
    participant Analyzer as ContextRegistrationAnalyzer
    participant Symbols as SymbolCache
    participant Index as RegistrationIndex
    participant Field as AnalyzeField
    participant Diag as ContextRegistrationDiagnostics

    Compiler->>Analyzer: Initialize(context)
    Analyzer->>Analyzer: ConfigureGeneratedCodeAnalysis(None)
    Analyzer->>Analyzer: EnableConcurrentExecution()

    Compiler->>Analyzer: CompilationStartAction(compilationContext)
    Analyzer->>Symbols: Create(compilation)
    Symbols-->>Analyzer: SymbolCache instance
    Analyzer->>Analyzer: Check symbols.IsReady
    alt Symbols ready
        Analyzer->>Analyzer: Create Lazy RegistrationIndex
        Analyzer->>Compiler: RegisterSyntaxNodeAction(AnalyzeField, VariableDeclarator)
        Analyzer->>Compiler: RegisterOperationAction(AnalyzeInvocation, Invocation)
    else Symbols not ready
        Analyzer-->>Compiler: Return (no analysis)
    end

    loop For each field declarator with attributes
        Compiler->>Analyzer: AnalyzeField(SyntaxNodeAnalysisContext)
        Analyzer->>Field: TryCreateBindingRequest(fieldSymbol, location, symbols)
        alt Attribute matches GetModel/GetSystem/GetUtility
            Field-->>Analyzer: BindingRequest(kind, ownerType, serviceType, location)
            Analyzer->>Index: registrationIndex.Value (build if first time)
            Note over Index: Build(compilation, symbols)
            Index->>Index: AnalyzeArchitecture for each Architecture subtype
            Index-->>Analyzer: RegistrationIndex instance

            Analyzer->>Index: TryGetOwningArchitecture(ownerType, out architectureType)
            alt Owning architecture found
                Index-->>Analyzer: architectureType
                Analyzer->>Index: HasRegistration(architectureType, kind, serviceType)
                alt Registration exists
                    Index-->>Analyzer: true
                    Analyzer-->>Compiler: No diagnostic
                else Registration missing
                    Index-->>Analyzer: false
                    Analyzer->>Diag: Select descriptor by kind
                    Diag-->>Analyzer: DiagnosticDescriptor
                    Analyzer-->>Compiler: ReportDiagnostic(GF_ContextRegistration_001/002/003)
                end
            else No unique architecture
                Index-->>Analyzer: false
                Analyzer-->>Compiler: No diagnostic
            end
        else Not a supported attribute
            Field-->>Analyzer: no request
            Analyzer-->>Compiler: Return
        end
    end
Loading

ContextRegistrationAnalyzer 及相关类型的类图

classDiagram
    direction LR

    class ContextRegistrationAnalyzer {
        <<DiagnosticAnalyzer>>
        +SupportedDiagnostics : ImmutableArray~DiagnosticDescriptor~
        +Initialize(context AnalysisContext) void
        -AnalyzeField(context SyntaxNodeAnalysisContext, symbols SymbolCache, registrationIndex RegistrationIndex) void
        -AnalyzeInvocation(context OperationAnalysisContext, symbols SymbolCache, registrationIndex RegistrationIndex) void
        -ReportMissingRegistration(context SyntaxNodeAnalysisContext, registrationIndex RegistrationIndex, request BindingRequest) void
        -ReportMissingRegistration(context OperationAnalysisContext, registrationIndex RegistrationIndex, request BindingRequest) void
        -CreateMissingRegistrationDiagnostic(request BindingRequest, architectureType INamedTypeSymbol) Diagnostic
        -TryCreateBindingRequest(fieldSymbol IFieldSymbol, location Location, symbols SymbolCache, outRequest BindingRequest) bool
        -TryCreateBindingRequest(invocation IInvocationOperation, ownerType INamedTypeSymbol, symbols SymbolCache, outRequest BindingRequest) bool
        -TryMapAttribute(attributeType INamedTypeSymbol, symbols SymbolCache, outKind ComponentKind, outExpectsCollection bool) bool
        -IsSupportedGetInvocationTarget(targetMethod IMethodSymbol, symbols SymbolCache) bool
        -TryGetCollectionElementType(fieldType ITypeSymbol, symbols SymbolCache) INamedTypeSymbol
        -_contextAwareBindingNames : IReadOnlyDictionary~string,ComponentKind~
    }

    class ComponentKind {
        <<enum>>
        Model
        System
        Utility
    }

    class BindingRequest {
        <<record struct>>
        +Kind : ComponentKind
        +OwnerType : INamedTypeSymbol
        +ServiceType : INamedTypeSymbol
        +Location : Location
    }

    class SymbolCache {
        -SymbolCache(architectureType INamedTypeSymbol, iArchitectureType INamedTypeSymbol, iArchitectureModuleType INamedTypeSymbol, iArchitectureContextType INamedTypeSymbol, iReadOnlyListType INamedTypeSymbol, contextAwareServiceExtensionsType INamedTypeSymbol, getModelAttribute INamedTypeSymbol, getModelsAttribute INamedTypeSymbol, getSystemAttribute INamedTypeSymbol, getSystemsAttribute INamedTypeSymbol, getUtilityAttribute INamedTypeSymbol, getUtilitiesAttribute INamedTypeSymbol)
        +ArchitectureType : INamedTypeSymbol
        +IArchitectureType : INamedTypeSymbol
        +IArchitectureModuleType : INamedTypeSymbol
        +IArchitectureContext : INamedTypeSymbol
        +IReadOnlyList : INamedTypeSymbol
        +ContextAwareServiceExtensions : INamedTypeSymbol
        +GetModelAttribute : INamedTypeSymbol
        +GetModelsAttribute : INamedTypeSymbol
        +GetSystemAttribute : INamedTypeSymbol
        +GetSystemsAttribute : INamedTypeSymbol
        +GetUtilityAttribute : INamedTypeSymbol
        +GetUtilitiesAttribute : INamedTypeSymbol
        +IsReady : bool
        +Create(compilation Compilation) SymbolCache
    }

    class RegistrationIndex {
        -_compilation : Compilation
        -_registrations : IReadOnlyDictionary~INamedTypeSymbol,ArchitectureRegistrationData~
        -RegistrationIndex(compilation Compilation, registrations IReadOnlyDictionary~INamedTypeSymbol,ArchitectureRegistrationData~)
        +Build(compilation Compilation, symbols SymbolCache) RegistrationIndex
        +TryGetOwningArchitecture(ownerType INamedTypeSymbol, outArchitectureType INamedTypeSymbol) bool
        +HasRegistration(architectureType INamedTypeSymbol, componentKind ComponentKind, serviceType INamedTypeSymbol) bool
        -AnalyzeArchitecture(compilation Compilation, symbols SymbolCache, architectureType INamedTypeSymbol) ArchitectureRegistrationData
        -AnalyzeModule(compilation Compilation, symbols SymbolCache, moduleType INamedTypeSymbol, data ArchitectureRegistrationData, visitedModules ISet~INamedTypeSymbol~) void
        -GetArchitectureRootMethods(architectureType INamedTypeSymbol) IEnumerable~IMethodSymbol~
        -GetModuleRootMethods(moduleType INamedTypeSymbol) IEnumerable~IMethodSymbol~
        -TryGetInstalledModuleType(invocation IInvocationOperation, symbols SymbolCache, outModuleType INamedTypeSymbol) bool
        -TryGetRegistration(invocation IInvocationOperation, symbols SymbolCache, outKind ComponentKind, outRegisteredType INamedTypeSymbol) bool
        -TryResolveArchitectureHelperMethod(targetMethod IMethodSymbol, architectureType INamedTypeSymbol, outHelperMethod IMethodSymbol) bool
        -TryResolveModuleHelperMethod(targetMethod IMethodSymbol, moduleType INamedTypeSymbol, outHelperMethod IMethodSymbol) bool
    }

    class ArchitectureRegistrationData {
        -_models : HashSet~INamedTypeSymbol~
        -_systems : HashSet~INamedTypeSymbol~
        -_utilities : HashSet~INamedTypeSymbol~
        +IsEmpty : bool
        +Add(componentKind ComponentKind, registeredType INamedTypeSymbol) void
        +HasRegistration(componentKind ComponentKind, requestedType INamedTypeSymbol, compilation Compilation) bool
        +ContainsOwner(ownerType INamedTypeSymbol, compilation Compilation) bool
        -GetCollection(componentKind ComponentKind) ISet~INamedTypeSymbol~
    }

    class SymbolHelpers {
        <<static>>
        +EnumerateNamedTypes(namespaceSymbol INamespaceSymbol) IEnumerable~INamedTypeSymbol~
        +EnumerateTypeHierarchy(type INamedTypeSymbol) IEnumerable~INamedTypeSymbol~
        +IsAssignableTo(fromType ITypeSymbol, toType ITypeSymbol) bool
        +IsWithinTypeHierarchy(candidateType INamedTypeSymbol, ownerType INamedTypeSymbol) bool
        +ResolveHierarchyMethodImplementation(method IMethodSymbol, ownerType INamedTypeSymbol) IMethodSymbol
        +GetInvocationOperations(method IMethodSymbol, compilation Compilation) IEnumerable~IInvocationOperation~
        +TryGetCreatedType(operation IOperation) INamedTypeSymbol
        +IsServiceCompatible(registeredType INamedTypeSymbol, requestedType INamedTypeSymbol, compilation Compilation) bool
        +IsOwnershipCompatible(ownerType INamedTypeSymbol, registeredType INamedTypeSymbol, compilation Compilation) bool
    }

    class ContextRegistrationDiagnostics {
        <<static>>
        +ModelRegistrationMissing : DiagnosticDescriptor
        +SystemRegistrationMissing : DiagnosticDescriptor
        +UtilityRegistrationMissing : DiagnosticDescriptor
    }

    class DiagnosticAnalyzer {
    }

    class DiagnosticDescriptor {
    }

    class AnalyzerTestDriver_TAnalyzer_ {
        <<static>>
        +RunAsync(source string, diagnostics DiagnosticResult[]) Task
    }

    class CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_ {
        +TestState : TestStateType
        +DisabledDiagnostics : ICollection~string~
        +ExpectedDiagnostics : IList~DiagnosticResult~
        +RunAsync() Task
    }

    class DiagnosticResult {
    }

    %% Relationships
    ContextRegistrationAnalyzer --|> DiagnosticAnalyzer
    ContextRegistrationAnalyzer *-- SymbolCache
    ContextRegistrationAnalyzer *-- RegistrationIndex
    ContextRegistrationAnalyzer o-- BindingRequest
    ContextRegistrationAnalyzer ..> ComponentKind
    ContextRegistrationAnalyzer ..> ContextRegistrationDiagnostics

    RegistrationIndex *-- ArchitectureRegistrationData
    RegistrationIndex ..> SymbolHelpers
    RegistrationIndex ..> ComponentKind

    ArchitectureRegistrationData ..> ComponentKind
    ArchitectureRegistrationData ..> SymbolHelpers

    SymbolCache ..> PathContests

    ContextRegistrationDiagnostics ..> DiagnosticDescriptor

    AnalyzerTestDriver_TAnalyzer_ ..> CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_
    AnalyzerTestDriver_TAnalyzer_ ..> DiagnosticResult

    CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_ ..> AnalyzerTestDriver_TAnalyzer_
Loading

文件级变更

Change Details Files
引入 ContextRegistrationAnalyzer,用于在架构/模块之间跟踪注册情况,并对 context-get 使用点缺失的 Model/System/Utility 注册进行报告。
  • 添加一个 DiagnosticAnalyzer,从 OnInitialize/InstallModules 和 IArchitectureModule.Install 方法中构建每个架构的注册索引,并沿着 InstallModule/InstallGodotModule 调用链进行分析。
  • 分析字段特性([GetModel(s)]、[GetSystem(s)]、[GetUtility(ies)])以及在支持上下文的类型上的手写 GetX 调用,以创建绑定请求。
  • 通过注册所有权启发式来解析某个使用点的所属架构,当找不到兼容的注册且所属架构是唯一时,发出 GF_ContextRegistration_001/2/3 诊断。
  • 分析仅限于可静态追踪的模式(直接注册、模块调用链、架构/模块的辅助方法),并跳过生成代码以及多架构歧义的情况。
GFramework.SourceGenerators/Analyzers/ContextRegistrationAnalyzer.cs
为新的注册分析器定义诊断信息及发布元数据,并在文档中进行说明。
  • 添加 ContextRegistrationDiagnostics,包含三个用于缺失 Model/System/Utility 注册的 DiagnosticDescriptor,包括消息和分类信息。
  • 在 AnalyzerReleases.Unshipped.md 中以 Warning 严重级别注册新的诊断 ID。
  • 扩展源码生成器文档(zh-CN)和 README,增加一个章节说明注册可见性分析、支持的使用/注册模式及其保守行为。
  • 扩展主源码生成器索引中的诊断表,列出 GF_ContextRegistration_001/2/3 以及对应的使用指导。
GFramework.SourceGenerators/Diagnostics/ContextRegistrationDiagnostics.cs
GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
docs/zh-CN/source-generators/context-get-generator.md
GFramework.SourceGenerators/README.md
docs/zh-CN/source-generators/index.md
为注册分析器添加分析器测试和可复用的分析器测试驱动工具。
  • 新增 ContextRegistrationAnalyzerTests,用于覆盖缺失/存在注册、模块提供的注册、所有权歧义(不报告)、基于特性的集合场景以及手写 GetX 调用等情况。
  • 提供一个预置测试前导代码,定义分析器测试所需的最小框架抽象(IArchitecture、IArchitectureModule、IArchitectureContext、ContextAwareServiceExtensions、GetX 特性等)。
  • 添加 AnalyzerTestDriver 辅助类,封装 CSharpAnalyzerTest,在禁用 trace 分析器的前提下以可配置的预期诊断结果运行分析器。
GFramework.SourceGenerators.Tests/Analyzers/ContextRegistrationAnalyzerTests.cs
GFramework.SourceGenerators.Tests/Core/AnalyzerTest.cs
GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj

提示与命令

与 Sourcery 交互

  • 触发新的代码审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 回复 Sourcery 的审查评论,请其从该评论创建一个 issue。你也可以在审查评论中回复 @sourcery-ai issue 来创建对应的 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 以(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写入 @sourcery-ai summary,即可在该位置生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 以在任意时间(重新)生成摘要。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,以标记所有 Sourcery 评论为已解决。如果你已经处理完所有评论且不想再看到它们,这会非常有用。
  • 撤销所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,以撤销所有现有的 Sourcery 审查。尤其适用于你想从头开始一次新的审查时——别忘了随后评论 @sourcery-ai review 来触发新一轮审查!

自定义你的体验

访问你的 dashboard 可以:

  • 启用或禁用审查功能,例如 Sourcery 自动生成的 pull request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查相关设置。

获取帮助

Original review guide in English

Reviewer's Guide

Adds a new Roslyn analyzer that checks whether Model/System/Utility context-get usage points have corresponding statically discoverable registrations in their architecture, wires its diagnostics into the package, documents the behavior, and covers it with analyzer tests and a small analyzer test harness helper.

Sequence diagram for ContextRegistrationAnalyzer field usage analysis

sequenceDiagram
    participant Compiler as Roslyn_Compiler
    participant Analyzer as ContextRegistrationAnalyzer
    participant Symbols as SymbolCache
    participant Index as RegistrationIndex
    participant Field as AnalyzeField
    participant Diag as ContextRegistrationDiagnostics

    Compiler->>Analyzer: Initialize(context)
    Analyzer->>Analyzer: ConfigureGeneratedCodeAnalysis(None)
    Analyzer->>Analyzer: EnableConcurrentExecution()

    Compiler->>Analyzer: CompilationStartAction(compilationContext)
    Analyzer->>Symbols: Create(compilation)
    Symbols-->>Analyzer: SymbolCache instance
    Analyzer->>Analyzer: Check symbols.IsReady
    alt Symbols ready
        Analyzer->>Analyzer: Create Lazy RegistrationIndex
        Analyzer->>Compiler: RegisterSyntaxNodeAction(AnalyzeField, VariableDeclarator)
        Analyzer->>Compiler: RegisterOperationAction(AnalyzeInvocation, Invocation)
    else Symbols not ready
        Analyzer-->>Compiler: Return (no analysis)
    end

    loop For each field declarator with attributes
        Compiler->>Analyzer: AnalyzeField(SyntaxNodeAnalysisContext)
        Analyzer->>Field: TryCreateBindingRequest(fieldSymbol, location, symbols)
        alt Attribute matches GetModel/GetSystem/GetUtility
            Field-->>Analyzer: BindingRequest(kind, ownerType, serviceType, location)
            Analyzer->>Index: registrationIndex.Value (build if first time)
            Note over Index: Build(compilation, symbols)
            Index->>Index: AnalyzeArchitecture for each Architecture subtype
            Index-->>Analyzer: RegistrationIndex instance

            Analyzer->>Index: TryGetOwningArchitecture(ownerType, out architectureType)
            alt Owning architecture found
                Index-->>Analyzer: architectureType
                Analyzer->>Index: HasRegistration(architectureType, kind, serviceType)
                alt Registration exists
                    Index-->>Analyzer: true
                    Analyzer-->>Compiler: No diagnostic
                else Registration missing
                    Index-->>Analyzer: false
                    Analyzer->>Diag: Select descriptor by kind
                    Diag-->>Analyzer: DiagnosticDescriptor
                    Analyzer-->>Compiler: ReportDiagnostic(GF_ContextRegistration_001/002/003)
                end
            else No unique architecture
                Index-->>Analyzer: false
                Analyzer-->>Compiler: No diagnostic
            end
        else Not a supported attribute
            Field-->>Analyzer: no request
            Analyzer-->>Compiler: Return
        end
    end
Loading

Class diagram for ContextRegistrationAnalyzer and related types

classDiagram
    direction LR

    class ContextRegistrationAnalyzer {
        <<DiagnosticAnalyzer>>
        +SupportedDiagnostics : ImmutableArray~DiagnosticDescriptor~
        +Initialize(context AnalysisContext) void
        -AnalyzeField(context SyntaxNodeAnalysisContext, symbols SymbolCache, registrationIndex RegistrationIndex) void
        -AnalyzeInvocation(context OperationAnalysisContext, symbols SymbolCache, registrationIndex RegistrationIndex) void
        -ReportMissingRegistration(context SyntaxNodeAnalysisContext, registrationIndex RegistrationIndex, request BindingRequest) void
        -ReportMissingRegistration(context OperationAnalysisContext, registrationIndex RegistrationIndex, request BindingRequest) void
        -CreateMissingRegistrationDiagnostic(request BindingRequest, architectureType INamedTypeSymbol) Diagnostic
        -TryCreateBindingRequest(fieldSymbol IFieldSymbol, location Location, symbols SymbolCache, outRequest BindingRequest) bool
        -TryCreateBindingRequest(invocation IInvocationOperation, ownerType INamedTypeSymbol, symbols SymbolCache, outRequest BindingRequest) bool
        -TryMapAttribute(attributeType INamedTypeSymbol, symbols SymbolCache, outKind ComponentKind, outExpectsCollection bool) bool
        -IsSupportedGetInvocationTarget(targetMethod IMethodSymbol, symbols SymbolCache) bool
        -TryGetCollectionElementType(fieldType ITypeSymbol, symbols SymbolCache) INamedTypeSymbol
        -_contextAwareBindingNames : IReadOnlyDictionary~string,ComponentKind~
    }

    class ComponentKind {
        <<enum>>
        Model
        System
        Utility
    }

    class BindingRequest {
        <<record struct>>
        +Kind : ComponentKind
        +OwnerType : INamedTypeSymbol
        +ServiceType : INamedTypeSymbol
        +Location : Location
    }

    class SymbolCache {
        -SymbolCache(architectureType INamedTypeSymbol, iArchitectureType INamedTypeSymbol, iArchitectureModuleType INamedTypeSymbol, iArchitectureContextType INamedTypeSymbol, iReadOnlyListType INamedTypeSymbol, contextAwareServiceExtensionsType INamedTypeSymbol, getModelAttribute INamedTypeSymbol, getModelsAttribute INamedTypeSymbol, getSystemAttribute INamedTypeSymbol, getSystemsAttribute INamedTypeSymbol, getUtilityAttribute INamedTypeSymbol, getUtilitiesAttribute INamedTypeSymbol)
        +ArchitectureType : INamedTypeSymbol
        +IArchitectureType : INamedTypeSymbol
        +IArchitectureModuleType : INamedTypeSymbol
        +IArchitectureContext : INamedTypeSymbol
        +IReadOnlyList : INamedTypeSymbol
        +ContextAwareServiceExtensions : INamedTypeSymbol
        +GetModelAttribute : INamedTypeSymbol
        +GetModelsAttribute : INamedTypeSymbol
        +GetSystemAttribute : INamedTypeSymbol
        +GetSystemsAttribute : INamedTypeSymbol
        +GetUtilityAttribute : INamedTypeSymbol
        +GetUtilitiesAttribute : INamedTypeSymbol
        +IsReady : bool
        +Create(compilation Compilation) SymbolCache
    }

    class RegistrationIndex {
        -_compilation : Compilation
        -_registrations : IReadOnlyDictionary~INamedTypeSymbol,ArchitectureRegistrationData~
        -RegistrationIndex(compilation Compilation, registrations IReadOnlyDictionary~INamedTypeSymbol,ArchitectureRegistrationData~)
        +Build(compilation Compilation, symbols SymbolCache) RegistrationIndex
        +TryGetOwningArchitecture(ownerType INamedTypeSymbol, outArchitectureType INamedTypeSymbol) bool
        +HasRegistration(architectureType INamedTypeSymbol, componentKind ComponentKind, serviceType INamedTypeSymbol) bool
        -AnalyzeArchitecture(compilation Compilation, symbols SymbolCache, architectureType INamedTypeSymbol) ArchitectureRegistrationData
        -AnalyzeModule(compilation Compilation, symbols SymbolCache, moduleType INamedTypeSymbol, data ArchitectureRegistrationData, visitedModules ISet~INamedTypeSymbol~) void
        -GetArchitectureRootMethods(architectureType INamedTypeSymbol) IEnumerable~IMethodSymbol~
        -GetModuleRootMethods(moduleType INamedTypeSymbol) IEnumerable~IMethodSymbol~
        -TryGetInstalledModuleType(invocation IInvocationOperation, symbols SymbolCache, outModuleType INamedTypeSymbol) bool
        -TryGetRegistration(invocation IInvocationOperation, symbols SymbolCache, outKind ComponentKind, outRegisteredType INamedTypeSymbol) bool
        -TryResolveArchitectureHelperMethod(targetMethod IMethodSymbol, architectureType INamedTypeSymbol, outHelperMethod IMethodSymbol) bool
        -TryResolveModuleHelperMethod(targetMethod IMethodSymbol, moduleType INamedTypeSymbol, outHelperMethod IMethodSymbol) bool
    }

    class ArchitectureRegistrationData {
        -_models : HashSet~INamedTypeSymbol~
        -_systems : HashSet~INamedTypeSymbol~
        -_utilities : HashSet~INamedTypeSymbol~
        +IsEmpty : bool
        +Add(componentKind ComponentKind, registeredType INamedTypeSymbol) void
        +HasRegistration(componentKind ComponentKind, requestedType INamedTypeSymbol, compilation Compilation) bool
        +ContainsOwner(ownerType INamedTypeSymbol, compilation Compilation) bool
        -GetCollection(componentKind ComponentKind) ISet~INamedTypeSymbol~
    }

    class SymbolHelpers {
        <<static>>
        +EnumerateNamedTypes(namespaceSymbol INamespaceSymbol) IEnumerable~INamedTypeSymbol~
        +EnumerateTypeHierarchy(type INamedTypeSymbol) IEnumerable~INamedTypeSymbol~
        +IsAssignableTo(fromType ITypeSymbol, toType ITypeSymbol) bool
        +IsWithinTypeHierarchy(candidateType INamedTypeSymbol, ownerType INamedTypeSymbol) bool
        +ResolveHierarchyMethodImplementation(method IMethodSymbol, ownerType INamedTypeSymbol) IMethodSymbol
        +GetInvocationOperations(method IMethodSymbol, compilation Compilation) IEnumerable~IInvocationOperation~
        +TryGetCreatedType(operation IOperation) INamedTypeSymbol
        +IsServiceCompatible(registeredType INamedTypeSymbol, requestedType INamedTypeSymbol, compilation Compilation) bool
        +IsOwnershipCompatible(ownerType INamedTypeSymbol, registeredType INamedTypeSymbol, compilation Compilation) bool
    }

    class ContextRegistrationDiagnostics {
        <<static>>
        +ModelRegistrationMissing : DiagnosticDescriptor
        +SystemRegistrationMissing : DiagnosticDescriptor
        +UtilityRegistrationMissing : DiagnosticDescriptor
    }

    class DiagnosticAnalyzer {
    }

    class DiagnosticDescriptor {
    }

    class AnalyzerTestDriver_TAnalyzer_ {
        <<static>>
        +RunAsync(source string, diagnostics DiagnosticResult[]) Task
    }

    class CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_ {
        +TestState : TestStateType
        +DisabledDiagnostics : ICollection~string~
        +ExpectedDiagnostics : IList~DiagnosticResult~
        +RunAsync() Task
    }

    class DiagnosticResult {
    }

    %% Relationships
    ContextRegistrationAnalyzer --|> DiagnosticAnalyzer
    ContextRegistrationAnalyzer *-- SymbolCache
    ContextRegistrationAnalyzer *-- RegistrationIndex
    ContextRegistrationAnalyzer o-- BindingRequest
    ContextRegistrationAnalyzer ..> ComponentKind
    ContextRegistrationAnalyzer ..> ContextRegistrationDiagnostics

    RegistrationIndex *-- ArchitectureRegistrationData
    RegistrationIndex ..> SymbolHelpers
    RegistrationIndex ..> ComponentKind

    ArchitectureRegistrationData ..> ComponentKind
    ArchitectureRegistrationData ..> SymbolHelpers

    SymbolCache ..> PathContests

    ContextRegistrationDiagnostics ..> DiagnosticDescriptor

    AnalyzerTestDriver_TAnalyzer_ ..> CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_
    AnalyzerTestDriver_TAnalyzer_ ..> DiagnosticResult

    CSharpAnalyzerTest_TAnalyzer_DefaultVerifier_ ..> AnalyzerTestDriver_TAnalyzer_
Loading

File-Level Changes

Change Details Files
Introduce ContextRegistrationAnalyzer to track registrations across architectures/modules and report missing Model/System/Utility registrations for context-get usage points.
  • Add a DiagnosticAnalyzer that builds a per-architecture registration index from OnInitialize/InstallModules and IArchitectureModule.Install methods, following InstallModule/InstallGodotModule chains.
  • Analyze field attributes ([GetModel(s)], [GetSystem(s)], [GetUtility(ies)]) and handwritten GetX calls on context-aware types to create binding requests.
  • Resolve owning architecture for a usage via registration ownership heuristics, and emit diagnostics GF_ContextRegistration_001/2/3 when no compatible registration is found and ownership is unique.
  • Limit analysis to statically traceable patterns (direct registrations, module chains, architecture/module helper methods) and skip generated code and ambiguous multi-architecture cases.
GFramework.SourceGenerators/Analyzers/ContextRegistrationAnalyzer.cs
Define diagnostics and release metadata for the new registration analyzer and describe it in the documentation.
  • Add ContextRegistrationDiagnostics with three DiagnosticDescriptors for missing Model/System/Utility registrations, including messages and category.
  • Register the new diagnostic IDs in AnalyzerReleases.Unshipped.md with Warning severity.
  • Extend the source generators docs (zh-CN) and README with a new section explaining registration visibility analysis, supported usage/registration patterns, and its conservative behavior.
  • Extend the diagnostics table in the main source generators index to list GF_ContextRegistration_001/2/3 and their guidance.
GFramework.SourceGenerators/Diagnostics/ContextRegistrationDiagnostics.cs
GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
docs/zh-CN/source-generators/context-get-generator.md
GFramework.SourceGenerators/README.md
docs/zh-CN/source-generators/index.md
Add analyzer tests and a reusable analyzer test driver for the registration analyzer.
  • Introduce ContextRegistrationAnalyzerTests covering missing and present registrations, module-provided registrations, ambiguous ownership (no report), attribute-based collections, and handwritten GetX calls.
  • Provide a canned test preamble that defines minimal framework abstractions (IArchitecture, IArchitectureModule, IArchitectureContext, ContextAwareServiceExtensions, GetX attributes, etc.) for use in analyzer tests.
  • Add AnalyzerTestDriver helper that wraps CSharpAnalyzerTest to run diagnostics with a disabled trace analyzer and configurable expected diagnostics.
GFramework.SourceGenerators.Tests/Analyzers/ContextRegistrationAnalyzerTests.cs
GFramework.SourceGenerators.Tests/Core/AnalyzerTest.cs
GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@GeWuYou

GeWuYou commented Apr 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

嗨 - 我发现了 1 个问题

给 AI Agent 的提示
Please address the comments from this code review:

## 单独评论

### 评论 1
<location path="docs/zh-CN/source-generators/context-get-generator.md" line_range="645" />
<code_context>
+- `GF_ContextRegistration_002``System` 使用点未找到静态可见注册
+- `GF_ContextRegistration_003``Utility` 使用点未找到静态可见注册
+
+这个分析器的目标是提供稳定提示,而不是完整模拟运行时 DI 图。以下场景默认不做强推断:
+
+- 运行时条件分支控制的注册
</code_context>
<issue_to_address>
**建议(拼写):** 建议将“强推断”改为“强行推断”,与 README 中的描述保持一致并使表述更自然。

README 的“注册分析器”章节已经采用“不会强行推断”的表述,这里建议改为“以下场景默认不做强行推断:”,以统一术语并使语句更自然。

```suggestion
这个分析器的目标是提供稳定提示,而不是完整模拟运行时 DI 图。以下场景默认不做强行推断:
```
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="docs/zh-CN/source-generators/context-get-generator.md" line_range="645" />
<code_context>
+- `GF_ContextRegistration_002``System` 使用点未找到静态可见注册
+- `GF_ContextRegistration_003``Utility` 使用点未找到静态可见注册
+
+这个分析器的目标是提供稳定提示,而不是完整模拟运行时 DI 图。以下场景默认不做强推断:
+
+- 运行时条件分支控制的注册
</code_context>
<issue_to_address>
**suggestion (typo):** 建议将“强推断”改为“强行推断”,与 README 中的描述保持一致并使表述更自然。

README 的“注册分析器”章节已经采用“不会强行推断”的表述,这里建议改为“以下场景默认不做强行推断:”,以统一术语并使语句更自然。

```suggestion
这个分析器的目标是提供稳定提示,而不是完整模拟运行时 DI 图。以下场景默认不做强行推断:
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/zh-CN/source-generators/context-get-generator.md Outdated
- 将“强推断”更正为“强行推断”以提高表述准确性

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8eaf837327

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +667 to +669
helperMethod = targetMethod.DeclaringSyntaxReferences.Length > 0 && !targetMethod.IsAbstract
? targetMethod
: SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, architectureType) ?? targetMethod;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve overridden architecture helpers during call traversal

When a root method (for example an inherited OnInitialize) calls a virtual helper, this branch keeps the base targetMethod whenever it has source, instead of resolving the implementation on the concrete architectureType. In architectures that register components only in derived overrides, those registrations are missed and the analyzer emits false GF_ContextRegistration_* warnings for valid GetModel/GetSystem/GetUtility usage.

Useful? React with 👍 / 👎.

Comment on lines +686 to +688
helperMethod = targetMethod.DeclaringSyntaxReferences.Length > 0 && !targetMethod.IsAbstract
? targetMethod
: SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, moduleType) ?? targetMethod;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Follow overridden module helpers when analyzing Install paths

This has the same override-resolution problem for modules: if Install(...) is inherited from a base module and delegates to a virtual helper, keeping the base method symbol here skips registrations that are implemented only in the derived override. As a result, InstallModule(new DerivedModule()) can be analyzed as if it registered nothing, producing false missing-registration warnings downstream.

Useful? React with 👍 / 👎.

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.

1 participant