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.Empty); private readonly ConsList? _directives; public bool IsNull => _directives == null; public bool IsEmpty => _directives == ConsList.Empty; private DirectiveStack(ConsList? 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 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 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 previousIfElifElseOrRegion = GetPreviousIfElifElseOrRegion(_directives); if (previousIfElifElseOrRegion != null && previousIfElifElseOrRegion.Any()) { return previousIfElifElseOrRegion.Head.Kind != SyntaxKind.RegionDirectiveTrivia; } return false; } public bool HasPreviousIfOrElif() { ConsList 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 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 previousIf = GetPreviousIf(_directives); bool include; if (previousIf != null && previousIf.Any()) { return new DirectiveStack(CompleteIf(_directives, out include)); } break; } case SyntaxKind.EndRegionDirectiveTrivia: { ConsList previousRegion = GetPreviousRegion(_directives); if (previousRegion != null && previousRegion.Any()) { return new DirectiveStack(CompleteRegion(_directives)); } break; } } return new DirectiveStack(new ConsList(directive, _directives ?? ConsList.Empty)); } private static ConsList CompleteIf(ConsList 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 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(stack.Head, val); } return val; } private static ConsList CompleteRegion(ConsList stack) { if (!stack.Any()) { return stack; } if (stack.Head.Kind == SyntaxKind.RegionDirectiveTrivia) { return stack.Tail; } ConsList val = CompleteRegion(stack.Tail); return new ConsList(stack.Head, val); } private static ConsList? GetPreviousIf(ConsList? directives) { ConsList val = directives; while (val != null && val.Any()) { if (val.Head.Kind == SyntaxKind.IfDirectiveTrivia) { return val; } val = val.Tail; } return val; } private static ConsList? GetPreviousIfElifElseOrRegion(ConsList? directives) { ConsList 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? GetPreviousRegion(ConsList? directives) { ConsList val = directives; while (val != null && val.Any() && val.Head.Kind != SyntaxKind.RegionDirectiveTrivia) { val = val.Tail; } return val; } internal string GetDebuggerDisplay() { if (IsNull) { return ""; } if (IsEmpty) { return "[]"; } StringBuilder stringBuilder = new StringBuilder(); ConsList 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 val = SkipInsignificantDirectives(_directives); ConsList 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? SkipInsignificantDirectives(ConsList? directives) { while (directives != null && directives.Any()) { SyntaxKind kind = directives.Head.Kind; if (kind - 8548 <= (SyntaxKind)7) { return directives; } directives = directives.Tail; } return directives; } }