You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roslyn分析器编辑时深层基类解析失效,报SyntaxTree不属于编译错误

问题根因

编辑场景下Roslyn会使用增量编译快照执行分析,此时compilationContext.Compilation仅包含当前未修改、或修改后纳入本次增量编译的语法树。基类对应的源文件如果在编辑过程中发生过修改,其DeclaringSyntaxReferences指向的语法树可能不在当前编译快照的语法树集合中,直接调用compilation.GetSemanticModel()就会抛出「SyntaxTree不属于当前编译」的ArgumentException。全量编译时所有项目内语法树都被纳入编译,因此不会触发该错误。

现有实现的问题

  • 未校验基类声明对应的语法树是否属于当前编译,直接获取SemanticModel
  • 自行遍历语法树根节点查找类声明的逻辑冗余,且容易引入不必要的性能开销
  • 无增量编译场景下的降级处理逻辑,遇到不完整快照时直接崩溃

修复方案

  1. 调用GetSemanticModel前先通过Compilation.ContainsSyntaxTree()校验语法树归属,不属于当前编译的直接跳过本次分析
  2. 直接使用DeclaringSyntaxReference.GetSyntax()获取类声明节点,无需自行遍历语法树
  3. 增加增量场景降级逻辑,遇到不完整编译快照时提前终止分析,避免报错
  4. 可缓存已获取的SemanticModel,减少重复实例化开销

修改后的代码示例

public override void Initialize(AnalysisContext context)
{
    context.EnableConcurrentExecution();
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    
    context.RegisterCompilationStartAction(compilationContext =>
    {
        // 缓存SemanticModel减少重复创建
        var semanticModelCache = new Dictionary<SyntaxTree, SemanticModel>();
        
        compilationContext.RegisterSyntaxNodeAction(syntaxNodeAnalysisContext => 
        {
            syntaxNodeAnalysisContext.CancellationToken.ThrowIfCancellationRequested();
            
            var classDeclaration = (ClassDeclarationSyntax)syntaxNodeAnalysisContext.Node;
            var meAndBaseClasses = GetClassDeclarationsDeep(classDeclaration, compilationContext.Compilation, semanticModelCache, syntaxNodeAnalysisContext.CancellationToken);
            
            // 后续分析逻辑
        }, SyntaxKind.ClassDeclaration);
    });
}

protected IEnumerable<ClassDeclarationSyntax> GetClassDeclarationsDeep(ClassDeclarationSyntax classDeclaration, Compilation compilation, Dictionary<SyntaxTree, SemanticModel> semanticModelCache, CancellationToken cancellationToken)
{
    if (!semanticModelCache.TryGetValue(classDeclaration.SyntaxTree, out var currentSemanticModel))
    {
        currentSemanticModel = compilation.GetSemanticModel(classDeclaration.SyntaxTree);
        semanticModelCache[classDeclaration.SyntaxTree] = currentSemanticModel;
    }
    
    foreach (var type in classDeclaration.GetMeAndBaseTypes(currentSemanticModel))
    {
        cancellationToken.ThrowIfCancellationRequested();
        foreach (var partialType in GetClassDeclarations(type, compilation, semanticModelCache, cancellationToken))
        {
            yield return partialType;
        }
    }
}

public static IEnumerable<INamedTypeSymbol> GetMeAndBaseTypes(this ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel)
{
    return GetMeAndBaseTypes(semanticModel.GetDeclaredSymbol(classDeclaration));
}

protected IEnumerable<ClassDeclarationSyntax> GetClassDeclarations(INamedTypeSymbol classType, Compilation compilation, Dictionary<SyntaxTree, SemanticModel> semanticModelCache, CancellationToken cancellationToken)
{
    // 跳过元数据程序集中的类型,无语法声明
    if (classType.Locations.All(l => l.IsInMetadata))
        yield break;
    
    foreach (var syntaxRef in classType.DeclaringSyntaxReferences)
    {
        cancellationToken.ThrowIfCancellationRequested();
        var tree = syntaxRef.SyntaxTree;
        
        // 关键校验:语法树不属于当前编译直接跳过
        if (!compilation.ContainsSyntaxTree(tree))
            continue;
        
        if (!semanticModelCache.TryGetValue(tree, out var semanticModel))
        {
            semanticModel = compilation.GetSemanticModel(tree);
            semanticModelCache[tree] = semanticModel;
        }
        
        if (syntaxRef.GetSyntax(cancellationToken) is ClassDeclarationSyntax classDecl
            && SymbolEqualityComparer.Default.Equals(classType, semanticModel.GetDeclaredSymbol(classDecl, cancellationToken)))
        {
            yield return classDecl;
        }
    }
}

public static IEnumerable<INamedTypeSymbol> GetMeAndBaseTypes(this INamedTypeSymbol type)
{
    while (type != null)
    {
        yield return type;
        type = type.BaseType;
    }
}

额外说明

如果业务逻辑要求必须拿到所有基类的信息才能执行分析,可以在发现语法树缺失时直接终止当前分析,Roslyn后续会在更新的编译快照中重新触发分析规则,不会影响最终结果的准确性。

内容的提问来源于stack exchange,提问作者Lori

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 04:06:01