diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 4631a0eea939f..8b0800ab52f00 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -15864,6 +15864,7 @@ internal sealed partial class AttributeSyntax : CSharpSyntaxNode internal AttributeSyntax(SyntaxKind kind, NameSyntax name, AttributeArgumentListSyntax? argumentList, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { + SetFlags(NodeFlags.ContainsAttributes); this.SlotCount = 2; this.AdjustFlagsAndWidth(name); this.name = name; @@ -15878,6 +15879,7 @@ internal AttributeSyntax(SyntaxKind kind, NameSyntax name, AttributeArgumentList : base(kind) { this.SetFactoryContext(context); + SetFlags(NodeFlags.ContainsAttributes); this.SlotCount = 2; this.AdjustFlagsAndWidth(name); this.name = name; @@ -15891,6 +15893,7 @@ internal AttributeSyntax(SyntaxKind kind, NameSyntax name, AttributeArgumentList internal AttributeSyntax(SyntaxKind kind, NameSyntax name, AttributeArgumentListSyntax? argumentList) : base(kind) { + SetFlags(NodeFlags.ContainsAttributes); this.SlotCount = 2; this.AdjustFlagsAndWidth(name); this.name = name; @@ -30780,17 +30783,7 @@ public AttributeSyntax Attribute(NameSyntax name, AttributeArgumentListSyntax? a if (name == null) throw new ArgumentNullException(nameof(name)); #endif - int hash; - var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.Attribute, name, argumentList, this.context, out hash); - if (cached != null) return (AttributeSyntax)cached; - - var result = new AttributeSyntax(SyntaxKind.Attribute, name, argumentList, this.context); - if (hash >= 0) - { - SyntaxNodeCache.AddNode(result, hash); - } - - return result; + return new AttributeSyntax(SyntaxKind.Attribute, name, argumentList, this.context); } public AttributeArgumentListSyntax AttributeArgumentList(SyntaxToken openParenToken, CoreSyntax.SeparatedSyntaxList arguments, SyntaxToken closeParenToken) @@ -35996,17 +35989,7 @@ public static AttributeSyntax Attribute(NameSyntax name, AttributeArgumentListSy if (name == null) throw new ArgumentNullException(nameof(name)); #endif - int hash; - var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.Attribute, name, argumentList, out hash); - if (cached != null) return (AttributeSyntax)cached; - - var result = new AttributeSyntax(SyntaxKind.Attribute, name, argumentList); - if (hash >= 0) - { - SyntaxNodeCache.AddNode(result, hash); - } - - return result; + return new AttributeSyntax(SyntaxKind.Attribute, name, argumentList); } public static AttributeArgumentListSyntax AttributeArgumentList(SyntaxToken openParenToken, CoreSyntax.SeparatedSyntaxList arguments, SyntaxToken closeParenToken) diff --git a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs index 4014712c69783..3be1c2ba29827 100644 --- a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs +++ b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.SourceGeneration; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -21,9 +19,6 @@ private CSharpSyntaxHelper() public override bool IsCaseSensitive => true; - protected override int AttributeListKind - => (int)SyntaxKind.AttributeList; - public override bool IsValidIdentifier(string name) => SyntaxFacts.IsValidIdentifier(name); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionExpressionParsingTests.cs index 53ac300301e9b..75cde7ce70fbc 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CollectionExpressionParsingTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -17096,4 +17097,35 @@ public void TreatKeywordAsCollectionExprElement() } EOF(); } + + [Theory, CombinatorialData] + public void CollectionExpressionParsingSlotCounts([CombinatorialRange(1, 20)] int count) + { + // Validate no errors for collections with small and large number of elements. Importantly, we want to test the + // boundary points where the slot count of the collection crosses over the amount that can be directly stored in + // the node, versus the slot count stored in subclass nodes. + var text = $"[{string.Join(", ", Enumerable.Range(1, count).Select(i => $"A{i}"))}]"; + + UsingExpression(text, TestOptions.Regular); + + N(SyntaxKind.CollectionExpression); + N(SyntaxKind.OpenBracketToken); + + for (var i = 1; i <= count; i++) + { + N(SyntaxKind.ExpressionElement); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, $"A{i}"); + } + + if (i < count) + { + N(SyntaxKind.CommaToken); + } + } + + N(SyntaxKind.CloseBracketToken); + EOF(); + } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs b/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs index 52906bd178683..9e9a46124b152 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs @@ -32,14 +32,12 @@ internal interface ISyntaxHelper void AddAliases(GreenNode node, ArrayBuilder<(string aliasName, string symbolName)> aliases, bool global); void AddAliases(CompilationOptions options, ArrayBuilder<(string aliasName, string symbolName)> aliases); - bool ContainsAttributeList(SyntaxNode root); bool ContainsGlobalAliases(SyntaxNode root); } internal abstract class AbstractSyntaxHelper : ISyntaxHelper { public abstract bool IsCaseSensitive { get; } - protected abstract int AttributeListKind { get; } public abstract bool IsValidIdentifier(string name); @@ -60,18 +58,5 @@ internal abstract class AbstractSyntaxHelper : ISyntaxHelper public abstract void AddAliases(CompilationOptions options, ArrayBuilder<(string aliasName, string symbolName)> aliases); public abstract bool ContainsGlobalAliases(SyntaxNode root); - - public bool ContainsAttributeList(SyntaxNode root) - { - var attributeListKind = this.AttributeListKind; - - foreach (var node in root.Green.EnumerateNodes()) - { - if (node.RawKind == attributeListKind) - return true; - } - - return false; - } } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs index c3b7b48cba0c1..f2fa22112ded2 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs @@ -234,6 +234,10 @@ void processMember(SyntaxNode member) { cancellationToken.ThrowIfCancellationRequested(); + // Don't bother descending into nodes that don't contain attributes. + if (!member.ContainsAttributes) + return; + // nodes can be arbitrarily deep. Use an explicit stack over recursion to prevent a stack-overflow. var nodeStack = s_nodeStackPool.Allocate(); nodeStack.Push(member); @@ -244,6 +248,10 @@ void processMember(SyntaxNode member) { var node = nodeStack.Pop(); + // Don't bother descending into nodes that don't contain attributes. + if (!node.ContainsAttributes) + continue; + if (syntaxHelper.IsAttributeList(node)) { foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node)) diff --git a/src/Compilers/Core/Portable/Syntax/GreenNode.NodeFlagsAndSlotCount.cs b/src/Compilers/Core/Portable/Syntax/GreenNode.NodeFlagsAndSlotCount.cs new file mode 100644 index 0000000000000..f3249a121238c --- /dev/null +++ b/src/Compilers/Core/Portable/Syntax/GreenNode.NodeFlagsAndSlotCount.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis +{ + internal abstract partial class GreenNode + { + /// + /// Combination of and stored in a single 16bit value. + /// + private struct NodeFlagsAndSlotCount + { + /// + /// 4 bits for the SlotCount. This allows slot counts of 0-14 to be stored as a direct byte. All 1s + /// indicates that the slot count must be computed. + /// + private const ushort SlotCountMask = 0b1111000000000000; + private const ushort NodeFlagsMask = 0b0000111111111111; + + private const int SlotCountShift = 12; + + /// + /// 12 bits for the NodeFlags. This allows for up to 12 distinct bits to be stored to designate interesting + /// aspects of a node. + /// + + /// + /// CCCCFFFFFFFFFFFF for Count bits then Flag bits. + /// + private ushort _data; + + /// + /// Returns the slot count if it was small enough to be stored directly in this object. Otherwise, returns + /// to indicate it could not be directly stored. + /// + public byte SmallSlotCount + { + readonly get + { + var shifted = _data >> SlotCountShift; + Debug.Assert(shifted <= SlotCountTooLarge); + return (byte)shifted; + } + + set + { + if (value > SlotCountTooLarge) + value = SlotCountTooLarge; + + // Clear out everything but the node-flags, and then assign into the slot-count segment. + _data = (ushort)((_data & NodeFlagsMask) | (value << SlotCountShift)); + } + } + + public NodeFlags NodeFlags + { + readonly get + { + return (NodeFlags)(_data & NodeFlagsMask); + } + + set + { + Debug.Assert((ushort)value <= NodeFlagsMask); + + // Clear out everything but the slot-count, and then assign into the node-flags segment. + _data = (ushort)((_data & SlotCountMask) | (ushort)value); + } + } + } + } +} diff --git a/src/Compilers/Core/Portable/Syntax/GreenNode.cs b/src/Compilers/Core/Portable/Syntax/GreenNode.cs index ba2be0cb0b454..ce0d2d07a96ed 100644 --- a/src/Compilers/Core/Portable/Syntax/GreenNode.cs +++ b/src/Compilers/Core/Portable/Syntax/GreenNode.cs @@ -25,11 +25,33 @@ private string GetDebuggerDisplay() internal const int ListKind = 1; + // Pack the kind, node-flags, slot-count, and full-width into 64bits. Note: if we need more bits in the future + // (say for additional node-flags), we can always directly use a packed int64 here, and manage where all these + // bits go manually. + + /// + /// Value used to indicate the slot count was too large to be encoded directly in our + /// value. Callers will have to store the value elsewhere and retrieve the full value themselves. + /// + protected const int SlotCountTooLarge = 0b0000000000001111; + private readonly ushort _kind; - protected NodeFlags flags; - private byte _slotCount; + private NodeFlagsAndSlotCount _nodeFlagsAndSlotCount; private int _fullWidth; + protected NodeFlags flags + { + get => _nodeFlagsAndSlotCount.NodeFlags; + set => _nodeFlagsAndSlotCount.NodeFlags = value; + } + + /// > + private byte _slotCount + { + get => _nodeFlagsAndSlotCount.SmallSlotCount; + set => _nodeFlagsAndSlotCount.SmallSlotCount = value; + } + private static readonly ConditionalWeakTable s_diagnosticsTable = new ConditionalWeakTable(); @@ -142,12 +164,7 @@ public int SlotCount get { int count = _slotCount; - if (count == byte.MaxValue) - { - count = GetSlotCount(); - } - - return count; + return count == SlotCountTooLarge ? GetSlotCount() : count; } protected set @@ -234,8 +251,13 @@ public virtual int FindSlotIndexContainingOffset(int offset) #endregion #region Flags + + /// + /// Special flags a node can have. Note: while this is typed as being `ushort`, we can only practically use 12 + /// of those 16 bits as we use the remaining 4 bits to store the slot count of a node. + /// [Flags] - internal enum NodeFlags : byte + internal enum NodeFlags : ushort { None = 0, ContainsDiagnostics = 1 << 0, @@ -244,12 +266,13 @@ internal enum NodeFlags : byte ContainsSkippedText = 1 << 3, ContainsAnnotations = 1 << 4, IsNotMissing = 1 << 5, + ContainsAttributes = 1 << 6, - FactoryContextIsInAsync = 1 << 6, - FactoryContextIsInQuery = 1 << 7, + FactoryContextIsInAsync = 1 << 7, + FactoryContextIsInQuery = 1 << 8, FactoryContextIsInIterator = FactoryContextIsInQuery, // VB does not use "InQuery", but uses "InIterator" instead - InheritMask = ContainsDiagnostics | ContainsStructuredTrivia | ContainsDirectives | ContainsSkippedText | ContainsAnnotations | IsNotMissing, + InheritMask = ContainsDiagnostics | ContainsStructuredTrivia | ContainsDirectives | ContainsSkippedText | ContainsAnnotations | ContainsAttributes | IsNotMissing, } internal NodeFlags Flags @@ -324,6 +347,14 @@ public bool ContainsDirectives } } + public bool ContainsAttributes + { + get + { + return (this.flags & NodeFlags.ContainsAttributes) != 0; + } + } + public bool ContainsDiagnostics { get diff --git a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.WithManyChildren.cs b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.WithManyChildren.cs index 503d907700cec..842d3e8f3a727 100644 --- a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.WithManyChildren.cs +++ b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.WithManyChildren.cs @@ -29,14 +29,13 @@ internal WithManyChildrenBase(DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? private void InitializeChildren() { int n = children.Length; - if (n < byte.MaxValue) - { - this.SlotCount = (byte)n; - } - else - { - this.SlotCount = byte.MaxValue; - } + + // Attempt to store small lengths directly into the storage provided within GreenNode. If, however, the + // length is too long, we will store a special value in the space, which will `SlotCount` to call back + // into `GetSlotCount` to retrieve the true length. + this.SlotCount = n < SlotCountTooLarge + ? (byte)n + : SlotCountTooLarge; for (int i = 0; i < children.Length; i++) { diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index 9f77eb52eeb95..ba5ab7ed1b3dc 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -442,6 +442,8 @@ public bool ContainsDiagnostics /// public bool ContainsDirectives => this.Green.ContainsDirectives; + internal bool ContainsAttributes => this.Green.ContainsAttributes; + /// /// Returns true if this node contains any directives (e.g. #if, #nullable, etc.) within it with a matching kind. /// diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs b/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs index f4e25ff87614f..6e9655d6fcf14 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs @@ -433,7 +433,7 @@ internal SourceGeneratorSyntaxTreeInfo GetSourceGeneratorInfo( if (syntaxHelper.ContainsGlobalAliases(root)) result |= SourceGeneratorSyntaxTreeInfo.ContainsGlobalAliases; - if (syntaxHelper.ContainsAttributeList(root)) + if (root.ContainsAttributes) result |= SourceGeneratorSyntaxTreeInfo.ContainsAttributeList; _sourceGeneratorInfo = result; diff --git a/src/Compilers/VisualBasic/Portable/Generated/Syntax.xml.Internal.Generated.vb b/src/Compilers/VisualBasic/Portable/Generated/Syntax.xml.Internal.Generated.vb index 611c758ee9a67..9473afa77c362 100644 --- a/src/Compilers/VisualBasic/Portable/Generated/Syntax.xml.Internal.Generated.vb +++ b/src/Compilers/VisualBasic/Portable/Generated/Syntax.xml.Internal.Generated.vb @@ -8178,6 +8178,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Me._argumentList = argumentList End If + SetFlags(NodeFlags.ContainsAttributes) End Sub Friend Sub New(ByVal kind As SyntaxKind, target As AttributeTargetSyntax, name As TypeSyntax, argumentList As ArgumentListSyntax, context As ISyntaxFactoryContext) @@ -8196,6 +8197,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Me._argumentList = argumentList End If + SetFlags(NodeFlags.ContainsAttributes) End Sub Friend Sub New(ByVal kind As SyntaxKind, ByVal errors as DiagnosticInfo(), ByVal annotations as SyntaxAnnotation(), target As AttributeTargetSyntax, name As TypeSyntax, argumentList As ArgumentListSyntax) @@ -8213,6 +8215,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Me._argumentList = argumentList End If + SetFlags(NodeFlags.ContainsAttributes) End Sub Friend Overrides Function CreateRed(ByVal parent As SyntaxNode, ByVal startLocation As Integer) As SyntaxNode @@ -37511,19 +37514,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ''' Friend Shared Function Attribute(target As AttributeTargetSyntax, name As TypeSyntax, argumentList As ArgumentListSyntax) As AttributeSyntax Debug.Assert(name IsNot Nothing) - - Dim hash As Integer - Dim cached = SyntaxNodeCache.TryGetNode(SyntaxKind.Attribute, target, name, argumentList, hash) - If cached IsNot Nothing Then - Return DirectCast(cached, AttributeSyntax) - End If - - Dim result = New AttributeSyntax(SyntaxKind.Attribute, target, name, argumentList) - If hash >= 0 Then - SyntaxNodeCache.AddNode(result, hash) - End If - - Return result + Return New AttributeSyntax(SyntaxKind.Attribute, target, name, argumentList) End Function @@ -49589,19 +49580,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ''' Friend Function Attribute(target As AttributeTargetSyntax, name As TypeSyntax, argumentList As ArgumentListSyntax) As AttributeSyntax Debug.Assert(name IsNot Nothing) - - Dim hash As Integer - Dim cached = VisualBasicSyntaxNodeCache.TryGetNode(SyntaxKind.Attribute, target, name, argumentList, _factoryContext, hash) - If cached IsNot Nothing Then - Return DirectCast(cached, AttributeSyntax) - End If - - Dim result = New AttributeSyntax(SyntaxKind.Attribute, target, name, argumentList, _factoryContext) - If hash >= 0 Then - SyntaxNodeCache.AddNode(result, hash) - End If - - Return result + Return New AttributeSyntax(SyntaxKind.Attribute, target, name, argumentList, _factoryContext) End Function diff --git a/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb index 6d3572dbb55f0..80f5312759192 100644 --- a/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb +++ b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb @@ -2,11 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Runtime.CompilerServices -Imports System.Runtime.InteropServices -Imports System.Threading Imports Microsoft.CodeAnalysis.PooledObjects -Imports Microsoft.CodeAnalysis.SourceGeneration Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic @@ -20,8 +16,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides ReadOnly Property IsCaseSensitive As Boolean = False - Protected Overrides ReadOnly Property AttributeListKind As Integer = SyntaxKind.AttributeList - Public Overrides Function IsValidIdentifier(name As String) As Boolean Return SyntaxFacts.IsValidIdentifier(name) End Function diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/SourceWriter.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/SourceWriter.cs index 7f05d6743aa7d..4a999bf7f0d92 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/SourceWriter.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/SourceWriter.cs @@ -173,7 +173,7 @@ private void WriteGreenType(TreeType node) WriteLine(", DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations)"); WriteLine(" : base(kind, diagnostics, annotations)"); OpenBlock(); - WriteCtorBody(valueFields, nodeFields); + WriteCtorBody(nd, valueFields, nodeFields); CloseBlock(); // write constructor with async @@ -186,7 +186,7 @@ private void WriteGreenType(TreeType node) WriteLine(" : base(kind)"); OpenBlock(); WriteLine("this.SetFactoryContext(context);"); - WriteCtorBody(valueFields, nodeFields); + WriteCtorBody(nd, valueFields, nodeFields); CloseBlock(); // write constructor without diagnostics and annotations @@ -198,7 +198,7 @@ private void WriteGreenType(TreeType node) WriteLine(")"); WriteLine(" : base(kind)"); OpenBlock(); - WriteCtorBody(valueFields, nodeFields); + WriteCtorBody(nd, valueFields, nodeFields); CloseBlock(); WriteLine(); @@ -289,8 +289,13 @@ private void WriteGreenNodeConstructorArgs(List nodeFields, List v } } - private void WriteCtorBody(List valueFields, List nodeFields) + private void WriteCtorBody(Node node, List valueFields, List nodeFields) { + if (node.Name == "AttributeSyntax") + { + WriteLine("SetFlags(NodeFlags.ContainsAttributes);"); + } + // constructor body WriteLine($"this.SlotCount = {nodeFields.Count};"); @@ -581,6 +586,7 @@ private void WriteGreenFactory(Node nd, bool withSyntaxFactoryContext = false) if (nd.Name != "SkippedTokensTriviaSyntax" && nd.Name != "DocumentationCommentTriviaSyntax" && nd.Name != "IncompleteMemberSyntax" && + nd.Name != "AttributeSyntax" && valueFields.Count + nodeFields.Count <= 3) { //int hash; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeFactoryWriter.vb b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeFactoryWriter.vb index 801c76f8aadec..43115c95069c7 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeFactoryWriter.vb +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeFactoryWriter.vb @@ -264,6 +264,7 @@ Friend Class GreenNodeFactoryWriter nodeStructure.Name = "SkippedTokensTriviaSyntax" OrElse nodeStructure.Name = "DocumentationCommentTriviaSyntax" OrElse nodeStructure.Name.EndsWith("DirectiveTriviaSyntax", StringComparison.Ordinal) OrElse + nodeStructure.Name = "AttributeSyntax" OrElse allFields.Count + allChildren.Count > 3) Then _writer.Write(" Return New {0}(", StructureTypeName(nodeStructure)) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb index f9030e0b901f2..12f1c7544afda 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb @@ -109,9 +109,9 @@ Friend Class GreenNodeWriter GenerateNodeStructureMembers(nodeStructure) ' Create the constructor. - GenerateNodeStructureConstructor(nodeStructure, False, noExtra:=True) - GenerateNodeStructureConstructor(nodeStructure, False, noExtra:=True, contextual:=True) - GenerateNodeStructureConstructor(nodeStructure, False) + GenerateNodeStructureConstructor(nodeStructure, noExtra:=True) + GenerateNodeStructureConstructor(nodeStructure, noExtra:=True, contextual:=True) + GenerateNodeStructureConstructor(nodeStructure) GenerateCreateRed(nodeStructure) @@ -293,7 +293,6 @@ Friend Class GreenNodeWriter ' Generate constructor for a node structure Private Sub GenerateNodeStructureConstructor(nodeStructure As ParseNodeStructure, - isRaw As Boolean, Optional noExtra As Boolean = False, Optional contextual As Boolean = False) @@ -420,6 +419,10 @@ Friend Class GreenNodeWriter _writer.WriteLine(" SetFlags(NodeFlags.ContainsDirectives)") End If + If StructureTypeName(nodeStructure) = "AttributeSyntax" Then + _writer.WriteLine(" SetFlags(NodeFlags.ContainsAttributes)") + End If + ' Generate End Sub _writer.WriteLine(" End Sub") _writer.WriteLine()