Skip to content

fix: 启用 Source Generator 程序集级自动发现 - #410

Merged
latte-gh merged 2 commits into
masterfrom
fix/assembly-level-auto-discovery
Aug 11, 2026
Merged

fix: 启用 Source Generator 程序集级自动发现#410
latte-gh merged 2 commits into
masterfrom
fix/assembly-level-auto-discovery

Conversation

@liuhaoyang

Copy link
Copy Markdown
Member

问题

[assembly: AspectCoreGenerateProxy] 程序集级自动发现功能实际是失效的。Execute 入口对每个候选无条件执行 attrData is null → continue(AspectCoreProxyGenerator.cs:184-188),而自动发现产出的类型不带类型级特性,全部被丢弃 → 生成器不产出任何代理。source-generator.mdExecute 注释均宣称支持该能力,但从未有测试或使用者触发过(全仓库 grep 不到 [assembly: AspectCoreGenerateProxy] 的真实调用方)。

改动

src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs:

  1. 引入 Candidate(Type, Explicit) 来源标记,将候选数据流拆分为显式标注与自动发现两条路径:
    • 显式标注(语法快路 + 引用程序集 type-level)→ 保留 attrData 解析(读取实现类型)
    • 自动发现(引用程序集 assembly-level + 本程序集 assembly-level)→ 按默认语义创建代理:class → 类代理,interface → 无目标 stub 代理
  2. 修复本程序集自动发现遍历错命名空间:原用 Compilation.GlobalNamespace,会混入引用程序集的 metadata 类型(实测会扫到 System.IUtfChar<TSelf> 并生成其代理、撞 hintName 冲突);改用 compilation.Assembly.GlobalNamespace(仅源程序集声明的类型)。该问题在 baseline 中即潜伏,只是被 attrData gate 掩盖。
  3. 合并 IsEligibleForAutoProxyGetAssemblyEligibleTypes 为统一实现,消除两处过滤逻辑漂移(事件跳过、带特性类型跳过的差异)。

测试

新增 tests/AspectCore.Core.Tests/EngineParity/AssemblyLevelAutoProxyTests.cs(4 个用例):

  • 本程序集 [assembly: AspectCoreGenerateProxy] 自动发现 class/interface
  • 不合格类型(sealed / static / 含事件 / 无可代理成员 / struct)被跳过
  • 显式标注与自动发现共存,显式类型不重复生成
  • 引用程序集带 assembly-level 特性时,其类型被自动发现(多程序集场景)

EngineParity 全套 133 个测试通过(net10.0,含两引擎各跑一遍),无回归。

行为边界(文档已同步)

程序集级自动发现的接口无可推断的实现类型,默认生成无目标 stub 代理(成员返回 default)。需要带实现的完整接口代理时,仍需显式 [AspectCoreGenerateProxy(typeof(Impl))]

程序集级 [assembly: AspectCoreGenerateProxy] 自动发现此前被 Execute 入口的
attrData is null -> continue 门挡死:自动发现产出的类型不带类型级特性,
全部被丢弃,导致该功能形同虚设(文档与 Execute 注释均宣称支持)。

引入 Candidate(Type, Explicit) 来源标记,将候选数据流拆分为显式标注与
自动发现两条路径;自动发现路径按默认语义创建代理(class -> 类代理,
interface -> 无目标 stub 代理)。同时修复本程序集自动发现误用
Compilation.GlobalNamespace(会混入引用程序集 metadata 类型)的问题,
改用 Assembly.GlobalNamespace,并合并 IsEligibleForAutoProxy /
GetAssemblyEligibleTypes 消除过滤逻辑漂移。

新增 AssemblyLevelAutoProxyTests 覆盖:本程序集与引用程序集自动发现、
不合格类型(sealed/static/含事件/无可代理成员/struct)跳过、显式标注
与自动发现共存不重复。EngineParity 全套 133 测试通过,无回归。
@liuhaoyang
liuhaoyang force-pushed the fix/assembly-level-auto-discovery branch from 3c6d62f to c1a8296 Compare August 11, 2026 04:14

@latte-gh latte-gh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[BLOCKING] 引用程序集的 assembly-level 自动发现会把 internal 类型也加入候选,导致消费者项目生成不可编译源码。

当前 GetReferencedAssemblyCandidates 在发现引用程序集有 [assembly: AspectCoreGenerateProxy] 后枚举 assemblySymbol.GlobalNamespace,并在 IsEligibleForAutoProxy 中接受 Accessibility.Internal

  • src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs:65-71:引用程序集 assembly-level attribute 分支把符合条件的类型作为 auto-discovered candidate 加入。
  • src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs:123-125:自动发现过滤接受 public/internal,注释写的是“Generated proxy code lives in the same assembly”。
  • src/AspectCore.SourceGenerator/AspectCoreProxyGenerator.cs:365-377:随后为这些类型创建 interface/class proxy entry。

