282 lines
6.7 KiB
C#
282 lines
6.7 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
internal readonly struct DirectiveStack
|
|
{
|
|
public static readonly DirectiveStack Empty = new DirectiveStack(ConsList<Directive>.Empty);
|
|
|
|
private readonly ConsList<Directive>? _directives;
|
|
|
|
public bool IsNull => _directives == null;
|
|
|
|
public bool IsEmpty => _directives == ConsList<Directive>.Empty;
|
|
|
|
private DirectiveStack(ConsList<Directive>? directives)
|
|
{
|
|
_directives = directives;
|
|
}
|
|
|
|
public static void InterlockedInitialize(ref DirectiveStack location, DirectiveStack value)
|
|
{
|
|
Interlocked.CompareExchange(ref Unsafe.AsRef(in location._directives), value._directives, null);
|
|
}
|
|
|
|
public DefineState IsDefined(string id)
|
|
{
|
|
for (ConsList<Directive> val = _directives; val != null && val.Any(); val = val.Tail)
|
|
{
|
|
switch (val.Head.Kind)
|
|
{
|
|
case SyntaxKind.DefineDirectiveTrivia:
|
|
if (!(val.Head.GetIdentifier() == id))
|
|
{
|
|
continue;
|
|
}
|
|
return DefineState.Defined;
|
|
case SyntaxKind.UndefDirectiveTrivia:
|
|
if (!(val.Head.GetIdentifier() == id))
|
|
{
|
|
continue;
|
|
}
|
|
return DefineState.Undefined;
|
|
case SyntaxKind.ElifDirectiveTrivia:
|
|
case SyntaxKind.ElseDirectiveTrivia:
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
do
|
|
{
|
|
val = val.Tail;
|
|
if (val == null || !val.Any())
|
|
{
|
|
return DefineState.Unspecified;
|
|
}
|
|
}
|
|
while (val.Head.Kind != SyntaxKind.IfDirectiveTrivia);
|
|
}
|
|
return DefineState.Unspecified;
|
|
}
|
|
|
|
public bool PreviousBranchTaken()
|
|
{
|
|
ConsList<Directive> val = _directives;
|
|
while (val != null && val.Any())
|
|
{
|
|
if (val.Head.BranchTaken)
|
|
{
|
|
return true;
|
|
}
|
|
if (val.Head.Kind == SyntaxKind.IfDirectiveTrivia)
|
|
{
|
|
return false;
|
|
}
|
|
val = val.Tail;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool HasUnfinishedIf()
|
|
{
|
|
ConsList<Directive> previousIfElifElseOrRegion = GetPreviousIfElifElseOrRegion(_directives);
|
|
if (previousIfElifElseOrRegion != null && previousIfElifElseOrRegion.Any())
|
|
{
|
|
return previousIfElifElseOrRegion.Head.Kind != SyntaxKind.RegionDirectiveTrivia;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool HasPreviousIfOrElif()
|
|
{
|
|
ConsList<Directive> previousIfElifElseOrRegion = GetPreviousIfElifElseOrRegion(_directives);
|
|
if (previousIfElifElseOrRegion != null && previousIfElifElseOrRegion.Any())
|
|
{
|
|
if (previousIfElifElseOrRegion.Head.Kind != SyntaxKind.IfDirectiveTrivia)
|
|
{
|
|
return previousIfElifElseOrRegion.Head.Kind == SyntaxKind.ElifDirectiveTrivia;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool HasUnfinishedRegion()
|
|
{
|
|
ConsList<Directive> previousIfElifElseOrRegion = GetPreviousIfElifElseOrRegion(_directives);
|
|
if (previousIfElifElseOrRegion != null && previousIfElifElseOrRegion.Any())
|
|
{
|
|
return previousIfElifElseOrRegion.Head.Kind == SyntaxKind.RegionDirectiveTrivia;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public DirectiveStack Add(Directive directive)
|
|
{
|
|
switch (directive.Kind)
|
|
{
|
|
case SyntaxKind.EndIfDirectiveTrivia:
|
|
{
|
|
ConsList<Directive> previousIf = GetPreviousIf(_directives);
|
|
bool include;
|
|
if (previousIf != null && previousIf.Any())
|
|
{
|
|
return new DirectiveStack(CompleteIf(_directives, out include));
|
|
}
|
|
break;
|
|
}
|
|
case SyntaxKind.EndRegionDirectiveTrivia:
|
|
{
|
|
ConsList<Directive> previousRegion = GetPreviousRegion(_directives);
|
|
if (previousRegion != null && previousRegion.Any())
|
|
{
|
|
return new DirectiveStack(CompleteRegion(_directives));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return new DirectiveStack(new ConsList<Directive>(directive, _directives ?? ConsList<Directive>.Empty));
|
|
}
|
|
|
|
private static ConsList<Directive> CompleteIf(ConsList<Directive> stack, out bool include)
|
|
{
|
|
if (!stack.Any())
|
|
{
|
|
include = true;
|
|
return stack;
|
|
}
|
|
if (stack.Head.Kind == SyntaxKind.IfDirectiveTrivia)
|
|
{
|
|
include = stack.Head.BranchTaken;
|
|
return stack.Tail;
|
|
}
|
|
ConsList<Directive> val = CompleteIf(stack.Tail, out include);
|
|
SyntaxKind kind = stack.Head.Kind;
|
|
if (kind - 8549 <= SyntaxKind.List)
|
|
{
|
|
include = stack.Head.BranchTaken;
|
|
}
|
|
else if (include)
|
|
{
|
|
val = new ConsList<Directive>(stack.Head, val);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
private static ConsList<Directive> CompleteRegion(ConsList<Directive> stack)
|
|
{
|
|
if (!stack.Any())
|
|
{
|
|
return stack;
|
|
}
|
|
if (stack.Head.Kind == SyntaxKind.RegionDirectiveTrivia)
|
|
{
|
|
return stack.Tail;
|
|
}
|
|
ConsList<Directive> val = CompleteRegion(stack.Tail);
|
|
return new ConsList<Directive>(stack.Head, val);
|
|
}
|
|
|
|
private static ConsList<Directive>? GetPreviousIf(ConsList<Directive>? directives)
|
|
{
|
|
ConsList<Directive> val = directives;
|
|
while (val != null && val.Any())
|
|
{
|
|
if (val.Head.Kind == SyntaxKind.IfDirectiveTrivia)
|
|
{
|
|
return val;
|
|
}
|
|
val = val.Tail;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
private static ConsList<Directive>? GetPreviousIfElifElseOrRegion(ConsList<Directive>? directives)
|
|
{
|
|
ConsList<Directive> val = directives;
|
|
while (val != null && val.Any())
|
|
{
|
|
SyntaxKind kind = val.Head.Kind;
|
|
if (kind - 8548 <= (SyntaxKind)2 || kind == SyntaxKind.RegionDirectiveTrivia)
|
|
{
|
|
return val;
|
|
}
|
|
val = val.Tail;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
private static ConsList<Directive>? GetPreviousRegion(ConsList<Directive>? directives)
|
|
{
|
|
ConsList<Directive> val = directives;
|
|
while (val != null && val.Any() && val.Head.Kind != SyntaxKind.RegionDirectiveTrivia)
|
|
{
|
|
val = val.Tail;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
internal string GetDebuggerDisplay()
|
|
{
|
|
if (IsNull)
|
|
{
|
|
return "<null>";
|
|
}
|
|
if (IsEmpty)
|
|
{
|
|
return "[]";
|
|
}
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
ConsList<Directive> val = _directives;
|
|
while (val != null && val.Any())
|
|
{
|
|
if (stringBuilder.Length > 0)
|
|
{
|
|
stringBuilder.Insert(0, " | ");
|
|
}
|
|
stringBuilder.Insert(0, val.Head.GetDebuggerDisplay());
|
|
val = val.Tail;
|
|
}
|
|
return stringBuilder.ToString();
|
|
}
|
|
|
|
public bool IncrementallyEquivalent(DirectiveStack other)
|
|
{
|
|
ConsList<Directive> val = SkipInsignificantDirectives(_directives);
|
|
ConsList<Directive> val2 = SkipInsignificantDirectives(other._directives);
|
|
bool flag = val?.Any() ?? false;
|
|
bool flag2 = val2?.Any() ?? false;
|
|
while (flag && flag2)
|
|
{
|
|
if (!val.Head.IncrementallyEquivalent(val2.Head))
|
|
{
|
|
return false;
|
|
}
|
|
val = SkipInsignificantDirectives(val.Tail);
|
|
val2 = SkipInsignificantDirectives(val2.Tail);
|
|
flag = val?.Any() ?? false;
|
|
flag2 = val2?.Any() ?? false;
|
|
}
|
|
return flag == flag2;
|
|
}
|
|
|
|
private static ConsList<Directive>? SkipInsignificantDirectives(ConsList<Directive>? directives)
|
|
{
|
|
while (directives != null && directives.Any())
|
|
{
|
|
SyntaxKind kind = directives.Head.Kind;
|
|
if (kind - 8548 <= (SyntaxKind)7)
|
|
{
|
|
return directives;
|
|
}
|
|
directives = directives.Tail;
|
|
}
|
|
return directives;
|
|
}
|
|
}
|