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);