diff --git a/src/Compilers/Core/Portable/Syntax/GreenNode.cs b/src/Compilers/Core/Portable/Syntax/GreenNode.cs index ce0d2d07a96ed..13625bb2847fe 100644 --- a/src/Compilers/Core/Portable/Syntax/GreenNode.cs +++ b/src/Compilers/Core/Portable/Syntax/GreenNode.cs @@ -104,7 +104,7 @@ protected GreenNode(ushort kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation if (annotation == null) throw new ArgumentException(paramName: nameof(annotations), message: "" /*CSharpResources.ElementsCannotBeNull*/); } - this.flags |= NodeFlags.ContainsAnnotations; + this.flags |= (NodeFlags.HasAnnotationsDirectly | NodeFlags.ContainsAnnotations); s_annotationsTable.Add(this, annotations); } } @@ -119,7 +119,7 @@ protected GreenNode(ushort kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation if (annotation == null) throw new ArgumentException(paramName: nameof(annotations), message: "" /*CSharpResources.ElementsCannotBeNull*/); } - this.flags |= NodeFlags.ContainsAnnotations; + this.flags |= (NodeFlags.HasAnnotationsDirectly | NodeFlags.ContainsAnnotations); s_annotationsTable.Add(this, annotations); } } @@ -260,19 +260,39 @@ public virtual int FindSlotIndexContainingOffset(int offset) internal enum NodeFlags : ushort { None = 0, - ContainsDiagnostics = 1 << 0, - ContainsStructuredTrivia = 1 << 1, - ContainsDirectives = 1 << 2, - ContainsSkippedText = 1 << 3, - ContainsAnnotations = 1 << 4, - IsNotMissing = 1 << 5, - ContainsAttributes = 1 << 6, - - FactoryContextIsInAsync = 1 << 7, - FactoryContextIsInQuery = 1 << 8, + /// + /// If this node is missing or not. We use a non-zero value for the not-missing case so that this value + /// automatically merges upwards when building parent nodes. In other words, once we have one node that is + /// not-missing, all nodes above it are definitely not-missing as well. + /// + IsNotMissing = 1 << 0, + /// + /// If this node directly has annotations (not its descendants). can be + /// used to determine if a node or any of its descendants has annotations. + /// + HasAnnotationsDirectly = 1 << 1, + + FactoryContextIsInAsync = 1 << 2, + FactoryContextIsInQuery = 1 << 3, FactoryContextIsInIterator = FactoryContextIsInQuery, // VB does not use "InQuery", but uses "InIterator" instead - InheritMask = ContainsDiagnostics | ContainsStructuredTrivia | ContainsDirectives | ContainsSkippedText | ContainsAnnotations | ContainsAttributes | IsNotMissing, + // Flags that are inherited upwards when building parent nodes. They should all start with "Contains" to + // indicate that the information could be found on it or anywhere in its children. + + /// + /// If this node, or any of its descendants has annotations attached to them. + /// + ContainsAnnotations = 1 << 4, + /// + /// If this node, or any of its descendants has attributes attached to it. + /// + ContainsAttributes = 1 << 5, + ContainsDiagnostics = 1 << 6, + ContainsDirectives = 1 << 7, + ContainsSkippedText = 1 << 8, + ContainsStructuredTrivia = 1 << 9, + + InheritMask = IsNotMissing | ContainsAnnotations | ContainsAttributes | ContainsDiagnostics | ContainsDirectives | ContainsSkippedText | ContainsStructuredTrivia, } internal NodeFlags Flags @@ -370,6 +390,15 @@ public bool ContainsAnnotations return (this.flags & NodeFlags.ContainsAnnotations) != 0; } } + + public bool HasAnnotationsDirectly + { + get + { + return (this.flags & NodeFlags.HasAnnotationsDirectly) != 0; + } + } + #endregion #region Spans @@ -541,17 +570,15 @@ private static IEnumerable GetAnnotationsSlow(SyntaxAnnotation public SyntaxAnnotation[] GetAnnotations() { - if (this.ContainsAnnotations) - { - SyntaxAnnotation[]? annotations; - if (s_annotationsTable.TryGetValue(this, out annotations)) - { - System.Diagnostics.Debug.Assert(annotations.Length != 0, "we should return nonempty annotations or NoAnnotations"); - return annotations; - } - } + if (!this.HasAnnotationsDirectly) + return s_noAnnotations; - return s_noAnnotations; + var found = s_annotationsTable.TryGetValue(this, out var annotations); + Debug.Assert(found, "We must be able to find annotations since we had the bit set on ourselves"); + Debug.Assert(annotations != null, "annotations should not be null"); + Debug.Assert(annotations != s_noAnnotations, "annotations should not be s_noAnnotations"); + Debug.Assert(annotations.Length != 0, "annotations should be non-empty"); + return annotations; } internal abstract GreenNode SetAnnotations(SyntaxAnnotation[]? annotations);