这个“同程序集可见”的前提只对当前编译程序集成立;对引用程序集不成立。消费者项目中的 source generator 输出不在被引用程序集内,不能访问对方的 internal class/interface。

我用隔离副本验证了最小复现:引用程序集声明 [assembly: AspectCoreGenerateProxy],并包含 internal class InternalServiceinternal interface IInternalService;主程序集只引用该库并运行 generator。新增临时回归测试后执行:

dotnet test tests/AspectCore.Core.Tests/AspectCore.Core.Tests.csproj --configuration Release -f net9.0 --filter "FullyQualifiedName~InternalReferencedAssemblyAutoProxyRegressionTests"

结果失败,输出包含:

CS0122: 'IInternalService' is inaccessible due to its protection level
CS0122: 'InternalService' is inaccessible due to its protection level

建议把 current assembly 和 referenced assembly 的自动发现可见性规则拆开:当前程序集可以继续允许 public/internal;引用程序集自动发现应至少限制为跨程序集可访问类型(通常只允许 public,除非显式完整支持 InternalsVisibleTo 的生成程序集身份契约)。同时补一个“引用程序集 assembly-level attribute + internal 类型”的回归测试,防止消费者编译场景再次漏掉。

引用程序集的 assembly-level 自动发现此前会接受 internal 类型,而生成的
代理代码编译进消费者程序集,无法访问被引用程序集的 internal 类型,导致
CS0122(消费者项目生成不可编译源码)。

修复:IsEligibleForAutoProxy 统一只接受 public 类型。顺带发现 current
assembly 的 internal 类型自动发现同样会产生 CS0060(生成的代理类恒为
public,ProxyEmitter 硬编码,不能继承/实现可访问性更低的类型),因此
两路自动发现一致排除 internal。

新增回归测试:
- 引用程序集带 assembly 特性 + internal class/interface -> 被排除,public 仍生成
- 本程序集 internal 类型 -> 被排除,public 仍生成
@liuhaoyang

Copy link
Copy Markdown
Member Author

已修复,感谢 review。新增提交 fd7ff0d

修复方式

IsEligibleForAutoProxy 现在统一只接受 public 类型(AspectCoreProxyGenerator.cs)。这样引用程序集的 assembly-level 自动发现不会再把 internal 类型加入候选,消费者项目不再生成 CS0122 的不可编译源码。

一个超出 review 范围的顺带发现

实现时实测发现:当前编译程序集的 internal 类型自动发现同样会产生 CS0060(可访问性不一致: 基类 InternalService(internal) 低于代理类(public))。原因是 ProxyEmitter 硬编码生成 public sealed class 代理类(ProxyEmitter.cs:62,165-166),一个 public 代理类不能继承/实现可访问性更低的源类型——这对显式标注路径也成立

因此我让两路自动发现一致排除 internal,而不是按 review 建议的"current assembly 允许 internal"。如果按原建议放开 current assembly 的 internal,会生成 CS0060 的坏源码。这是一个更广的既有限制(生成器代理类恒 public),若要支持 internal 源类型需要让代理类可见性跟随源类型,建议作为后续增强单独处理。

验证

新增 2 个回归测试(共 6 个):

  • ReferencedAssemblyWithAssemblyLevelAttribute_SkipsInternalTypes:引用程序集 internal class/interface 被排除,public 仍自动发现
  • AssemblyLevelAttribute_SkipsInternalTypesInCurrentAssembly:本程序集 internal 同样排除,public 仍自动发现

EngineParity 全套 141 个测试通过(net10.0),无回归。请重新 review。

@latte-gh latte-gh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed head fd7ff0ddce7ae57fcfe34dce4fbd204c7b1e3cd4.

The previous blocking issue is fixed. Assembly-level auto-discovery now excludes non-public types, so referenced assemblies with [assembly: AspectCoreGenerateProxy] no longer emit proxies for internal class/interface symbols that are inaccessible from the consuming compilation. The new regression tests cover both referenced-assembly internal types and current-assembly internal types, while preserving public type auto-discovery.

Checks reviewed:

  • AssemblyLevelAutoProxyTests on net9.0: 6/6 passed locally in an isolated archive checkout.
  • git diff --check origin/master...origin/pr/410: passed.
  • GitHub checks for the current head are green, including build-and-test on Ubuntu/Windows, lint, Unit/E2E/NativeAOT test execution, coverage gates, CodeQL, and NativeAOT Verify.

No actionable findings for this revision.

@latte-gh
latte-gh merged commit f4571e7 into master Aug 11, 2026
20 checks passed
@latte-gh
latte-gh deleted the fix/assembly-level-auto-discovery branch August 11, 2026 06:20
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.

2 participants