// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Instrumentation;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
///
/// Represents a non-terminal node in the syntax tree.
///
//The fact that this type implements IMessageSerializable
//enables it to be used as an argument to a diagnostic. This allows diagnostics
//to defer the realization of strings. Often diagnostics generated while binding
//in service of a SemanticModel API are never realized. So this
//deferral can result in meaningful savings of strings.
public abstract partial class CSharpSyntaxNode : SyntaxNode, IMessageSerializable
{
internal CSharpSyntaxNode(GreenNode green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
///
/// Used by structured trivia which has "parent == null", and therefore must know its
/// SyntaxTree explicitly when created.
///
internal CSharpSyntaxNode(GreenNode green, int position, SyntaxTree syntaxTree)
: base(green, position, syntaxTree)
{
}
internal override AbstractSyntaxNavigator Navigator
{
get
{
return SyntaxNavigator.Instance;
}
}
//TODO: move to common
///
/// Creates a clone of a red node that can be used as a root of given syntaxTree.
/// New node has no parents, position == 0, and syntaxTree as specified.
///
internal static T CloneNodeAsRoot(T node, SyntaxTree syntaxTree) where T : SyntaxNode
{
var clone = (T)node.Green.CreateRed(null, 0);
clone._syntaxTree = syntaxTree;
return clone;
}
///
/// Returns a non-null SyntaxTree that owns this node.
/// If this node was created with an explicit non-null SyntaxTree, returns that tree.
/// Otherwise, if this node has a non-null parent, then returns the parent's SyntaxTree.
/// Otherwise, returns a newly created SyntaxTree rooted at this node, preserving this node's reference identity.
///
internal new SyntaxTree SyntaxTree
{
get
{
if (this._syntaxTree == null)
{
var tree = Parent != null ?
Parent.SyntaxTree :
CSharpSyntaxTree.CreateWithoutClone(this);
Debug.Assert(tree != null);
Interlocked.CompareExchange(ref this._syntaxTree, tree, null);
}
return this._syntaxTree;
}
}
public abstract TResult Accept(CSharpSyntaxVisitor visitor);
public abstract void Accept(CSharpSyntaxVisitor visitor);
///
/// The node that contains this node in its Children collection.
///
internal new CSharpSyntaxNode Parent
{
get
{
return (CSharpSyntaxNode)base.Parent;
}
}
internal new CSharpSyntaxNode ParentOrStructuredTriviaParent
{
get
{
return (CSharpSyntaxNode)base.ParentOrStructuredTriviaParent;
}
}
// TODO: may be eventually not needed
internal Syntax.InternalSyntax.CSharpSyntaxNode CsGreen
{
get { return (Syntax.InternalSyntax.CSharpSyntaxNode)this.Green; }
}
///
/// Gets the of the node.
///
internal SyntaxKind Kind
{
get
{
return (SyntaxKind)this.Green.RawKind;
}
}
public SyntaxKind CSharpKind()
{
return (SyntaxKind)this.Green.RawKind;
}
protected override string KindText
{
get
{
return this.Kind.ToString();
}
}
///
/// The language name that this node is syntax of.
///
public override string Language
{
get { return LanguageNames.CSharp; }
}
internal bool HasErrors
{
get
{
if (!this.ContainsDiagnostics)
{
return false;
}
return HasErrorsSlow();
}
}
private bool HasErrorsSlow()
{
return new Syntax.InternalSyntax.SyntaxDiagnosticInfoList(this.Green).Any((info) => info.Severity == DiagnosticSeverity.Error);
}
///
/// The list of trivia that appears before this node in the source code.
///
public new SyntaxTriviaList GetLeadingTrivia()
{
var firstToken = this.GetFirstToken(includeZeroWidth: true);
return firstToken.LeadingTrivia;
}
///
/// The list of trivia that appears after this node in the source code.
///
public new SyntaxTriviaList GetTrailingTrivia()
{
var lastToken = this.GetLastToken(includeZeroWidth: true);
return lastToken.TrailingTrivia;
}
internal Location Location
{
get
{
// CSharpSyntaxNode always has a non-null SyntaxTree, however the tree might be rooted at a node which is not a CompilationUnit.
// These kind of nodes may be seen during binding in couple of scenarios:
// (a) Compiler synthesized syntax nodes (e.g. missing nodes, qualified names for command line using directives, etc.)
// (b) Speculatively binding syntax nodes through the semantic model.
//
// For scenario (a), we need to ensure that we return NoLocation for generating location agnostic compiler diagnostics.
// For scenario (b), at present, we do not expose the diagnostics for speculative binding, hence we can return NoLocation.
// In future, if we decide to support this, we will need some mechanism to distinguish between scenarios (a) and (b) here.
SyntaxTree tree = SyntaxTree;
Debug.Assert(tree != null);
return (!tree.HasCompilationUnitRoot) ? NoLocation.Singleton : new SourceLocation(this);
}
}
///
/// Returns the string representation of this node, not including its leading and trailing trivia.
///
/// The string representation of this node, not including its leading and trailing trivia.
/// The length of the returned string is always the same as Span.Length
public sealed override string ToString()
{
return this.Green.ToString();
}
///
/// Returns full string representation of this node including its leading and trailing trivia.
///
/// The full string representation of this node including its leading and trailing trivia.
/// The length of the returned string is always the same as FullSpan.Length
public sealed override string ToFullString()
{
return this.Green.ToFullString();
}
///
/// Writes the full text of this node to the specified TextWriter.
///
public override void WriteTo(System.IO.TextWriter writer)
{
this.Green.WriteTo(writer, true, true);
}
#region serialization
private static readonly RecordingObjectBinder defaultBinder = new RecordingObjectBinder();
///
/// Serialize the syntax node into a byte stream.
///
public override void SerializeTo(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{
using (Logger.LogBlock(FunctionId.CSharp_SyntaxNode_SerializeTo, cancellationToken: cancellationToken))
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (!stream.CanWrite)
{
throw new InvalidOperationException(CSharpResources.TheStreamCannotBeWritten);
}
using (var writer = new ObjectWriter(stream, GetDefaultObjectWriterData(), binder: defaultBinder, cancellationToken: cancellationToken))
{
writer.WriteValue(this.Green);
}
}
}
///
/// Deserialize a syntax node from the byte stream.
///
public static SyntaxNode DeserializeFrom(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{
using (Logger.LogBlock(FunctionId.CSharp_SyntaxNode_DeserializeFrom, cancellationToken: cancellationToken))
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (!stream.CanRead)
{
throw new InvalidOperationException(CSharpResources.TheStreamCannotBeReadFrom);
}
using (var reader = new ObjectReader(stream, defaultData: GetDefaultObjectReaderData(), binder: defaultBinder))
{
var root = (Syntax.InternalSyntax.CSharpSyntaxNode)reader.ReadValue();
return root.CreateRed();
}
}
}
private static ObjectWriterData defaultObjectWriterData;
private static ObjectWriterData GetDefaultObjectWriterData()
{
if (defaultObjectWriterData == null)
{
var data = new ObjectWriterData(GetSerializationData());
Interlocked.CompareExchange(ref defaultObjectWriterData, data, null);
}
return defaultObjectWriterData;
}
private static ObjectReaderData defaultObjectReaderData;
private static ObjectReaderData GetDefaultObjectReaderData()
{
if (defaultObjectReaderData == null)
{
var data = new ObjectReaderData(GetSerializationData());
Interlocked.CompareExchange(ref defaultObjectReaderData, data, null);
}
return defaultObjectReaderData;
}
private static IEnumerable