| File: FlowAnalysis\ControlFlowAnalysis.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.Collections.Immutable; using System.Threading; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// This class implements the region control flow analysis operations. Region control flow /// analysis provides information about statements which enter and leave a region. The analysis /// is done lazily. When created, it performs no analysis, but simply caches the arguments. /// Then, the first time one of the analysis results is used it computes that one result and /// caches it. Each result is computed using a custom algorithm. /// </summary> internal class CSharpControlFlowAnalysis : ControlFlowAnalysis { private readonly RegionAnalysisContext _context; private ImmutableArray<SyntaxNode> _entryPoints; private ImmutableArray<SyntaxNode> _exitPoints; private object _regionStartPointIsReachable; private object _regionEndPointIsReachable; private bool? _succeeded; internal CSharpControlFlowAnalysis(RegionAnalysisContext context) { _context = context; } /// <summary> /// A collection of statements outside the region that jump into the region. /// </summary> public override ImmutableArray<SyntaxNode> EntryPoints { get { if (_entryPoints == null) { _succeeded = !_context.Failed; var result = _context.Failed ? ImmutableArray<SyntaxNode>.Empty : ((IEnumerable<SyntaxNode>)EntryPointsWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion, out _succeeded)).ToImmutableArray(); ImmutableInterlocked.InterlockedInitialize(ref _entryPoints, result); } return _entryPoints; } } /// <summary> /// A collection of statements inside the region that jump to locations outside the region. /// </summary> public override ImmutableArray<SyntaxNode> ExitPoints { get { if (_exitPoints == null) { var result = Succeeded ? ImmutableArray<SyntaxNode>.CastUp(ExitPointsWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion)) : ImmutableArray<SyntaxNode>.Empty; ImmutableInterlocked.InterlockedInitialize(ref _exitPoints, result); } return _exitPoints; } } /// <summary> /// Returns true if and only if the endpoint of the last statement in the region is reachable or the region contains no /// statements. /// </summary> public sealed override bool EndPointIsReachable { // To determine if the region completes normally, we just check if // its last statement completes normally. get { if (_regionEndPointIsReachable == null) { ComputeReachability(); } return (bool)_regionEndPointIsReachable; } } public sealed override bool StartPointIsReachable { // To determine if the region completes normally, we just check if // its last statement completes normally. get { if (_regionStartPointIsReachable == null) { ComputeReachability(); } return (bool)_regionStartPointIsReachable; } } private void ComputeReachability() { bool startIsReachable, endIsReachable; if (Succeeded) { RegionReachableWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion, out startIsReachable, out endIsReachable); } else { startIsReachable = endIsReachable = true; } Interlocked.CompareExchange(ref _regionEndPointIsReachable, endIsReachable, null); Interlocked.CompareExchange(ref _regionStartPointIsReachable, startIsReachable, null); } /// <summary> /// A collection of return (or yield break) statements found within the region that return from the enclosing method or lambda. /// </summary> public override ImmutableArray<SyntaxNode> ReturnStatements { // Return statements out of the region are computed in precisely the same // way that jumps out of the region are computed. get { return ExitPoints.WhereAsArray(s => s.IsKind(SyntaxKind.ReturnStatement) || s.IsKind(SyntaxKind.YieldBreakStatement)); } } /// <summary> /// Returns true if and only if analysis was successful. Analysis can fail if the region does not properly span a single expression, /// a single statement, or a contiguous series of statements within the enclosing block. /// </summary> public sealed override bool Succeeded { get { if (_succeeded == null) { var discarded = EntryPoints; } return _succeeded.Value; } } } }