// 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.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// /// The parsed representation of a source document. /// public abstract class SyntaxTree { /// /// Cached value for empty . /// internal protected static readonly ImmutableDictionary EmptyDiagnosticOptions = ImmutableDictionary.Create(CaseInsensitiveComparison.Comparer); private ImmutableArray _lazyChecksum; private SourceHashAlgorithm _lazyHashAlgorithm; /// /// The path of the source document file. /// /// /// If this syntax tree is not associated with a file, this value can be empty. /// The path shall not be null. /// /// The file doesn't need to exist on disk. The path is opaque to the compiler. /// The only requirement on the path format is that the implementations of /// , and /// passed to the compilation that contains the tree understand it. /// /// Clients must also not assume that the values of this property are unique /// within a Compilation. /// /// The path is used as follows: /// - When debug information is emitted, this path is embedded in the debug information. /// - When resolving and normalizing relative paths in #r, #load, #line/#ExternalSource, /// #pragma checksum, #ExternalChecksum directives, XML doc comment include elements, etc. /// public abstract string FilePath { get; } /// /// Returns true if this syntax tree has a root with SyntaxKind "CompilationUnit". /// public abstract bool HasCompilationUnitRoot { get; } /// /// The options used by the parser to produce the syntax tree. /// public ParseOptions Options { get { return this.OptionsCore; } } /// /// The options used by the parser to produce the syntax tree. /// protected abstract ParseOptions OptionsCore { get; } /// /// Option to specify custom behavior for each warning in this tree. /// /// /// A map from diagnostic ID to diagnostic reporting level. The diagnostic /// ID string may be case insensitive depending on the language. /// public virtual ImmutableDictionary DiagnosticOptions => EmptyDiagnosticOptions; /// /// The length of the text of the syntax tree. /// public abstract int Length { get; } /// /// Gets the syntax tree's text if it is available. /// public abstract bool TryGetText(out SourceText text); /// /// Gets the text of the source document. /// public abstract SourceText GetText(CancellationToken cancellationToken = default(CancellationToken)); /// /// The text encoding of the source document. /// public abstract Encoding Encoding { get; } /// /// Gets the text of the source document asynchronously. /// /// /// 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 . /// public virtual Task GetTextAsync(CancellationToken cancellationToken = default(CancellationToken)) { SourceText text; return Task.FromResult(this.TryGetText(out text) ? text : this.GetText(cancellationToken)); } /// /// Gets the root of the syntax tree if it is available. /// public bool TryGetRoot(out SyntaxNode root) { return TryGetRootCore(out root); } /// /// Gets the root of the syntax tree if it is available. /// protected abstract bool TryGetRootCore(out SyntaxNode root); /// /// Gets the root node of the syntax tree, causing computation if necessary. /// public SyntaxNode GetRoot(CancellationToken cancellationToken = default(CancellationToken)) { return GetRootCore(cancellationToken); } /// /// Gets the root node of the syntax tree, causing computation if necessary. /// protected abstract SyntaxNode GetRootCore(CancellationToken cancellationToken); /// /// Gets the root node of the syntax tree asynchronously. /// public Task GetRootAsync(CancellationToken cancellationToken = default(CancellationToken)) { return GetRootAsyncCore(cancellationToken); } /// /// Gets the root node of the syntax tree asynchronously. /// [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Public API.")] protected abstract Task GetRootAsyncCore(CancellationToken cancellationToken); /// /// Create a new syntax tree based off this tree using a new source text. /// /// 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. /// public abstract SyntaxTree WithChangedText(SourceText newText); /// /// Gets a list of all the diagnostics in the syntax tree. /// This method does not filter diagnostics based on #pragmas and compiler options /// like nowarn, warnaserror etc. /// public abstract IEnumerable GetDiagnostics(CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets a list of all the diagnostics in the sub tree that has the specified node as its root. /// This method does not filter diagnostics based on #pragmas and compiler options /// like nowarn, warnaserror etc. /// public abstract IEnumerable GetDiagnostics(SyntaxNode node); /// /// Gets a list of all the diagnostics associated with the token and any related trivia. /// This method does not filter diagnostics based on #pragmas and compiler options /// like nowarn, warnaserror etc. /// public abstract IEnumerable GetDiagnostics(SyntaxToken token); /// /// Gets a list of all the diagnostics associated with the trivia. /// This method does not filter diagnostics based on #pragmas and compiler options /// like nowarn, warnaserror etc. /// public abstract IEnumerable GetDiagnostics(SyntaxTrivia trivia); /// /// 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. /// This method does not filter diagnostics based on #pragmas and compiler options /// like nowarn, warnaserror etc. /// public abstract IEnumerable GetDiagnostics(SyntaxNodeOrToken nodeOrToken); /// /// Gets the location in terms of path, line and column for a given span. /// /// Span within the tree. /// Cancellation token. /// /// A valid that contains path, line and column information. /// The values are not affected by line mapping directives (#line). /// public abstract FileLinePositionSpan GetLineSpan(TextSpan span, CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets the location in terms of path, line and column after applying source line mapping directives /// (#line in C# or #ExternalSource in VB). /// /// Span within the tree. /// Cancellation token. /// /// A valid that contains path, line and column information. /// /// If the location path is mapped the resulting path is the path specified in the corresponding #line, /// otherwise it's . /// /// A location path is considered mapped if the first #line directive that precedes it and that /// either specifies an explicit file path or is #line default exists and specifies an explicit path. /// public abstract FileLinePositionSpan GetMappedLineSpan(TextSpan span, CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns the visibility for the line at the given position. /// /// The position to check. /// The cancellation token. public virtual LineVisibility GetLineVisibility(int position, CancellationToken cancellationToken = default(CancellationToken)) { return LineVisibility.Visible; } /// /// Gets a FileLinePositionSpan for a TextSpan and the information whether this span is considered to be hidden or not. /// FileLinePositionSpans are used primarily for diagnostics and source locations. /// This method combines a call to GetLineSpan and IsHiddenPosition. /// /// /// Returns a boolean indicating whether this span is considered hidden or not. /// This function is being called only in the context of sequence point creation and therefore interprets the /// LineVisibility accordingly (BeforeFirstRemappingDirective -> Visible). internal virtual FileLinePositionSpan GetMappedLineSpanAndVisibility(TextSpan span, out bool isHiddenPosition) { isHiddenPosition = GetLineVisibility(span.Start) == LineVisibility.Hidden; return GetMappedLineSpan(span); } /// /// Returns a path for particular location in source that is presented to the user. /// /// /// Used for implementation of /// or for embedding source paths in error messages. /// /// Unlike Dev12 we do account for #line and #ExternalSource directives when determining value for /// . /// internal string GetDisplayPath(TextSpan span, SourceReferenceResolver resolver) { var mappedSpan = GetMappedLineSpan(span); if (resolver == null || mappedSpan.Path.IsEmpty()) { return mappedSpan.Path; } return resolver.NormalizePath(mappedSpan.Path, baseFilePath: mappedSpan.HasMappedPath ? FilePath : null) ?? mappedSpan.Path; } /// /// Returns a line number for particular location in source that is presented to the user. /// /// /// Used for implementation of /// or for embedding source line numbers in error messages. /// /// Unlike Dev12 we do account for #line and #ExternalSource directives when determining value for /// . /// internal int GetDisplayLineNumber(TextSpan span) { // display line numbers are 1-based return GetMappedLineSpan(span).StartLinePosition.Line + 1; } /// /// Are there any hidden regions in the tree? /// /// True if there is at least one hidden region. public abstract bool HasHiddenRegions(); /// /// Returns a list of the changed regions between this tree and the specified tree. The list is conservative for /// performance reasons. It may return larger regions than what has actually changed. /// public abstract IList GetChangedSpans(SyntaxTree syntaxTree); /// /// Gets a location for the specified text span. /// public abstract Location GetLocation(TextSpan span); /// /// Determines if two trees are the same, disregarding trivia differences. /// /// The tree to compare against. /// 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. /// public abstract bool IsEquivalentTo(SyntaxTree tree, bool topLevel = false); /// /// Gets a SyntaxReference for a specified syntax node. SyntaxReferences can be used to /// regain access to a syntax node without keeping the entire tree and source text in /// memory. /// public abstract SyntaxReference GetReference(SyntaxNode node); /// /// Gets a list of text changes that when applied to the old tree produce this tree. /// /// The old tree. /// The list of changes may be different than the original changes that produced /// this tree. public abstract IList GetChanges(SyntaxTree oldTree); /// /// Gets the checksum + algorithm id to use in the PDB. /// internal Cci.DebugSourceInfo GetDebugSourceInfo() { if (_lazyChecksum.IsDefault) { var text = this.GetText(); _lazyChecksum = text.GetChecksum(); _lazyHashAlgorithm = text.ChecksumAlgorithm; } Debug.Assert(!_lazyChecksum.IsDefault); Debug.Assert(_lazyHashAlgorithm != default(SourceHashAlgorithm)); // NOTE: If this tree is to be embedded, it's debug source info should have // been obtained via EmbeddedText.GetDebugSourceInfo() and not here. return new Cci.DebugSourceInfo(_lazyChecksum, _lazyHashAlgorithm); } /// /// Returns a new tree whose root and options are as specified and other properties are copied from the current tree. /// public abstract SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options); /// /// Returns a new tree whose is the specified node and other properties are copied from the current tree. /// public abstract SyntaxTree WithFilePath(string path); /// /// Returns a new tree whose are the specified value and other properties are copied /// from the current tree. /// /// /// A mapping from diagnostic id to diagnostic reporting level. The diagnostic ID may be case-sensitive depending /// on the language. /// public virtual SyntaxTree WithDiagnosticOptions(ImmutableDictionary options) { throw new NotImplementedException(); } /// /// Returns a that represents the entire source text of this . /// public override string ToString() { return this.GetText(CancellationToken.None).ToString(); } internal virtual bool SupportsLocations { get { return this.HasCompilationUnitRoot; } } } }