| File: FlowAnalysis\AlwaysAssignedWalker.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 using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// A region analysis walker that computes the set of variables that are always assigned a value /// in the region. A variable is "always assigned" in a region if an analysis of the region that /// starts with the variable unassigned ends with the variable assigned. /// </summary> internal class AlwaysAssignedWalker : AbstractRegionDataFlowPass { private LocalState _endOfRegionState; private readonly HashSet<LabelSymbol> _labelsInside = new HashSet<LabelSymbol>(); private AlwaysAssignedWalker(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion) : base(compilation, member, node, firstInRegion, lastInRegion) { } internal static IEnumerable<Symbol> Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion) { var walker = new AlwaysAssignedWalker(compilation, member, node, firstInRegion, lastInRegion); bool badRegion = false; try { var result = walker.Analyze(ref badRegion); return badRegion ? SpecializedCollections.EmptyEnumerable<Symbol>() : result; } finally { walker.Free(); } } private List<Symbol> Analyze(ref bool badRegion) { base.Analyze(ref badRegion, null); List<Symbol> result = new List<Symbol>(); Debug.Assert(!IsInside); if (_endOfRegionState.Reachable) { foreach (var i in _endOfRegionState.Assigned.TrueBits()) { if (i >= variableBySlot.Count) { continue; } var v = base.variableBySlot[i]; if (v.Exists && !(v.Symbol is FieldSymbol)) { result.Add(v.Symbol); } } } return result; } protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method) { // ref parameter does not "always" assign. if (refKind == RefKind.Out) { Assign(arg, value: null); } } protected override void ResolveBranch(PendingBranch pending, LabelSymbol label, BoundStatement target, ref bool labelStateChanged) { // branches into a region are considered entry points if (IsInside && pending.Branch != null && !RegionContains(pending.Branch.Syntax.Span)) { pending.State = pending.State.Reachable ? TopState() : UnreachableState(); } base.ResolveBranch(pending, label, target, ref labelStateChanged); } public override BoundNode VisitLabel(BoundLabel node) { ResolveLabel(node, node.Label); return base.VisitLabel(node); } public override BoundNode VisitLabeledStatement(BoundLabeledStatement node) { ResolveLabel(node, node.Label); return base.VisitLabeledStatement(node); } private void ResolveLabel(BoundNode node, LabelSymbol label) { if (node.Syntax != null && RegionContains(node.Syntax.Span)) _labelsInside.Add(label); } protected override LocalState TopState() { return new LocalState(BitVector.Empty); } protected override void EnterRegion() { this.State = TopState(); base.EnterRegion(); } protected override void LeaveRegion() { if (this.IsConditionalState) { // If the region is in a condition, then the state will be split and state.Assigned will // be null. Merge to get sensible results. _endOfRegionState = StateWhenTrue.Clone(); Join(ref _endOfRegionState, ref StateWhenFalse); } else { _endOfRegionState = this.State.Clone(); } foreach (var branch in PendingBranches.AsEnumerable()) { if (branch.Branch != null && RegionContains(branch.Branch.Syntax.Span) && !_labelsInside.Contains(branch.Label)) { Join(ref _endOfRegionState, ref branch.State); } } base.LeaveRegion(); } } }