404 lines
13 KiB
C#
404 lines
13 KiB
C#
using System.Collections.Generic;
|
|||
|
|
using System.Collections.Immutable;
|
||
|
|
using System.Linq;
|
||
|
|
using Microsoft.CodeAnalysis.CSharp.Symbols;
|
||
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
||
|
|
using Microsoft.CodeAnalysis.Text;
|
||
|
|
using Roslyn.Utilities;
|
||
|
|
|
||
|
|
namespace Microsoft.CodeAnalysis.CSharp;
|
||
|
|
|
||
|
|
internal class ControlFlowPass : AbstractFlowPass<ControlFlowPass.LocalState, ControlFlowPass.LocalFunctionState>
|
||
|
|
{
|
||
|
|
internal struct LocalState : ILocalState
|
||
|
|
{
|
||
|
|
internal bool Alive;
|
||
|
|
|
||
|
|
internal bool Reported;
|
||
|
|
|
||
|
|
public bool Reachable => Alive;
|
||
|
|
|
||
|
|
internal LocalState(bool live, bool reported)
|
||
|
|
{
|
||
|
|
Alive = live;
|
||
|
|
Reported = reported;
|
||
|
|
}
|
||
|
|
|
||
|
|
public LocalState Clone()
|
||
|
|
{
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
internal sealed class LocalFunctionState : AbstractLocalFunctionState
|
||
|
|
{
|
||
|
|
public LocalFunctionState(LocalState unreachableState)
|
||
|
|
: base(unreachableState.Clone(), unreachableState.Clone())
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private readonly PooledDictionary<LabelSymbol, BoundNode> _labelsDefined = PooledDictionary<LabelSymbol, BoundNode>.GetInstance();
|
||
|
|
|
||
|
|
private readonly PooledHashSet<LabelSymbol> _labelsUsed = PooledHashSet<LabelSymbol>.GetInstance();
|
||
|
|
|
||
|
|
protected bool _convertInsufficientExecutionStackExceptionToCancelledByStackGuardException;
|
||
|
|
|
||
|
|
private readonly ArrayBuilder<(LocalSymbol symbol, BoundBlock block)> _usingDeclarations = ArrayBuilder<(LocalSymbol, BoundBlock)>.GetInstance();
|
||
|
|
|
||
|
|
private BoundBlock _currentBlock;
|
||
|
|
|
||
|
|
public sealed override bool AwaitUsingAndForeachAddsPendingBranch => false;
|
||
|
|
|
||
|
|
protected override void Free()
|
||
|
|
{
|
||
|
|
_labelsDefined.Free();
|
||
|
|
_labelsUsed.Free();
|
||
|
|
_usingDeclarations.Free();
|
||
|
|
base.Free();
|
||
|
|
}
|
||
|
|
|
||
|
|
internal ControlFlowPass(CSharpCompilation compilation, Symbol member, BoundNode node)
|
||
|
|
: base(compilation, member, node, (BoundNode)null, (BoundNode)null, false, false)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
internal ControlFlowPass(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
|
||
|
|
: base(compilation, member, node, firstInRegion, lastInRegion, false, false)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override LocalFunctionState CreateLocalFunctionState(LocalFunctionSymbol symbol)
|
||
|
|
{
|
||
|
|
return new LocalFunctionState(UnreachableState());
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override bool Meet(ref LocalState self, ref LocalState other)
|
||
|
|
{
|
||
|
|
LocalState localState = self;
|
||
|
|
self.Alive &= other.Alive;
|
||
|
|
self.Reported &= other.Reported;
|
||
|
|
return self.Alive != localState.Alive;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override bool Join(ref LocalState self, ref LocalState other)
|
||
|
|
{
|
||
|
|
LocalState localState = self;
|
||
|
|
self.Alive |= other.Alive;
|
||
|
|
self.Reported &= other.Reported;
|
||
|
|
return self.Alive != localState.Alive;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override string Dump(LocalState state)
|
||
|
|
{
|
||
|
|
return "[alive: " + state.Alive + "; reported: " + state.Reported + "]";
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override LocalState TopState()
|
||
|
|
{
|
||
|
|
return new LocalState(live: true, reported: false);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override LocalState UnreachableState()
|
||
|
|
{
|
||
|
|
return new LocalState(live: false, State.Reported);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override LocalState LabelState(LabelSymbol label)
|
||
|
|
{
|
||
|
|
LocalState result = base.LabelState(label);
|
||
|
|
result.Reported = false;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
public override BoundNode Visit(BoundNode node)
|
||
|
|
{
|
||
|
|
if (!(node is BoundExpression))
|
||
|
|
{
|
||
|
|
return base.Visit(node);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
|
||
|
|
{
|
||
|
|
base.Diagnostics.Clear();
|
||
|
|
ImmutableArray<PendingBranch> result = base.Scan(ref badRegion);
|
||
|
|
LabelSymbol labelSymbol = default(LabelSymbol);
|
||
|
|
BoundNode boundNode = default(BoundNode);
|
||
|
|
foreach (KeyValuePair<LabelSymbol, BoundNode> item in (Dictionary<LabelSymbol, BoundNode>)(object)_labelsDefined)
|
||
|
|
{
|
||
|
|
KeyValuePairUtil.Deconstruct<LabelSymbol, BoundNode>(item, ref labelSymbol, ref boundNode);
|
||
|
|
LabelSymbol labelSymbol2 = labelSymbol;
|
||
|
|
if (!(boundNode is BoundSwitchStatement) && !((HashSet<LabelSymbol>)(object)_labelsUsed).Contains(labelSymbol2))
|
||
|
|
{
|
||
|
|
base.Diagnostics.Add(ErrorCode.WRN_UnreferencedLabel, labelSymbol2.GetFirstLocation());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static bool Analyze(CSharpCompilation compilation, Symbol member, BoundBlock block, DiagnosticBag diagnostics)
|
||
|
|
{
|
||
|
|
ControlFlowPass controlFlowPass = new ControlFlowPass(compilation, member, block);
|
||
|
|
if (diagnostics != null)
|
||
|
|
{
|
||
|
|
controlFlowPass._convertInsufficientExecutionStackExceptionToCancelledByStackGuardException = true;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
bool badRegion = false;
|
||
|
|
return controlFlowPass.Analyze(ref badRegion, diagnostics);
|
||
|
|
}
|
||
|
|
catch (CancelledByStackGuardException ex) when (diagnostics != null)
|
||
|
|
{
|
||
|
|
ex.AddAnError(diagnostics);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
controlFlowPass.Free();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()
|
||
|
|
{
|
||
|
|
return _convertInsufficientExecutionStackExceptionToCancelledByStackGuardException;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected bool Analyze(ref bool badRegion, DiagnosticBag diagnostics)
|
||
|
|
{
|
||
|
|
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
Analyze(ref badRegion);
|
||
|
|
if (diagnostics != null)
|
||
|
|
{
|
||
|
|
diagnostics.AddRange(base.Diagnostics);
|
||
|
|
}
|
||
|
|
return State.Alive;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override ImmutableArray<PendingBranch> RemoveReturns()
|
||
|
|
{
|
||
|
|
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_00bb: Expected O, but got Unknown
|
||
|
|
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0069: Expected O, but got Unknown
|
||
|
|
ImmutableArray<PendingBranch> result = base.RemoveReturns();
|
||
|
|
ImmutableArray<PendingBranch>.Enumerator enumerator = result.GetEnumerator();
|
||
|
|
while (enumerator.MoveNext())
|
||
|
|
{
|
||
|
|
PendingBranch current = enumerator.Current;
|
||
|
|
if (current.Branch != null)
|
||
|
|
{
|
||
|
|
switch (current.Branch.Kind)
|
||
|
|
{
|
||
|
|
case BoundKind.GotoStatement:
|
||
|
|
{
|
||
|
|
SyntaxToken firstToken = current.Branch.Syntax.GetFirstToken(false, false, false, false);
|
||
|
|
SourceLocation location2 = new SourceLocation(ref firstToken);
|
||
|
|
base.Diagnostics.Add(ErrorCode.ERR_LabelNotFound, (Location)(object)location2, ((BoundGotoStatement)current.Branch).Label.Name);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case BoundKind.BreakStatement:
|
||
|
|
case BoundKind.ContinueStatement:
|
||
|
|
{
|
||
|
|
SyntaxToken firstToken = current.Branch.Syntax.GetFirstToken(false, false, false, false);
|
||
|
|
SourceLocation location = new SourceLocation(ref firstToken);
|
||
|
|
base.Diagnostics.Add(ErrorCode.ERR_BadDelegateLeave, (Location)(object)location);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitStatement(BoundStatement statement)
|
||
|
|
{
|
||
|
|
switch (statement.Kind)
|
||
|
|
{
|
||
|
|
case BoundKind.Block:
|
||
|
|
case BoundKind.LocalFunctionStatement:
|
||
|
|
case BoundKind.NoOpStatement:
|
||
|
|
case BoundKind.ThrowStatement:
|
||
|
|
case BoundKind.LabeledStatement:
|
||
|
|
base.VisitStatement(statement);
|
||
|
|
break;
|
||
|
|
case BoundKind.StatementList:
|
||
|
|
VisitStatementList((BoundStatementList)statement);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
CheckReachable(statement);
|
||
|
|
base.VisitStatement(statement);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CheckReachable(BoundStatement statement)
|
||
|
|
{
|
||
|
|
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_005e: Expected O, but got Unknown
|
||
|
|
if (!State.Alive && !State.Reported && !statement.WasCompilerGenerated)
|
||
|
|
{
|
||
|
|
TextSpan span = statement.Syntax.Span;
|
||
|
|
if (((TextSpan)(ref span)).Length != 0)
|
||
|
|
{
|
||
|
|
SyntaxToken firstToken = statement.Syntax.GetFirstToken(false, false, false, false);
|
||
|
|
base.Diagnostics.Add(ErrorCode.WRN_UnreachableCode, (Location)new SourceLocation(ref firstToken));
|
||
|
|
State.Reported = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitTryBlock(BoundStatement tryBlock, BoundTryStatement node, ref LocalState tryState)
|
||
|
|
{
|
||
|
|
if (node.CatchBlocks.IsEmpty)
|
||
|
|
{
|
||
|
|
base.VisitTryBlock(tryBlock, node, ref tryState);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
SavedPending oldPending = SavePending();
|
||
|
|
base.VisitTryBlock(tryBlock, node, ref tryState);
|
||
|
|
RestorePending(oldPending);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState)
|
||
|
|
{
|
||
|
|
SavedPending oldPending = SavePending();
|
||
|
|
base.VisitCatchBlock(catchBlock, ref finallyState);
|
||
|
|
RestorePending(oldPending);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref LocalState endState)
|
||
|
|
{
|
||
|
|
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_005e: Expected O, but got Unknown
|
||
|
|
SavedPending oldPending = SavePending();
|
||
|
|
SavedPending oldPending2 = SavePending();
|
||
|
|
base.VisitFinallyBlock(finallyBlock, ref endState);
|
||
|
|
RestorePending(oldPending2);
|
||
|
|
foreach (PendingBranch item in base.PendingBranches.AsEnumerable())
|
||
|
|
{
|
||
|
|
if (item.Branch != null)
|
||
|
|
{
|
||
|
|
SyntaxToken firstToken = item.Branch.Syntax.GetFirstToken(false, false, false, false);
|
||
|
|
SourceLocation location = new SourceLocation(ref firstToken);
|
||
|
|
BoundKind kind = item.Branch.Kind;
|
||
|
|
if (kind - 89 > BoundKind.PropertyEqualsValue)
|
||
|
|
{
|
||
|
|
base.Diagnostics.Add(ErrorCode.ERR_BadFinallyLeave, (Location)(object)location);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
RestorePending(oldPending);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitLabel(BoundLabeledStatement node)
|
||
|
|
{
|
||
|
|
((Dictionary<LabelSymbol, BoundNode>)(object)_labelsDefined)[node.Label] = _currentBlock;
|
||
|
|
base.VisitLabel(node);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override BoundNode VisitLabeledStatement(BoundLabeledStatement node)
|
||
|
|
{
|
||
|
|
VisitLabel(node);
|
||
|
|
CheckReachable(node);
|
||
|
|
VisitStatement(node.Body);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public override BoundNode VisitGotoStatement(BoundGotoStatement node)
|
||
|
|
{
|
||
|
|
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
((HashSet<LabelSymbol>)(object)_labelsUsed).Add(node.Label);
|
||
|
|
Location location = node.Syntax.Location;
|
||
|
|
TextSpan sourceSpan = location.SourceSpan;
|
||
|
|
int start = ((TextSpan)(ref sourceSpan)).Start;
|
||
|
|
sourceSpan = node.Label.GetFirstLocation().SourceSpan;
|
||
|
|
int start2 = ((TextSpan)(ref sourceSpan)).Start;
|
||
|
|
Enumerator<(LocalSymbol, BoundBlock)> enumerator = _usingDeclarations.GetEnumerator();
|
||
|
|
while (enumerator.MoveNext())
|
||
|
|
{
|
||
|
|
(LocalSymbol, BoundBlock) current = enumerator.Current;
|
||
|
|
sourceSpan = current.Item1.GetFirstLocation().SourceSpan;
|
||
|
|
int start3 = ((TextSpan)(ref sourceSpan)).Start;
|
||
|
|
if (start < start3 && start2 > start3)
|
||
|
|
{
|
||
|
|
base.Diagnostics.Add(ErrorCode.ERR_GoToForwardJumpOverUsingVar, location);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (start > start3 && start2 < start3 && ((Dictionary<LabelSymbol, BoundNode>)(object)_labelsDefined)[node.Label] == current.Item2)
|
||
|
|
{
|
||
|
|
base.Diagnostics.Add(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, location);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return base.VisitGotoStatement(node);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void VisitSwitchSection(BoundSwitchSection node, bool isLastSection)
|
||
|
|
{
|
||
|
|
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_0055: Expected O, but got Unknown
|
||
|
|
base.VisitSwitchSection(node, isLastSection);
|
||
|
|
if (State.Alive)
|
||
|
|
{
|
||
|
|
SyntaxNode syntax = node.SwitchLabels.Last().Syntax;
|
||
|
|
base.Diagnostics.Add(isLastSection ? ErrorCode.ERR_SwitchFallOut : ErrorCode.ERR_SwitchFallThrough, (Location)new SourceLocation(syntax), ((object)syntax).ToString());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
|
||
|
|
{
|
||
|
|
ImmutableArray<BoundSwitchSection>.Enumerator enumerator = node.SwitchSections.GetEnumerator();
|
||
|
|
while (enumerator.MoveNext())
|
||
|
|
{
|
||
|
|
ImmutableArray<BoundSwitchLabel>.Enumerator enumerator2 = enumerator.Current.SwitchLabels.GetEnumerator();
|
||
|
|
while (enumerator2.MoveNext())
|
||
|
|
{
|
||
|
|
BoundSwitchLabel current = enumerator2.Current;
|
||
|
|
((Dictionary<LabelSymbol, BoundNode>)(object)_labelsDefined)[current.Label] = node;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return base.VisitSwitchStatement(node);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override BoundNode VisitBlock(BoundBlock node)
|
||
|
|
{
|
||
|
|
BoundBlock currentBlock = _currentBlock;
|
||
|
|
_currentBlock = node;
|
||
|
|
int count = _usingDeclarations.Count;
|
||
|
|
ImmutableArray<LocalSymbol>.Enumerator enumerator = node.Locals.GetEnumerator();
|
||
|
|
while (enumerator.MoveNext())
|
||
|
|
{
|
||
|
|
LocalSymbol current = enumerator.Current;
|
||
|
|
if (current.IsUsing)
|
||
|
|
{
|
||
|
|
_usingDeclarations.Add((current, node));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
BoundNode result = base.VisitBlock(node);
|
||
|
|
_usingDeclarations.Clip(count);
|
||
|
|
_currentBlock = currentBlock;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|