Files
2026-08-27 10:56:38 -06:00

142 lines
4.2 KiB
C#

using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp;
internal class DataFlowsOutWalker : AbstractRegionDataFlowPass
{
private readonly ImmutableArray<ISymbol> _dataFlowsIn;
private readonly HashSet<Symbol> _dataFlowsOut = new HashSet<Symbol>();
private DataFlowsOutWalker(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion, HashSet<Symbol> unassignedVariables, ImmutableArray<ISymbol> dataFlowsIn)
: base(compilation, member, node, firstInRegion, lastInRegion, unassignedVariables, null, trackUnassignments: true)
{
_dataFlowsIn = dataFlowsIn;
}
internal static HashSet<Symbol> Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion, HashSet<Symbol> unassignedVariables, ImmutableArray<ISymbol> dataFlowsIn)
{
DataFlowsOutWalker dataFlowsOutWalker = new DataFlowsOutWalker(compilation, member, node, firstInRegion, lastInRegion, unassignedVariables, dataFlowsIn);
try
{
bool badRegion = false;
HashSet<Symbol> hashSet = dataFlowsOutWalker.Analyze(ref badRegion);
return badRegion ? new HashSet<Symbol>() : hashSet;
}
finally
{
dataFlowsOutWalker.Free();
}
}
private HashSet<Symbol> Analyze(ref bool badRegion)
{
Analyze(ref badRegion, null);
return _dataFlowsOut;
}
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{
_dataFlowsOut.Clear();
return base.Scan(ref badRegion);
}
protected override void EnterRegion()
{
ImmutableArray<ISymbol>.Enumerator enumerator = _dataFlowsIn.GetEnumerator();
while (enumerator.MoveNext())
{
Symbol symbol = enumerator.Current.GetSymbol();
int orCreateSlot = GetOrCreateSlot(symbol);
if (orCreateSlot > 0 && !State.IsAssigned(orCreateSlot))
{
_dataFlowsOut.Add(symbol);
}
}
base.EnterRegion();
}
protected override void NoteWrite(Symbol variable, BoundExpression value, bool read)
{
if (State.Reachable && base.IsInside)
{
ParameterSymbol parameterSymbol = variable as ParameterSymbol;
if (FlowsOut(parameterSymbol))
{
_dataFlowsOut.Add(parameterSymbol);
}
}
base.NoteWrite(variable, value, read);
}
protected override void AssignImpl(BoundNode node, BoundExpression value, bool isRef, bool written, bool read)
{
if (base.IsInside)
{
written = false;
if (State.Reachable)
{
ParameterSymbol parameterSymbol = Param(node);
if (FlowsOut(parameterSymbol))
{
_dataFlowsOut.Add(parameterSymbol);
}
}
}
base.AssignImpl(node, value, isRef, written, read);
}
private bool FlowsOut(ParameterSymbol param)
{
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if ((object)param != null)
{
if ((int)param.RefKind == 0 || param.IsImplicitlyDeclared || RegionContains(param.GetFirstLocation().SourceSpan))
{
return param.ContainingSymbol is SynthesizedPrimaryConstructor;
}
return true;
}
return false;
}
private ParameterSymbol Param(BoundNode node)
{
return node.Kind switch
{
BoundKind.Parameter => ((BoundParameter)node).ParameterSymbol,
BoundKind.ThisReference => base.MethodThisParameter,
_ => null,
};
}
public override BoundNode VisitQueryClause(BoundQueryClause node)
{
return base.VisitQueryClause(node);
}
protected override void ReportUnassigned(Symbol symbol, SyntaxNode node, int slot, bool skipIfUseBeforeDeclaration)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Invalid comparison between Unknown and I4
if (!base.IsInside)
{
_dataFlowsOut.Add(((int)symbol.Kind == 6) ? GetNonMemberSymbol(slot) : symbol);
}
base.ReportUnassigned(symbol, node, slot, skipIfUseBeforeDeclaration);
}
protected override void ReportUnassignedOutParameter(ParameterSymbol parameter, SyntaxNode node, Location location)
{
if (!_dataFlowsOut.Contains(parameter) && (node == null || node is ReturnStatementSyntax))
{
_dataFlowsOut.Add(parameter);
}
base.ReportUnassignedOutParameter(parameter, node, location);
}
}