调用DataFlowAnalysis导致Roslyn分析器停止运行问题排查
问题背景
我正在开发一款Roslyn分析器,需求如下:
- 查找所有静态字段声明
- 若存在静态字段,检查是否有标注
RuntimeInitializeOnLoadMethod特性的函数,若无则触发诊断 - 若存在该函数,检查静态字段是否在该方法范围内被赋值,未赋值则触发诊断
步骤1和2运行正常,但步骤3出现问题。我的实现思路是:获取标注RuntimeInitializeOnLoadMethod特性的函数的SyntaxNode,对其执行DataFlowAnalysis,再检查静态字段的符号是否在DataFlowAnalysis.AlwaysAssigned中。但调用DataFlowAnalysis后,分析器似乎不再继续执行。
相关实现代码
private static void AnalyzeStaticFields(SyntaxNodeAnalysisContext context) { var declaration = (FieldDeclarationSyntax)context.Node; if (declaration.Modifiers.Any(SyntaxKind.StaticKeyword)) { // Find node with RuntimeInitializeOnLoadMethodAttribute SyntaxNode initializationNode; if (GetInitializationNode(context, out initializationNode)) { DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(initializationNode); foreach (var variable in declaration.Declaration.Variables) { //Get symbol of static variable ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken); //Check if static variable is assigned within scope of function if (dataFlowAnalysis.AlwaysAssigned.Contains(variableSymbol)) { var diagnostic = Diagnostic.Create(FieldRule, context.Node.GetLocation(), declaration.Declaration.Variables.First().Identifier.ValueText); context.ReportDiagnostic(diagnostic); } } } } }
测试代码
期望Class2中的intContent变量触发诊断,但实际并未触发。若移除AnalyzeDataFlow调用并注释相关条件,诊断信息会正常显示:
class Class2 { private static string stringContent = "This is the default value"; private static int intContent = -1; [RuntimeInitializeOnLoadMethod] static void OnEnterPlayMode() { stringContent = "Initialised value"; } public static void Test() { Debug.Log($"{stringContent} {intContent}"); EventExample.StaticEvent += Test; } }
问题分析与修正方案
核心问题点
判断逻辑完全反转:你期望未在初始化方法中赋值的字段触发诊断,但代码中却是当字段**存在于
AlwaysAssigned**时才报告。而AlwaysAssigned包含了字段的初始赋值(比如intContent = -1),所以即使方法里没赋值,这个集合也会包含该字段,导致诊断无法触发。数据流分析的范围错误:如果
initializationNode是MethodDeclarationSyntax类型,直接传入整个方法节点做数据流分析可能不准确,应该传入方法的Body节点,确保分析覆盖方法内的所有执行逻辑。错误使用
AlwaysAssigned集合:AlwaysAssigned表示字段在所有代码路径中都被赋值,包括字段的初始值。你需要检查的是初始化方法是否主动写入了该字段,应该使用WrittenSymbols集合,这个集合记录了在分析范围内被显式写入的符号。
修正后的代码
private static void AnalyzeStaticFields(SyntaxNodeAnalysisContext context) { var declaration = (FieldDeclarationSyntax)context.Node; if (declaration.Modifiers.Any(SyntaxKind.StaticKeyword)) { if (GetInitializationNode(context, out var initializationNode)) { // 确保分析的是方法体节点 var methodBody = (initializationNode as MethodDeclarationSyntax)?.Body; if (methodBody == null) return; DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(methodBody); foreach (var variable in declaration.Declaration.Variables) { var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken); if (variableSymbol == null) continue; // 当字段未在初始化方法中被写入时,触发诊断 if (!dataFlowAnalysis.WrittenSymbols.Contains(variableSymbol)) { var diagnostic = Diagnostic.Create(FieldRule, variable.GetLocation(), variable.Identifier.ValueText); context.ReportDiagnostic(diagnostic); } } } } }
关键修改说明
- 替换
AlwaysAssigned为WrittenSymbols:精准定位到初始化方法内是否对字段进行了显式赋值,排除字段初始值的干扰。 - 修正判断逻辑:当字段不在
WrittenSymbols中时触发诊断,符合需求预期。 - 限定分析范围为方法体:确保数据流分析仅针对
RuntimeInitializeOnLoadMethod标注方法的内部执行代码,避免无关语法节点的干扰。 - 增加空值检查:对
variableSymbol和methodBody进行空值判断,提升分析器的稳定性。
内容的提问来源于stack exchange,提问作者Ronan Tumelty

