// 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; }
}
}
}