/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs
941 строка
40 KB
Cyrus Najmabadi
Switch diagnostic computation into a normal yield-based enumerator (#80384)
23 сен 2025, 09:28
Не верифицирован
23 сен 2025, 09:28
d9b59ea
Код
Авторство
О чём код?
// 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; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// The parsed representation of a C# source document. /// </summary> public abstract partial class CSharpSyntaxTree : SyntaxTree { internal static readonly SyntaxTree Dummy = new DummySyntaxTree(); private InternalSyntax.DirectiveStack _lazyDirectives; /// <summary> /// Stores positions where preprocessor state changes. Sorted by position. /// The updated state can be found in <see cref="_preprocessorStates"/> array at the same index. /// </summary> private ImmutableArray<int> _preprocessorStateChangePositions; /// <summary> /// Preprocessor states corresponding to positions in <see cref="_preprocessorStateChangePositions"/>. /// </summary> private ImmutableArray<InternalSyntax.DirectiveStack> _preprocessorStates; public CSharpSyntaxTree() : this(directives: default) { } internal CSharpSyntaxTree(InternalSyntax.DirectiveStack directives) { _lazyDirectives = directives; } /// <summary> /// The options used by the parser to produce the syntax tree. /// </summary> public new abstract CSharpParseOptions Options { get; } // REVIEW: I would prefer to not expose CloneAsRoot and make the functionality // internal to CaaS layer, to ensure that for a given SyntaxTree there can not // be multiple trees claiming to be its children. // // However, as long as we provide GetRoot extensibility point on SyntaxTree // the guarantee above cannot be implemented and we have to provide some way for // creating root nodes. // // Therefore I place CloneAsRoot API on SyntaxTree and make it protected to // at least limit its visibility to SyntaxTree extenders. /// <summary> /// Produces a clone of a <see cref="CSharpSyntaxNode"/> which will have current syntax tree as its parent. /// /// Caller must guarantee that if the same instance of <see cref="CSharpSyntaxNode"/> makes multiple calls /// to this function, only one result is observable. /// </summary> /// <typeparam name="T">Type of the syntax node.</typeparam> /// <param name="node">The original syntax node.</param> /// <returns>A clone of the original syntax node that has current <see cref="CSharpSyntaxTree"/> as its parent.</returns> protected T CloneNodeAsRoot<T>(T node) where T : CSharpSyntaxNode { return CSharpSyntaxNode.CloneNodeAsRoot(node, this); } /// <summary> /// Gets the root node of the syntax tree. /// </summary> public new abstract CSharpSyntaxNode GetRoot(CancellationToken cancellationToken = default); /// <summary> /// Gets the root node of the syntax tree if it is already available. /// </summary> public abstract bool TryGetRoot([NotNullWhen(true)] out CSharpSyntaxNode? root); /// <summary> /// Gets the root node of the syntax tree asynchronously. /// </summary> /// <remarks> /// By default, the work associated with this method will be executed immediately on the current thread. /// Implementations that wish to schedule this work differently should override <see cref="GetRootAsync(CancellationToken)"/>. /// </remarks> public new virtual Task<CSharpSyntaxNode> GetRootAsync(CancellationToken cancellationToken = default) { return Task.FromResult(this.TryGetRoot(out CSharpSyntaxNode? node) ? node : this.GetRoot(cancellationToken)); } /// <summary> /// Gets the root of the syntax tree statically typed as <see cref="CompilationUnitSyntax"/>. /// </summary> /// <remarks> /// Ensure that <see cref="SyntaxTree.HasCompilationUnitRoot"/> is true for this tree prior to invoking this method. /// </remarks> /// <exception cref="InvalidCastException">Throws this exception if <see cref="SyntaxTree.HasCompilationUnitRoot"/> is false.</exception> public CompilationUnitSyntax GetCompilationUnitRoot(CancellationToken cancellationToken = default) { return (CompilationUnitSyntax)this.GetRoot(cancellationToken); } /// <summary> /// Determines if two trees are the same, disregarding trivia differences. /// </summary> /// <param name="tree">The tree to compare against.</param> /// <param name="topLevel"> /// If true then the trees are equivalent if the contained nodes and tokens declaring metadata visible symbolic information are equivalent, /// ignoring any differences of nodes inside method bodies or initializer expressions, otherwise all nodes and tokens must be equivalent. /// </param> public override bool IsEquivalentTo(SyntaxTree tree, bool topLevel = false) { return SyntaxFactory.AreEquivalent(this, tree, topLevel); } internal bool HasReferenceDirectives { get { Debug.Assert(HasCompilationUnitRoot); return Options.Kind == SourceCodeKind.Script && GetCompilationUnitRoot().HasReferenceDirectives; } } internal bool HasReferenceOrLoadDirectives { get { Debug.Assert(HasCompilationUnitRoot); if (Options.Kind == SourceCodeKind.Script) { var compilationUnitRoot = GetCompilationUnitRoot(); return compilationUnitRoot.HasReferenceDirectives || compilationUnitRoot.HasLoadDirectives; } return false; } } #region Preprocessor Symbols internal InternalSyntax.DirectiveStack GetDirectives() { if (_lazyDirectives.IsNull) { InternalSyntax.DirectiveStack.InterlockedInitialize(ref _lazyDirectives, GetRoot().CsGreen.ApplyDirectives(InternalSyntax.DirectiveStack.Empty)); } Debug.Assert(!_lazyDirectives.IsNull); return _lazyDirectives; } internal bool IsAnyPreprocessorSymbolDefined(ImmutableArray<string> conditionalSymbols) { var directives = GetDirectives(); foreach (string conditionalSymbol in conditionalSymbols) { if (IsPreprocessorSymbolDefined(directives, conditionalSymbol)) { return true; } } return false; } private bool IsPreprocessorSymbolDefined(InternalSyntax.DirectiveStack directives, string symbolName) { switch (directives.IsDefined(symbolName)) { case InternalSyntax.DefineState.Defined: return true; case InternalSyntax.DefineState.Undefined: return false; default: return this.Options.PreprocessorSymbols.Contains(symbolName); } } internal bool IsPreprocessorSymbolDefined(string symbolName, int position) { if (_preprocessorStateChangePositions.IsDefault) { BuildPreprocessorStateChangeMap(); } int searchResult = _preprocessorStateChangePositions.BinarySearch(position); InternalSyntax.DirectiveStack directives; if (searchResult < 0) { searchResult = (~searchResult) - 1; if (searchResult >= 0) { directives = _preprocessorStates[searchResult]; } else { directives = InternalSyntax.DirectiveStack.Empty; } } else { directives = _preprocessorStates[searchResult]; } return IsPreprocessorSymbolDefined(directives, symbolName); } private void BuildPreprocessorStateChangeMap() { InternalSyntax.DirectiveStack currentState = InternalSyntax.DirectiveStack.Empty; var positions = ArrayBuilder<int>.GetInstance(); var states = ArrayBuilder<InternalSyntax.DirectiveStack>.GetInstance(); foreach (DirectiveTriviaSyntax directive in this.GetRoot().GetDirectives(d => { switch (d.Kind()) { case SyntaxKind.IfDirectiveTrivia: case SyntaxKind.ElifDirectiveTrivia: case SyntaxKind.ElseDirectiveTrivia: case SyntaxKind.EndIfDirectiveTrivia: case SyntaxKind.DefineDirectiveTrivia: case SyntaxKind.UndefDirectiveTrivia: return true; default: return false; } })) { currentState = directive.ApplyDirectives(currentState); switch (directive.Kind()) { case SyntaxKind.IfDirectiveTrivia: // #if directive doesn't affect the set of defined/undefined symbols break; case SyntaxKind.ElifDirectiveTrivia: states.Add(currentState); positions.Add(((ElifDirectiveTriviaSyntax)directive).ElifKeyword.SpanStart); break; case SyntaxKind.ElseDirectiveTrivia: states.Add(currentState); positions.Add(((ElseDirectiveTriviaSyntax)directive).ElseKeyword.SpanStart); break; case SyntaxKind.EndIfDirectiveTrivia: states.Add(currentState); positions.Add(((EndIfDirectiveTriviaSyntax)directive).EndIfKeyword.SpanStart); break; case SyntaxKind.DefineDirectiveTrivia: states.Add(currentState); positions.Add(((DefineDirectiveTriviaSyntax)directive).Name.SpanStart); break; case SyntaxKind.UndefDirectiveTrivia: states.Add(currentState); positions.Add(((UndefDirectiveTriviaSyntax)directive).Name.SpanStart); break; default: throw ExceptionUtilities.UnexpectedValue(directive.Kind()); } } #if DEBUG int currentPos = -1; foreach (int pos in positions) { Debug.Assert(currentPos < pos); currentPos = pos; } #endif ImmutableInterlocked.InterlockedInitialize(ref _preprocessorStates, states.ToImmutableAndFree()); ImmutableInterlocked.InterlockedInitialize(ref _preprocessorStateChangePositions, positions.ToImmutableAndFree()); } #endregion #region Factories // The overload that has more parameters is itself obsolete, as an intentional break to allow future // expansion #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. /// <summary> /// Creates a new syntax tree from a syntax node. /// </summary> public static SyntaxTree Create(CSharpSyntaxNode root, CSharpParseOptions? options = null, string? path = "", Encoding? encoding = null) { #pragma warning disable CS0618 // We are calling into the obsolete member as that's the one that still does the real work return Create(root, options, path, encoding, diagnosticOptions: null); #pragma warning restore CS0618 } #pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. /// <summary> /// Creates a new syntax tree from a syntax node. /// </summary> /// <param name="diagnosticOptions">An obsolete parameter. Diagnostic options should now be passed with <see cref="CompilationOptions.SyntaxTreeOptionsProvider"/></param> /// <param name="isGeneratedCode">An obsolete parameter. It is unused.</param> [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions and isGeneratedCode parameters are obsolete due to performance problems, if you are using them use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree Create( CSharpSyntaxNode root, CSharpParseOptions? options, string? path, Encoding? encoding, // obsolete parameter -- unused ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions, // obsolete parameter -- unused bool? isGeneratedCode) { if (root == null) { throw new ArgumentNullException(nameof(root)); } return new ParsedSyntaxTree( textOpt: null, encodingOpt: encoding, checksumAlgorithm: SourceHashAlgorithm.Sha1, path: path, options: options ?? CSharpParseOptions.Default, root: root, directives: default, diagnosticOptions, cloneRoot: true); } internal static SyntaxTree Create( CSharpSyntaxNode root, CSharpParseOptions options, string? path, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) { return new ParsedSyntaxTree( textOpt: null, encodingOpt: encoding, checksumAlgorithm: checksumAlgorithm, path: path, options: options, root: root, directives: default, diagnosticOptions: null, cloneRoot: true); } /// <summary> /// Creates a new syntax tree from a syntax node with text that should correspond to the syntax node. /// </summary> /// <remarks>This is used by the ExpressionEvaluator.</remarks> internal static SyntaxTree CreateForDebugger(CSharpSyntaxNode root, SourceText text, CSharpParseOptions options) { Debug.Assert(root != null); return new DebuggerSyntaxTree(root, text, options); } /// <summary> /// <para> /// Internal helper for <see cref="CSharpSyntaxNode"/> class to create a new syntax tree rooted at the given root node. /// This method does not create a clone of the given root, but instead preserves it's reference identity. /// </para> /// <para>NOTE: This method is only intended to be used from <see cref="CSharpSyntaxNode.SyntaxTree"/> property and <c>SyntaxFactory.Parse*</c> methods.</para> /// <para>NOTE: Do not use this method elsewhere, instead use <see cref="Create(CSharpSyntaxNode, CSharpParseOptions, string, Encoding)"/> method for creating a syntax tree.</para> /// </summary> internal static SyntaxTree CreateWithoutClone(CSharpSyntaxNode root, CSharpParseOptions options) { Debug.Assert(root != null); return new ParsedSyntaxTree( textOpt: null, encodingOpt: null, checksumAlgorithm: SourceHashAlgorithm.Sha1, path: "", options: options, root: root, directives: default, diagnosticOptions: null, cloneRoot: false); } /// <summary> /// Produces a syntax tree by parsing the source text lazily. The syntax tree is realized when /// <see cref="CSharpSyntaxTree.GetRoot(CancellationToken)"/> is called. /// </summary> internal static SyntaxTree ParseTextLazy( SourceText text, CSharpParseOptions? options = null, string path = "") { return new LazySyntaxTree(text, options ?? CSharpParseOptions.Default, path, diagnosticOptions: null); } // The overload that has more parameters is itself obsolete, as an intentional break to allow future // expansion #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. /// <summary> /// Produces a syntax tree by parsing the source text. /// </summary> public static SyntaxTree ParseText( string text, CSharpParseOptions? options = null, string path = "", Encoding? encoding = null, CancellationToken cancellationToken = default) { #pragma warning disable CS0618 // We are calling into the obsolete member as that's the one that still does the real work return ParseText(text, options, path, encoding, diagnosticOptions: null, cancellationToken); #pragma warning restore CS0618 } #pragma warning restore RS0027 /// <summary> /// Produces a syntax tree by parsing the source text. /// </summary> /// <param name="diagnosticOptions">An obsolete parameter. Diagnostic options should now be passed with <see cref="CompilationOptions.SyntaxTreeOptionsProvider"/></param> /// <param name="isGeneratedCode">An obsolete parameter. It is unused.</param> [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions and isGeneratedCode parameters are obsolete due to performance problems, if you are using them use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree ParseText( string text, CSharpParseOptions? options, string path, Encoding? encoding, ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions, bool? isGeneratedCode, CancellationToken cancellationToken) { return ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), options, path, diagnosticOptions, isGeneratedCode, cancellationToken); } // The overload that has more parameters is itself obsolete, as an intentional break to allow future // expansion #pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. /// <summary> /// Produces a syntax tree by parsing the source text. /// </summary> public static SyntaxTree ParseText( SourceText text, CSharpParseOptions? options = null, string path = "", CancellationToken cancellationToken = default) { #pragma warning disable CS0618 // We are calling into the obsolete member as that's the one that still does the real work return ParseText(text, options, path, diagnosticOptions: null, cancellationToken); #pragma warning restore CS0618 } #pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. /// <summary> /// Produces a syntax tree by parsing the source text. /// </summary> /// <param name="diagnosticOptions">An obsolete parameter. Diagnostic options should now be passed with <see cref="CompilationOptions.SyntaxTreeOptionsProvider"/></param> /// <param name="isGeneratedCode">An obsolete parameter. It is unused.</param> [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions and isGeneratedCode parameters are obsolete due to performance problems, if you are using them use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree ParseText( SourceText text, CSharpParseOptions? options, string path, ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions, bool? isGeneratedCode, CancellationToken cancellationToken) { if (text == null) { throw new ArgumentNullException(nameof(text)); } options = options ?? CSharpParseOptions.Default; using var lexer = new InternalSyntax.Lexer(text, options); using var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, cancellationToken: cancellationToken); var compilationUnit = (CompilationUnitSyntax)parser.ParseCompilationUnit().CreateRed(); var tree = new ParsedSyntaxTree( text, text.Encoding, text.ChecksumAlgorithm, path, options, compilationUnit, parser.Directives, diagnosticOptions: diagnosticOptions, cloneRoot: false); Debug.Assert(compilationUnit._syntaxTree == null); compilationUnit._syntaxTree = tree; tree.VerifySource(); return tree; } #endregion #region Changes /// <summary> /// Creates a new syntax based off this tree using a new source text. /// </summary> /// <remarks> /// If the new source text is a minor change from the current source text an incremental parse will occur /// reusing most of the current syntax tree internal data. Otherwise, a full parse will occur using the new /// source text. /// </remarks> public override SyntaxTree WithChangedText(SourceText newText) { // try to find the changes between the old text and the new text. if (this.TryGetText(out SourceText? oldText)) { var changes = newText.GetChangeRanges(oldText); if (changes.Count == 0 && newText == oldText) { return this; } return this.WithChanges(newText, changes); } // if we do not easily know the old text, then specify entire text as changed so we do a full reparse. return this.WithChanges(newText, new[] { new TextChangeRange(new TextSpan(0, this.Length), newText.Length) }); } private SyntaxTree WithChanges(SourceText newText, IReadOnlyList<TextChangeRange> changes) { if (changes == null) { throw new ArgumentNullException(nameof(changes)); } IReadOnlyList<TextChangeRange>? workingChanges = changes; CSharpSyntaxTree? oldTree = this; // if changes is entire text do a full reparse if (workingChanges.Count == 1 && workingChanges[0].Span == new TextSpan(0, this.Length) && workingChanges[0].NewLength == newText.Length) { // parser will do a full parse if we give it no changes workingChanges = null; oldTree = null; } using var lexer = new InternalSyntax.Lexer(newText, this.Options); using var parser = new InternalSyntax.LanguageParser(lexer, oldTree?.GetRoot(), workingChanges); var compilationUnit = (CompilationUnitSyntax)parser.ParseCompilationUnit().CreateRed(); var tree = new ParsedSyntaxTree( newText, newText.Encoding, newText.ChecksumAlgorithm, FilePath, Options, compilationUnit, parser.Directives, #pragma warning disable CS0618 DiagnosticOptions, #pragma warning restore CS0618 cloneRoot: true); tree.VerifySource(changes); return tree; } /// <summary> /// Produces a pessimistic list of spans that denote the regions of text in this tree that /// are changed from the text of the old tree. /// </summary> /// <param name="oldTree">The old tree. Cannot be <c>null</c>.</param> /// <remarks>The list is pessimistic because it may claim more or larger regions than actually changed.</remarks> public override IList<TextSpan> GetChangedSpans(SyntaxTree oldTree) { if (oldTree == null) { throw new ArgumentNullException(nameof(oldTree)); } return SyntaxDiffer.GetPossiblyDifferentTextSpans(oldTree, this); } /// <summary> /// Gets a list of text changes that when applied to the old tree produce this tree. /// </summary> /// <param name="oldTree">The old tree. Cannot be <c>null</c>.</param> /// <remarks>The list of changes may be different than the original changes that produced this tree.</remarks> public override IList<TextChange> GetChanges(SyntaxTree oldTree) { if (oldTree == null) { throw new ArgumentNullException(nameof(oldTree)); } return SyntaxDiffer.GetTextChanges(oldTree, this); } #endregion #region LinePositions and Locations private CSharpLineDirectiveMap GetDirectiveMap() { if (_lazyLineDirectiveMap == null) { // Create the line directive map on demand. Interlocked.CompareExchange(ref _lazyLineDirectiveMap, new CSharpLineDirectiveMap(this), null); } return _lazyLineDirectiveMap; } /// <summary> /// Gets the location in terms of path, line and column for a given span. /// </summary> /// <param name="span">Span within the tree.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns> /// <see cref="FileLinePositionSpan"/> that contains path, line and column information. /// </returns> /// <remarks>The values are not affected by line mapping directives (<c>#line</c>).</remarks> public override FileLinePositionSpan GetLineSpan(TextSpan span, CancellationToken cancellationToken = default) => new(FilePath, GetLinePosition(span.Start, cancellationToken), GetLinePosition(span.End, cancellationToken)); /// <summary> /// Gets the location in terms of path, line and column after applying source line mapping directives (<c>#line</c>). /// </summary> /// <param name="span">Span within the tree.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns> /// <para>A valid <see cref="FileLinePositionSpan"/> that contains path, line and column information.</para> /// <para> /// If the location path is mapped the resulting path is the path specified in the corresponding <c>#line</c>, /// otherwise it's <see cref="SyntaxTree.FilePath"/>. /// </para> /// <para> /// A location path is considered mapped if the first <c>#line</c> directive that precedes it and that /// either specifies an explicit file path or is <c>#line default</c> exists and specifies an explicit path. /// </para> /// </returns> public override FileLinePositionSpan GetMappedLineSpan(TextSpan span, CancellationToken cancellationToken = default) => GetDirectiveMap().TranslateSpan(GetText(cancellationToken), this.FilePath, span); /// <inheritdoc/> public override LineVisibility GetLineVisibility(int position, CancellationToken cancellationToken = default) => GetDirectiveMap().GetLineVisibility(GetText(cancellationToken), position); /// <inheritdoc/> public override IEnumerable<LineMapping> GetLineMappings(CancellationToken cancellationToken = default) { var map = GetDirectiveMap(); Debug.Assert(map.Entries.Length >= 1); return (map.Entries.Length == 1) ? Array.Empty<LineMapping>() : map.GetLineMappings(GetText(cancellationToken).Lines); } /// <summary> /// Gets a <see cref="FileLinePositionSpan"/> for a <see cref="TextSpan"/>. FileLinePositionSpans are used /// primarily for diagnostics and source locations. /// </summary> /// <param name="span">The source <see cref="TextSpan" /> to convert.</param> /// <param name="isHiddenPosition">When the method returns, contains a boolean value indicating whether this span is considered hidden or not.</param> /// <returns>A resulting <see cref="FileLinePositionSpan"/>.</returns> internal override FileLinePositionSpan GetMappedLineSpanAndVisibility(TextSpan span, out bool isHiddenPosition) => GetDirectiveMap().TranslateSpanAndVisibility(GetText(), FilePath, span, out isHiddenPosition); /// <summary> /// Gets a boolean value indicating whether there are any hidden regions in the tree. /// </summary> /// <returns>True if there is at least one hidden region.</returns> public override bool HasHiddenRegions() => GetDirectiveMap().HasAnyHiddenRegions(); /// <summary> /// Given the error code and the source location, get the warning state based on <c>#pragma warning</c> directives. /// </summary> /// <param name="id">Error code.</param> /// <param name="position">Source location.</param> internal PragmaWarningState GetPragmaDirectiveWarningState(string id, int position) { if (_lazyPragmaWarningStateMap == null) { // Create the warning state map on demand. Interlocked.CompareExchange(ref _lazyPragmaWarningStateMap, new CSharpPragmaWarningStateMap(this), null); } return _lazyPragmaWarningStateMap.GetWarningState(id, position); } private NullableContextStateMap GetNullableContextStateMap() // Create the #nullable directive map on demand. => _lazyNullableContextStateMap.Initialize(static @this => NullableContextStateMap.Create(@this), this); internal NullableContextState GetNullableContextState(int position) => GetNullableContextStateMap().GetContextState(position); internal bool? IsNullableAnalysisEnabled(TextSpan span) => GetNullableContextStateMap().IsNullableAnalysisEnabled(span); internal bool IsGeneratedCode(SyntaxTreeOptionsProvider? provider, CancellationToken cancellationToken) { return provider?.IsGenerated(this, cancellationToken) switch { null or GeneratedKind.Unknown => isGeneratedHeuristic(), GeneratedKind kind => kind != GeneratedKind.NotGenerated }; bool isGeneratedHeuristic() { if (_lazyIsGeneratedCode == GeneratedKind.Unknown) { // Create the generated code status on demand bool isGenerated = GeneratedCodeUtilities.IsGeneratedCode( this, isComment: trivia => trivia.Kind() == SyntaxKind.SingleLineCommentTrivia || trivia.Kind() == SyntaxKind.MultiLineCommentTrivia, cancellationToken: default); _lazyIsGeneratedCode = isGenerated ? GeneratedKind.MarkedGenerated : GeneratedKind.NotGenerated; } return _lazyIsGeneratedCode == GeneratedKind.MarkedGenerated; } } private CSharpLineDirectiveMap? _lazyLineDirectiveMap; private CSharpPragmaWarningStateMap? _lazyPragmaWarningStateMap; private SingleInitNullable<NullableContextStateMap> _lazyNullableContextStateMap; private GeneratedKind _lazyIsGeneratedCode = GeneratedKind.Unknown; private LinePosition GetLinePosition(int position, CancellationToken cancellationToken) => GetText(cancellationToken).Lines.GetLinePosition(position); /// <summary> /// Gets a <see cref="Location"/> for the specified text <paramref name="span"/>. /// </summary> public override Location GetLocation(TextSpan span) { return new SourceLocation(this, span); } #endregion #region Diagnostics /// <summary> /// Gets a list of all the diagnostics in the sub tree that has the specified node as its root. /// </summary> /// <remarks> /// This method does not filter diagnostics based on <c>#pragma</c>s and compiler options /// like /nowarn, /warnaserror etc. /// </remarks> public override IEnumerable<Diagnostic> GetDiagnostics(SyntaxNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } return GetDiagnostics(node.Green, node.Position); } private IEnumerable<Diagnostic> GetDiagnostics(GreenNode greenNode, int position) { if (greenNode == null) { throw new InvalidOperationException(); } if (greenNode.ContainsDiagnostics) { return SyntaxTreeDiagnosticEnumerator.EnumerateDiagnostics(this, greenNode, position); } return SpecializedCollections.EmptyEnumerable<Diagnostic>(); } /// <summary> /// Gets a list of all the diagnostics associated with the token and any related trivia. /// </summary> /// <remarks> /// This method does not filter diagnostics based on <c>#pragma</c>s and compiler options /// like /nowarn, /warnaserror etc. /// </remarks> public override IEnumerable<Diagnostic> GetDiagnostics(SyntaxToken token) { if (token.Node == null) { throw new InvalidOperationException(); } return GetDiagnostics(token.Node, token.Position); } /// <summary> /// Gets a list of all the diagnostics associated with the trivia. /// </summary> /// <remarks> /// This method does not filter diagnostics based on <c>#pragma</c>s and compiler options /// like /nowarn, /warnaserror etc. /// </remarks> public override IEnumerable<Diagnostic> GetDiagnostics(SyntaxTrivia trivia) { if (trivia.UnderlyingNode == null) { throw new InvalidOperationException(); } return GetDiagnostics(trivia.UnderlyingNode, trivia.Position); } /// <summary> /// Gets a list of all the diagnostics in either the sub tree that has the specified node as its root or /// associated with the token and its related trivia. /// </summary> /// <remarks> /// This method does not filter diagnostics based on <c>#pragma</c>s and compiler options /// like /nowarn, /warnaserror etc. /// </remarks> public override IEnumerable<Diagnostic> GetDiagnostics(SyntaxNodeOrToken nodeOrToken) { if (nodeOrToken.UnderlyingNode == null) { throw new InvalidOperationException(); } return GetDiagnostics(nodeOrToken.UnderlyingNode, nodeOrToken.Position); } /// <summary> /// Gets a list of all the diagnostics in the syntax tree. /// </summary> /// <remarks> /// This method does not filter diagnostics based on <c>#pragma</c>s and compiler options /// like /nowarn, /warnaserror etc. /// </remarks> public override IEnumerable<Diagnostic> GetDiagnostics(CancellationToken cancellationToken = default) { return this.GetDiagnostics(this.GetRoot(cancellationToken)); } #endregion #region SyntaxTree protected override SyntaxNode GetRootCore(CancellationToken cancellationToken) { return this.GetRoot(cancellationToken); } protected override async Task<SyntaxNode> GetRootAsyncCore(CancellationToken cancellationToken) { return await this.GetRootAsync(cancellationToken).ConfigureAwait(false); } protected override bool TryGetRootCore([NotNullWhen(true)] out SyntaxNode? root) { if (this.TryGetRoot(out CSharpSyntaxNode? node)) { root = node; return true; } else { root = null; return false; } } protected override ParseOptions OptionsCore { get { return this.Options; } } #endregion // 3.3 BACK COMPAT OVERLOAD -- DO NOT MODIFY [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions parameter is obsolete due to performance problems, if you are passing non-null use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree ParseText( SourceText text, CSharpParseOptions? options, string path, ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions, CancellationToken cancellationToken) => ParseText(text, options, path, diagnosticOptions, isGeneratedCode: null, cancellationToken); // 3.3 BACK COMPAT OVERLOAD -- DO NOT MODIFY [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions parameter is obsolete due to performance problems, if you are passing non-null use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree ParseText( string text, CSharpParseOptions? options, string path, Encoding? encoding, ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions, CancellationToken cancellationToken) => ParseText(SourceText.From(text, encoding, SourceHashAlgorithm.Sha1), options, path, diagnosticOptions, isGeneratedCode: null, cancellationToken); // 3.3 BACK COMPAT OVERLOAD -- DO NOT MODIFY [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("The diagnosticOptions parameter is obsolete due to performance problems, if you are passing non-null use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public static SyntaxTree Create( CSharpSyntaxNode root, CSharpParseOptions? options, string? path, Encoding? encoding, ImmutableDictionary<string, ReportDiagnostic>? diagnosticOptions) => Create(root, options, path, encoding, diagnosticOptions, isGeneratedCode: null); /// <summary> /// This is ONLY used for debugging purpose /// </summary> internal string Dump() { return this.GetRoot().Dump(); } } }