Feat/context aware injection generator#147
Conversation
- 移除ContextAwareServiceExtensions中GetService/GetSystem/GetModel/GetUtility方法的可空返回值 - 添加ContextGetGenerator源码生成器,支持通过特性自动生成上下文注入代码 - 新增GetService/GetServices/GetSystem/GetSystems/GetModel/GetModels/GetUtility/GetUtilities/GetAll特性 - 添加ContextGetDiagnostics提供注入相关的编译时诊断检查 - 实现INamedTypeSymbol扩展方法AreAllDeclarationsPartial用于检查partial类声明 - 添加ITypeSymbol扩展方法IsAssignableTo用于类型兼容性判断 - 创建FieldCandidateInfo和TypeCandidateInfo记录类型用于存储生成器候选信息 - 添加IsExternalInit内部类型支持低版本.NET框架的init-only setter功能 - 更新AnalyzerReleases.Unshipped.md添加新的诊断规则条目 - 创建完整的单元测试验证生成器功能和各种边界情况
- 将 ContextSymbols 的创建提取为独立方法 CreateContextSymbols - 将源码生成逻辑提取为独立方法 GenerateSources - 将绑定收集逻辑提取为独立方法 CollectBindings - 将显式绑定添加逻辑提取为独立方法 AddExplicitBindings - 将推断绑定添加逻辑提取为独立方法 AddInferredBindings - 将绑定推断检查逻辑提取为独立方法 CanInferBinding - 优化了代码组织结构和可读性
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| C# | Mar 28, 2026 5:06a.m. | Review ↗ | |
| Secrets | Mar 28, 2026 5:06a.m. | Review ↗ |
审阅者指南实现了一个新的 Roslyn 增量源生成器,用于为带注解的类自动生成具备上下文感知能力的 Get* 注入方法;同时引入了相应的公共 Attribute、诊断信息、辅助工具和测试;并通过将 Get* 方法改为非可空返回值以及增强符号工具,略微收紧了核心的上下文感知扩展 API。 运行时 Context Get 注入用法的时序图sequenceDiagram
participant InventoryPanel
participant InjectMethod
participant ContextExtensions
participant Context
InventoryPanel->>InjectMethod: __InjectContextBindings_Generated()
InjectMethod->>ContextExtensions: GetModel~IInventoryModel~(InventoryPanel)
ContextExtensions->>Context: resolve IInventoryModel
Context-->>ContextExtensions: IInventoryModel (non-null)
ContextExtensions-->>InjectMethod: IInventoryModel (non-null)
InjectMethod->>InventoryPanel: assign _model
InjectMethod->>ContextExtensions: GetServices~IInventoryStrategy~(InventoryPanel)
ContextExtensions->>Context: resolve IReadOnlyList~IInventoryStrategy~
Context-->>ContextExtensions: IReadOnlyList~IInventoryStrategy~
ContextExtensions-->>InjectMethod: IReadOnlyList~IInventoryStrategy~
InjectMethod->>InventoryPanel: assign _strategies
ContextGetGenerator 及相关类型的类图classDiagram
class ContextGetGenerator {
+Initialize(context IncrementalGeneratorInitializationContext) void
-IsFieldCandidate(node SyntaxNode) bool
-TransformField(context GeneratorSyntaxContext) FieldCandidateInfo
-IsTypeCandidate(node SyntaxNode) bool
-TransformType(context GeneratorSyntaxContext) TypeCandidateInfo
-Execute(context SourceProductionContext, compilation Compilation, fieldCandidates ImmutableArray~FieldCandidateInfo~, typeCandidates ImmutableArray~TypeCandidateInfo~) void
-GenerateSources(context SourceProductionContext, descriptors ImmutableArray~ResolvedBindingDescriptor~, symbols ContextSymbols, workItems Dictionary~INamedTypeSymbol, TypeWorkItem~) void
-CollectBindings(context SourceProductionContext, workItem TypeWorkItem, descriptors ImmutableArray~ResolvedBindingDescriptor~, symbols ContextSymbols) List~BindingInfo~
-CanGenerateForType(context SourceProductionContext, workItem TypeWorkItem, symbols ContextSymbols) bool
-GenerateSource(typeSymbol INamedTypeSymbol, bindings IReadOnlyList~BindingInfo~) string
-GetHintName(typeSymbol INamedTypeSymbol) string
}
class BindingKind {
<<enumeration>>
Service
Services
System
Systems
Model
Models
Utility
Utilities
}
class BindingDescriptor {
+Kind BindingKind
+MetadataName string
+AttributeName string
+IsCollection bool
}
class ResolvedBindingDescriptor {
+Definition BindingDescriptor
+AttributeSymbol INamedTypeSymbol
}
class BindingInfo {
+Field IFieldSymbol
+Kind BindingKind
+TargetType ITypeSymbol
}
class ContextSymbols {
+ContextAwareAttribute INamedTypeSymbol
+IContextAware INamedTypeSymbol
+ContextAwareBase INamedTypeSymbol
+IModel INamedTypeSymbol
+ISystem INamedTypeSymbol
+IUtility INamedTypeSymbol
+IReadOnlyList INamedTypeSymbol
+GodotNode INamedTypeSymbol
}
class TypeWorkItem {
+TypeSymbol INamedTypeSymbol
+FieldCandidates List~FieldCandidateInfo~
+GetAllDeclaration ClassDeclarationSyntax
}
class FieldCandidateInfo {
+Variable VariableDeclaratorSyntax
+FieldSymbol IFieldSymbol
}
class TypeCandidateInfo {
+Declaration ClassDeclarationSyntax
+TypeSymbol INamedTypeSymbol
}
class ContextGetDiagnostics {
<<static>>
+NestedClassNotSupported DiagnosticDescriptor
+StaticFieldNotSupported DiagnosticDescriptor
+ReadOnlyFieldNotSupported DiagnosticDescriptor
+InvalidBindingType DiagnosticDescriptor
+ContextAwareTypeRequired DiagnosticDescriptor
+MultipleBindingAttributesNotSupported DiagnosticDescriptor
}
class ITypeSymbolExtensions {
+IsAssignableTo(typeSymbol ITypeSymbol, targetType INamedTypeSymbol) bool
}
class INamedTypeSymbolExtensions {
+AreAllDeclarationsPartial(symbol INamedTypeSymbol) bool
+GetFullClassName(symbol INamedTypeSymbol) string
+GetNamespace(symbol INamedTypeSymbol) string
}
class GetAllAttribute {
<<attribute>>
}
class GetServiceAttribute {
<<attribute>>
}
class GetServicesAttribute {
<<attribute>>
}
class GetSystemAttribute {
<<attribute>>
}
class GetSystemsAttribute {
<<attribute>>
}
class GetModelAttribute {
<<attribute>>
}
class GetModelsAttribute {
<<attribute>>
}
class GetUtilityAttribute {
<<attribute>>
}
class GetUtilitiesAttribute {
<<attribute>>
}
ContextGetGenerator --> FieldCandidateInfo : uses
ContextGetGenerator --> TypeCandidateInfo : uses
ContextGetGenerator --> BindingDescriptor : uses
ContextGetGenerator --> ResolvedBindingDescriptor : uses
ContextGetGenerator --> BindingInfo : uses
ContextGetGenerator --> ContextSymbols : uses
ContextGetGenerator --> TypeWorkItem : aggregates
ContextGetGenerator --> ContextGetDiagnostics : reports
ContextGetGenerator --> ITypeSymbolExtensions : calls
ContextGetGenerator --> INamedTypeSymbolExtensions : calls
TypeWorkItem --> FieldCandidateInfo : contains
FieldCandidateInfo --> IFieldSymbol : wraps
TypeCandidateInfo --> INamedTypeSymbol : wraps
GetAllAttribute <.. ContextGetGenerator : reflected
GetServiceAttribute <.. ContextGetGenerator : reflected
GetServicesAttribute <.. ContextGetGenerator : reflected
GetSystemAttribute <.. ContextGetGenerator : reflected
GetSystemsAttribute <.. ContextGetGenerator : reflected
GetModelAttribute <.. ContextGetGenerator : reflected
GetModelsAttribute <.. ContextGetGenerator : reflected
GetUtilityAttribute <.. ContextGetGenerator : reflected
GetUtilitiesAttribute <.. ContextGetGenerator : reflected
ContextGetGenerator 增量流水线流程图flowchart TD
A[来自编译的语法节点] --> B[筛选带有 Get* Attribute 的字段节点]
A --> C[筛选带有 GetAll Attribute 的类节点]
B --> D[使用 SemanticModel 转换为 FieldCandidateInfo]
C --> E[使用 SemanticModel 转换为 TypeCandidateInfo]
D --> F[收集字段候选]
E --> G[收集类型候选]
F --> H[与 Compilation 合并]
G --> H
H --> I[根据 Attribute 元数据解析 BindingDescriptor]
H --> J[为具备上下文感知能力的类型创建 ContextSymbols]
I --> K[按包含类型收集 TypeWorkItem]
G --> K
F --> K
K --> L[验证类型:非嵌套、partial、具备上下文感知能力]
L --> M{能为该类型生成吗?}
M -- No --> N[报告诊断并跳过]
M -- Yes --> O[收集显式绑定]
O --> P[为 GetAll 收集推断绑定]
P --> Q{是否存在任何绑定或 GetAll?}
Q -- No --> R[跳过该类型的代码生成]
Q -- Yes --> S[生成 __InjectContextBindings_Generated 方法]
S --> T[将生成的源码添加到编译中]
文件级变更
提示与命令与 Sourcery 交互
自定义你的体验访问你的 控制面板 以:
获取帮助Original review guide in EnglishReviewer's GuideImplements a new Roslyn incremental source generator that auto-generates context-aware Get* injection methods for annotated classes, introduces the corresponding public attributes, diagnostics, helpers, and tests, and slightly tightens the core context-aware extension API by making Get* methods non-nullable and enhancing symbol utilities. Sequence diagram for runtime Context Get injection usagesequenceDiagram
participant InventoryPanel
participant InjectMethod
participant ContextExtensions
participant Context
InventoryPanel->>InjectMethod: __InjectContextBindings_Generated()
InjectMethod->>ContextExtensions: GetModel~IInventoryModel~(InventoryPanel)
ContextExtensions->>Context: resolve IInventoryModel
Context-->>ContextExtensions: IInventoryModel (non-null)
ContextExtensions-->>InjectMethod: IInventoryModel (non-null)
InjectMethod->>InventoryPanel: assign _model
InjectMethod->>ContextExtensions: GetServices~IInventoryStrategy~(InventoryPanel)
ContextExtensions->>Context: resolve IReadOnlyList~IInventoryStrategy~
Context-->>ContextExtensions: IReadOnlyList~IInventoryStrategy~
ContextExtensions-->>InjectMethod: IReadOnlyList~IInventoryStrategy~
InjectMethod->>InventoryPanel: assign _strategies
Class diagram for ContextGetGenerator and related typesclassDiagram
class ContextGetGenerator {
+Initialize(context IncrementalGeneratorInitializationContext) void
-IsFieldCandidate(node SyntaxNode) bool
-TransformField(context GeneratorSyntaxContext) FieldCandidateInfo
-IsTypeCandidate(node SyntaxNode) bool
-TransformType(context GeneratorSyntaxContext) TypeCandidateInfo
-Execute(context SourceProductionContext, compilation Compilation, fieldCandidates ImmutableArray~FieldCandidateInfo~, typeCandidates ImmutableArray~TypeCandidateInfo~) void
-GenerateSources(context SourceProductionContext, descriptors ImmutableArray~ResolvedBindingDescriptor~, symbols ContextSymbols, workItems Dictionary~INamedTypeSymbol, TypeWorkItem~) void
-CollectBindings(context SourceProductionContext, workItem TypeWorkItem, descriptors ImmutableArray~ResolvedBindingDescriptor~, symbols ContextSymbols) List~BindingInfo~
-CanGenerateForType(context SourceProductionContext, workItem TypeWorkItem, symbols ContextSymbols) bool
-GenerateSource(typeSymbol INamedTypeSymbol, bindings IReadOnlyList~BindingInfo~) string
-GetHintName(typeSymbol INamedTypeSymbol) string
}
class BindingKind {
<<enumeration>>
Service
Services
System
Systems
Model
Models
Utility
Utilities
}
class BindingDescriptor {
+Kind BindingKind
+MetadataName string
+AttributeName string
+IsCollection bool
}
class ResolvedBindingDescriptor {
+Definition BindingDescriptor
+AttributeSymbol INamedTypeSymbol
}
class BindingInfo {
+Field IFieldSymbol
+Kind BindingKind
+TargetType ITypeSymbol
}
class ContextSymbols {
+ContextAwareAttribute INamedTypeSymbol
+IContextAware INamedTypeSymbol
+ContextAwareBase INamedTypeSymbol
+IModel INamedTypeSymbol
+ISystem INamedTypeSymbol
+IUtility INamedTypeSymbol
+IReadOnlyList INamedTypeSymbol
+GodotNode INamedTypeSymbol
}
class TypeWorkItem {
+TypeSymbol INamedTypeSymbol
+FieldCandidates List~FieldCandidateInfo~
+GetAllDeclaration ClassDeclarationSyntax
}
class FieldCandidateInfo {
+Variable VariableDeclaratorSyntax
+FieldSymbol IFieldSymbol
}
class TypeCandidateInfo {
+Declaration ClassDeclarationSyntax
+TypeSymbol INamedTypeSymbol
}
class ContextGetDiagnostics {
<<static>>
+NestedClassNotSupported DiagnosticDescriptor
+StaticFieldNotSupported DiagnosticDescriptor
+ReadOnlyFieldNotSupported DiagnosticDescriptor
+InvalidBindingType DiagnosticDescriptor
+ContextAwareTypeRequired DiagnosticDescriptor
+MultipleBindingAttributesNotSupported DiagnosticDescriptor
}
class ITypeSymbolExtensions {
+IsAssignableTo(typeSymbol ITypeSymbol, targetType INamedTypeSymbol) bool
}
class INamedTypeSymbolExtensions {
+AreAllDeclarationsPartial(symbol INamedTypeSymbol) bool
+GetFullClassName(symbol INamedTypeSymbol) string
+GetNamespace(symbol INamedTypeSymbol) string
}
class GetAllAttribute {
<<attribute>>
}
class GetServiceAttribute {
<<attribute>>
}
class GetServicesAttribute {
<<attribute>>
}
class GetSystemAttribute {
<<attribute>>
}
class GetSystemsAttribute {
<<attribute>>
}
class GetModelAttribute {
<<attribute>>
}
class GetModelsAttribute {
<<attribute>>
}
class GetUtilityAttribute {
<<attribute>>
}
class GetUtilitiesAttribute {
<<attribute>>
}
ContextGetGenerator --> FieldCandidateInfo : uses
ContextGetGenerator --> TypeCandidateInfo : uses
ContextGetGenerator --> BindingDescriptor : uses
ContextGetGenerator --> ResolvedBindingDescriptor : uses
ContextGetGenerator --> BindingInfo : uses
ContextGetGenerator --> ContextSymbols : uses
ContextGetGenerator --> TypeWorkItem : aggregates
ContextGetGenerator --> ContextGetDiagnostics : reports
ContextGetGenerator --> ITypeSymbolExtensions : calls
ContextGetGenerator --> INamedTypeSymbolExtensions : calls
TypeWorkItem --> FieldCandidateInfo : contains
FieldCandidateInfo --> IFieldSymbol : wraps
TypeCandidateInfo --> INamedTypeSymbol : wraps
GetAllAttribute <.. ContextGetGenerator : reflected
GetServiceAttribute <.. ContextGetGenerator : reflected
GetServicesAttribute <.. ContextGetGenerator : reflected
GetSystemAttribute <.. ContextGetGenerator : reflected
GetSystemsAttribute <.. ContextGetGenerator : reflected
GetModelAttribute <.. ContextGetGenerator : reflected
GetModelsAttribute <.. ContextGetGenerator : reflected
GetUtilityAttribute <.. ContextGetGenerator : reflected
GetUtilitiesAttribute <.. ContextGetGenerator : reflected
Flow diagram for ContextGetGenerator incremental pipelineflowchart TD
A[Syntax nodes from compilation] --> B[Filter field nodes with Get* attributes]
A --> C[Filter class nodes with GetAll attribute]
B --> D[Transform to FieldCandidateInfo using SemanticModel]
C --> E[Transform to TypeCandidateInfo using SemanticModel]
D --> F[Collect field candidates]
E --> G[Collect type candidates]
F --> H[Combine with Compilation]
G --> H
H --> I[Resolve BindingDescriptors from attribute metadata]
H --> J[Create ContextSymbols for context aware types]
I --> K[Collect TypeWorkItem per containing type]
G --> K
F --> K
K --> L[Validate type: non nested, partial, context aware]
L --> M{Can generate for type?}
M -- No --> N[Report diagnostics and skip]
M -- Yes --> O[Collect explicit bindings]
O --> P[Collect inferred bindings for GetAll]
P --> Q{Any bindings or GetAll?}
Q -- No --> R[Skip generation for type]
Q -- Yes --> S[Generate __InjectContextBindings_Generated method]
S --> T[Add generated source to compilation]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些高层次的反馈:
IsFieldCandidate/IsTypeCandidate中的特性检测当前依赖于attribute.Name.ToString().Contains("Get")/"GetAll"),这种方式比较脆弱(别名、限定名或无关的特性也可能命中);建议改为基于语义、针对已知特性符号进行检查。TryResolveCollectionElement目前只识别IReadOnlyList<T>这一种集合形态,因此像[GetModels]这样的特性在数组或其它可赋值的列表接口上将无法工作;如果这不是有意为之,建议遍历已实现的接口(如AllInterfaces),以支持更宽泛的集合类型。IsExternalInitshim 在GFramework.SourceGenerators.Common和GFramework.SourceGenerators中都有一份重复实现;建议抽取到一个共享文件或项目中,以避免出现差异,并将该兼容性补丁集中维护在单一位置。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The attribute detection in `IsFieldCandidate`/`IsTypeCandidate` currently relies on `attribute.Name.ToString().Contains("Get")/"GetAll")`, which is brittle (aliasing, qualification, or unrelated attributes can match); consider switching to semantic checks against the known attribute symbols instead.
- `TryResolveCollectionElement` only recognizes `IReadOnlyList<T>` as the collection shape, so attributes like `[GetModels]` won’t work with arrays or other list interfaces even if they are assignable; if that’s not intentional, you may want to inspect implemented interfaces (e.g., `AllInterfaces`) to allow a broader set of collection types.
- The `IsExternalInit` shim is duplicated in both `GFramework.SourceGenerators.Common` and `GFramework.SourceGenerators`; consider factoring this into a shared file or project to avoid divergence and keep the workaround in a single place.
## Individual Comments
### Comment 1
<location path="GFramework.Core/Extensions/ContextAwareServiceExtensions.cs" line_range="23" />
<code_context>
/// <returns>指定类型的服务实例,如果未找到则返回 null</returns>
/// <exception cref="ArgumentNullException">当 contextAware 参数为 null 时抛出</exception>
- public static TService? GetService<TService>(this IContextAware contextAware) where TService : class
+ public static TService GetService<TService>(this IContextAware contextAware) where TService : class
{
ArgumentNullException.ThrowIfNull(contextAware);
</code_context>
<issue_to_address>
**issue (bug_risk):** GetService 的可空性契约现在声称永远不会返回 null,这可能与底层上下文实现不一致。
将返回类型从 `TService?` 修改为 `TService` 表示调用方现在会假设该方法永远不会返回 null。除非 `context.GetService<TService>()` 被保证要么返回服务实例,要么抛出异常,否则这会与实际行为不符,进而导致隐藏的 null 和 NRE。可以选择保持可空返回类型,或者显式使用 `!` 抑制并在文档中声明该保证,或者在服务缺失时抛出异常以强制保证非空。
</issue_to_address>
### Comment 2
<location path="GFramework.SourceGenerators/Rule/ContextGetGenerator.cs" line_range="128-130" />
<code_context>
+ })
+ return false;
+
+ return fieldDeclaration.AttributeLists
+ .SelectMany(static list => list.Attributes)
+ .Any(static attribute => attribute.Name.ToString().Contains("Get", StringComparison.Ordinal));
+ }
+
</code_context>
<issue_to_address>
**suggestion:** 使用字符串 Contains("Get") 来根据特性名预筛选候选项比较脆弱,且可能产生误报。
该谓词会将名称文本包含 "Get" 的任意特性都标记为候选,这会无意中包含不相关的特性(例如 `[Target]`、`[ForgetMe]`,或者只在命名空间别名中包含 "Get" 的情况)。虽然后续基于符号的过滤能保证正确性,但会增加工作量,也可能带来令人意外的匹配结果。可以考虑仅限制在简单标识符名上(例如 `attribute.Name is IdentifierNameSyntax id && id.Identifier.ValueText.StartsWith("Get", ...)`),或者只匹配一组已知的特性标识符,以避免偶然命中并减少增量管线中的噪音。
```suggestion
return fieldDeclaration.AttributeLists
.SelectMany(static list => list.Attributes)
.Any(static attribute =>
attribute.Name is IdentifierNameSyntax id &&
id.Identifier.ValueText.StartsWith("Get", StringComparison.Ordinal));
```
</issue_to_address>Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The attribute detection in
IsFieldCandidate/IsTypeCandidatecurrently relies onattribute.Name.ToString().Contains("Get")/"GetAll"), which is brittle (aliasing, qualification, or unrelated attributes can match); consider switching to semantic checks against the known attribute symbols instead. TryResolveCollectionElementonly recognizesIReadOnlyList<T>as the collection shape, so attributes like[GetModels]won’t work with arrays or other list interfaces even if they are assignable; if that’s not intentional, you may want to inspect implemented interfaces (e.g.,AllInterfaces) to allow a broader set of collection types.- The
IsExternalInitshim is duplicated in bothGFramework.SourceGenerators.CommonandGFramework.SourceGenerators; consider factoring this into a shared file or project to avoid divergence and keep the workaround in a single place.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The attribute detection in `IsFieldCandidate`/`IsTypeCandidate` currently relies on `attribute.Name.ToString().Contains("Get")/"GetAll")`, which is brittle (aliasing, qualification, or unrelated attributes can match); consider switching to semantic checks against the known attribute symbols instead.
- `TryResolveCollectionElement` only recognizes `IReadOnlyList<T>` as the collection shape, so attributes like `[GetModels]` won’t work with arrays or other list interfaces even if they are assignable; if that’s not intentional, you may want to inspect implemented interfaces (e.g., `AllInterfaces`) to allow a broader set of collection types.
- The `IsExternalInit` shim is duplicated in both `GFramework.SourceGenerators.Common` and `GFramework.SourceGenerators`; consider factoring this into a shared file or project to avoid divergence and keep the workaround in a single place.
## Individual Comments
### Comment 1
<location path="GFramework.Core/Extensions/ContextAwareServiceExtensions.cs" line_range="23" />
<code_context>
/// <returns>指定类型的服务实例,如果未找到则返回 null</returns>
/// <exception cref="ArgumentNullException">当 contextAware 参数为 null 时抛出</exception>
- public static TService? GetService<TService>(this IContextAware contextAware) where TService : class
+ public static TService GetService<TService>(this IContextAware contextAware) where TService : class
{
ArgumentNullException.ThrowIfNull(contextAware);
</code_context>
<issue_to_address>
**issue (bug_risk):** The nullability contract of GetService now claims to never return null, which may be inconsistent with the underlying context implementation.
Changing the return type from `TService?` to `TService` means callers now assume this method never returns null. Unless `context.GetService<TService>()` is guaranteed to either return a service or throw, this mismatches the actual behavior and can lead to hidden nulls and NREs. Either keep the nullable return type, explicitly suppress with `!` and document the guarantee, or enforce non-null by throwing when the service is missing.
</issue_to_address>
### Comment 2
<location path="GFramework.SourceGenerators/Rule/ContextGetGenerator.cs" line_range="128-130" />
<code_context>
+ })
+ return false;
+
+ return fieldDeclaration.AttributeLists
+ .SelectMany(static list => list.Attributes)
+ .Any(static attribute => attribute.Name.ToString().Contains("Get", StringComparison.Ordinal));
+ }
+
</code_context>
<issue_to_address>
**suggestion:** Using string Contains("Get") on attribute names to preselect candidates is fragile and may yield false positives.
This predicate marks any attribute whose name text contains "Get" as a candidate, which can unintentionally include unrelated attributes (e.g., `[Target]`, `[ForgetMe]`, or names with "Get" only in a namespace alias). While later symbol filtering preserves correctness, it increases work and may lead to surprising matches. Consider restricting this to the simple identifier name (e.g., `attribute.Name is IdentifierNameSyntax id && id.Identifier.ValueText.StartsWith("Get", ...)`) or matching only a known set of attribute identifiers to avoid incidental matches and reduce noise in the incremental pipeline.
```suggestion
return fieldDeclaration.AttributeLists
.SelectMany(static list => list.Attributes)
.Any(static attribute =>
attribute.Name is IdentifierNameSyntax id &&
id.Identifier.ValueText.StartsWith("Get", StringComparison.Ordinal));
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- 修改GetService方法文档,将返回值描述从"返回null"改为"抛出异常" - 为GetService方法添加InvalidOperationException异常说明 - 删除冗余的IsExternalInit类文件 - 重构属性匹配逻辑,使用预定义集合进行候选属性名称验证 - 添加辅助方法HasCandidateAttribute、TryGetAttributeSimpleName等提升代码可读性 - 改进集合类型推断逻辑,支持接口类型的遍历匹配 - 更新单元测试以验证完全限定名属性和只读列表类型的支持 - 修正诊断错误位置信息的准确性
- 引入通用的 GetRequiredComponent 方法来统一服务、系统、模型和工具的获取逻辑 - 添加对架构上下文组件的空值检查和异常处理 - 实现更清晰的错误消息以指示未注册的组件类型 - 为所有组件类型(服务、系统、模型、工具)添加缺失的单元测试 - 测试验证当上下文返回空组件时抛出正确的 InvalidOperationException - 改进代码可维护性并减少重复的获取组件逻辑
- 为 GetSystem 方法添加 InvalidOperationException 异常文档说明 - 为 GetModel 方法添加 InvalidOperationException 异常文档说明 - 为 GetUtility 方法添加 InvalidOperationException 异常文档说明 - 清理文件末尾多余空行
Summary by Sourcery
引入一个源码生成器,用于为符合条件的类自动生成与上下文相关的注入方法,并将其接入诊断和核心 API。
新特性:
GetService/GetSystem/GetModel/GetUtility及其集合变体,以及GetAll)。ContextGetGenerator增量源码生成器,用于合成一个上下文注入方法,根据特性或类型推断将字段绑定到上下文中的服务(services)、系统(systems)、模型(models)和工具(utilities)。缺陷修复:
ContextAwareServiceExtensions中的上下文感知服务访问器对服务、系统、模型和工具返回非可空类型。增强项:
INamedTypeSymbol工具,添加对部分类声明的检查,以及用于生成器输出的命名空间/类名辅助方法。netstandard兼容的IsExternalInitshim,以支持 init-only 和 record 特性。文档:
Get注入生成器的说明、使用模式以及基于特性的配置。测试:
ContextGetGenerator添加单元测试套件,覆盖显式特性绑定、GetAll推断、IContextAware使用,以及针对非法字段类型和非上下文感知类的错误诊断。Original summary in English
Summary by Sourcery
Introduce a source generator that auto-generates context-aware injection methods for eligible classes and wire it into the diagnostics and core APIs.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: