| File: FlowAnalysis\DefinitelyAssignedWalker.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable #if DEBUG // See comment in DefiniteAssignment. #define REFERENCE_STATE #endif using System; using System.Collections.Generic; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// A region analysis walker that computes the set of variables that are definitely assigned /// when a region is entered or exited. /// </summary> internal sealed class DefinitelyAssignedWalker : AbstractRegionDataFlowPass { private readonly HashSet<Symbol> _definitelyAssignedOnEntry = new HashSet<Symbol>(); private readonly HashSet<Symbol> _definitelyAssignedOnExit = new HashSet<Symbol>(); private DefinitelyAssignedWalker( CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion) : base(compilation, member, node, firstInRegion, lastInRegion) { } internal static (HashSet<Symbol> entry, HashSet<Symbol> exit) Analyze( CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion) { var walker = new DefinitelyAssignedWalker(compilation, member, node, firstInRegion, lastInRegion); try { bool badRegion = false; walker.Analyze(ref badRegion, diagnostics: null); return badRegion ? (new HashSet<Symbol>(), new HashSet<Symbol>()) : (walker._definitelyAssignedOnEntry, walker._definitelyAssignedOnExit); } finally { walker.Free(); } } protected override void EnterRegion() { ProcessRegion(_definitelyAssignedOnEntry); base.EnterRegion(); } protected override void LeaveRegion() { ProcessRegion(_definitelyAssignedOnExit); base.LeaveRegion(); } private void ProcessRegion(HashSet<Symbol> definitelyAssigned) { // this can happen multiple times as flow analysis is multi-pass. Always // take the latest data and use that to determine our result. definitelyAssigned.Clear(); if (this.IsConditionalState) { // We're in a state where there are different flow paths (i.e. when-true and when-false). // In that case, a variable is only definitely assigned if it's definitely assigned through // both paths. this.ProcessState(definitelyAssigned, this.StateWhenTrue, this.StateWhenFalse); } else { this.ProcessState(definitelyAssigned, this.State, state2opt: null); } } #if REFERENCE_STATE private void ProcessState(HashSet<Symbol> definitelyAssigned, LocalState state1, LocalState state2opt) #else private void ProcessState(HashSet<Symbol> definitelyAssigned, LocalState state1, LocalState? state2opt) #endif { foreach (var slot in state1.Assigned.TrueBits()) { if (slot < variableBySlot.Count && state2opt?.IsAssigned(slot) != false && variableBySlot[slot].Symbol is { } symbol && symbol.Kind != SymbolKind.Field) { definitelyAssigned.Add(symbol); } } } } }