| File: Syntax\InternalSyntax\CSharpSyntaxNode.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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. #nullable disable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Syntax.InternalSyntax; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax { [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal abstract class CSharpSyntaxNode : GreenNode { internal CSharpSyntaxNode(SyntaxKind kind) : base((ushort)kind) { GreenStats.NoteGreen(this); } internal CSharpSyntaxNode(SyntaxKind kind, int fullWidth) : base((ushort)kind, fullWidth) { GreenStats.NoteGreen(this); } internal CSharpSyntaxNode(SyntaxKind kind, DiagnosticInfo[] diagnostics) : base((ushort)kind, diagnostics) { GreenStats.NoteGreen(this); } internal CSharpSyntaxNode(SyntaxKind kind, DiagnosticInfo[] diagnostics, int fullWidth) : base((ushort)kind, diagnostics, fullWidth) { GreenStats.NoteGreen(this); } internal CSharpSyntaxNode(SyntaxKind kind, DiagnosticInfo[] diagnostics, SyntaxAnnotation[] annotations) : base((ushort)kind, diagnostics, annotations) { GreenStats.NoteGreen(this); } internal CSharpSyntaxNode(SyntaxKind kind, DiagnosticInfo[] diagnostics, SyntaxAnnotation[] annotations, int fullWidth) : base((ushort)kind, diagnostics, annotations, fullWidth) { GreenStats.NoteGreen(this); } public override string Language { get { return LanguageNames.CSharp; } } public SyntaxKind Kind { get { return (SyntaxKind)this.RawKind; } } public override string KindText => this.Kind.ToString(); public override int RawContextualKind { get { return this.RawKind; } } public override bool IsSkippedTokensTrivia => this.Kind == SyntaxKind.SkippedTokensTrivia; public override bool IsDocumentationCommentTrivia => SyntaxFacts.IsDocumentationCommentTrivia(this.Kind); public override int GetSlotOffset(int index) { // This implementation should not support arbitrary // length lists since the implementation is O(n). System.Diagnostics.Debug.Assert(index < 11); // Max. slots 11 (TypeDeclarationSyntax) int offset = 0; for (int i = 0; i < index; i++) { var child = this.GetSlot(i); if (child != null) { offset += child.FullWidth; } } return offset; } public SyntaxToken GetFirstToken() { return (SyntaxToken)this.GetFirstTerminal(); } public SyntaxToken GetLastToken() { return (SyntaxToken)this.GetLastTerminal(); } public SyntaxToken GetLastNonmissingToken() { return (SyntaxToken)this.GetLastNonmissingTerminal(); } public virtual GreenNode GetLeadingTrivia() { return null; } public override GreenNode GetLeadingTriviaCore() { return this.GetLeadingTrivia(); } public virtual GreenNode GetTrailingTrivia() { return null; } public override GreenNode GetTrailingTriviaCore() { return this.GetTrailingTrivia(); } public abstract TResult Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor); public abstract void Accept(CSharpSyntaxVisitor visitor); internal virtual DirectiveStack ApplyDirectives(DirectiveStack stack) { return ApplyDirectives(this, stack); } internal static DirectiveStack ApplyDirectives(GreenNode node, DirectiveStack stack) { if (node.ContainsDirectives) { for (int i = 0, n = node.SlotCount; i < n; i++) { var child = node.GetSlot(i); if (child != null) { stack = ApplyDirectivesToListOrNode(child, stack); } } } return stack; } internal static DirectiveStack ApplyDirectivesToListOrNode(GreenNode listOrNode, DirectiveStack stack) { // If we have a list of trivia, then that node is not actually a CSharpSyntaxNode. // Just defer to our standard ApplyDirectives helper as it will do the appropriate // walking of this list to ApplyDirectives to the children. if (listOrNode.RawKind == GreenNode.ListKind) { return ApplyDirectives(listOrNode, stack); } else { // Otherwise, we must have an actual piece of C# trivia. Just apply the stack // to that node directly. return ((CSharpSyntaxNode)listOrNode).ApplyDirectives(stack); } } internal virtual IList<DirectiveTriviaSyntax> GetDirectives() { if (this.ContainsDirectives) { var list = new List<DirectiveTriviaSyntax>(32); GetDirectives(this, list); return list; } return SpecializedCollections.EmptyList<DirectiveTriviaSyntax>(); } private static void GetDirectives(GreenNode node, List<DirectiveTriviaSyntax> directives) { if (node != null && node.ContainsDirectives) { var d = node as DirectiveTriviaSyntax; if (d != null) { directives.Add(d); } else { var t = node as SyntaxToken; if (t != null) { GetDirectives(t.GetLeadingTrivia(), directives); GetDirectives(t.GetTrailingTrivia(), directives); } else { for (int i = 0, n = node.SlotCount; i < n; i++) { GetDirectives(node.GetSlot(i), directives); } } } } } /// <summary> /// Should only be called during construction. /// </summary> /// <remarks> /// This should probably be an extra constructor parameter, but we don't need more constructor overloads. /// </remarks> protected void SetFactoryContext(SyntaxFactoryContext context) { if (context.IsInAsync) { SetFlags(NodeFlags.FactoryContextIsInAsync); } if (context.IsInQuery) { SetFlags(NodeFlags.FactoryContextIsInQuery); } if (context.IsInFieldKeywordContext) { SetFlags(NodeFlags.FactoryContextIsInFieldKeywordContext); } } public sealed override CodeAnalysis.SyntaxToken CreateSeparator(SyntaxNode element) { return CSharp.SyntaxFactory.Token(SyntaxKind.CommaToken); } public override bool IsTriviaWithEndOfLine() { return this.Kind == SyntaxKind.EndOfLineTrivia || this.Kind == SyntaxKind.SingleLineCommentTrivia; } // Use conditional weak table so we always return same identity for structured trivia // // As there are commonly few structured trivia per parent, use a SmallDictionary for // mapping from trivia to StructuredTriviaSyntax. Testing against roslyn, of parents // containing structured trivia: // 81.2% contain 1 structured trivia // 96.5% contain 2 or fewer structured trivia // 99.6% contain 4 or fewer structured trivia // 100% contain 7 or fewer structured trivia private static readonly ConditionalWeakTable<SyntaxNode, SmallDictionary<CodeAnalysis.SyntaxTrivia, SyntaxNode>> s_structuresTable = new ConditionalWeakTable<SyntaxNode, SmallDictionary<CodeAnalysis.SyntaxTrivia, SyntaxNode>>(); /// <summary> /// Gets the syntax node represented the structure of this trivia, if any. The HasStructure property can be used to /// determine if this trivia has structure. /// </summary> /// <returns> /// A CSharpSyntaxNode derived from StructuredTriviaSyntax, with the structured view of this trivia node. /// If this trivia node does not have structure, returns null. /// </returns> /// <remarks> /// Some types of trivia have structure that can be accessed as additional syntax nodes. /// These forms of trivia include: /// directives, where the structure describes the structure of the directive. /// documentation comments, where the structure describes the XML structure of the comment. /// skipped tokens, where the structure describes the tokens that were skipped by the parser. /// </remarks> public override SyntaxNode GetStructure(Microsoft.CodeAnalysis.SyntaxTrivia trivia) { if (!trivia.HasStructure) { return null; } var parent = trivia.Token.Parent; if (parent is null) { return CSharp.Syntax.StructuredTriviaSyntax.Create(trivia); } SyntaxNode structure; var structsInParent = s_structuresTable.GetOrCreateValue(parent); lock (structsInParent) { if (!structsInParent.TryGetValue(trivia, out structure)) { structure = CSharp.Syntax.StructuredTriviaSyntax.Create(trivia); structsInParent.Add(trivia, structure); } } return structure; } } }