-
Notifications
You must be signed in to change notification settings - Fork 4k
/
SyntaxTree.cs
392 lines (345 loc) · 17.5 KB
/
SyntaxTree.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// 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 enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// The parsed representation of a source document.
/// </summary>
public abstract class SyntaxTree
{
/// <summary>
/// Cached value for empty <see cref="DiagnosticOptions"/>.
/// </summary>
internal protected static readonly ImmutableDictionary<string, ReportDiagnostic> EmptyDiagnosticOptions =
ImmutableDictionary.Create<string, ReportDiagnostic>(CaseInsensitiveComparison.Comparer);
private ImmutableArray<byte> _lazyChecksum;
private SourceHashAlgorithm _lazyHashAlgorithm;
/// <summary>
/// The path of the source document file.
/// </summary>
/// <remarks>
/// 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
/// <see cref="SourceReferenceResolver"/>, <see cref="XmlReferenceResolver"/> and <see cref="MetadataReferenceResolver"/>
/// 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.
/// </remarks>
public abstract string FilePath { get; }
/// <summary>
/// Returns true if this syntax tree has a root with SyntaxKind "CompilationUnit".
/// </summary>
public abstract bool HasCompilationUnitRoot { get; }
/// <summary>
/// The options used by the parser to produce the syntax tree.
/// </summary>
public ParseOptions Options
{
get
{
return this.OptionsCore;
}
}
/// <summary>
/// The options used by the parser to produce the syntax tree.
/// </summary>
protected abstract ParseOptions OptionsCore { get; }
/// <summary>
/// Option to specify custom behavior for each warning in this tree.
/// </summary>
/// <returns>
/// A map from diagnostic ID to diagnostic reporting level. The diagnostic
/// ID string may be case insensitive depending on the language.
/// </returns>
public virtual ImmutableDictionary<string, ReportDiagnostic> DiagnosticOptions
=> EmptyDiagnosticOptions;
/// <summary>
/// The length of the text of the syntax tree.
/// </summary>
public abstract int Length { get; }
/// <summary>
/// Gets the syntax tree's text if it is available.
/// </summary>
public abstract bool TryGetText([NotNullWhen(true)] out SourceText? text);
/// <summary>
/// Gets the text of the source document.
/// </summary>
public abstract SourceText GetText(CancellationToken cancellationToken = default);
/// <summary>
/// The text encoding of the source document.
/// </summary>
public abstract Encoding? Encoding { get; }
/// <summary>
/// Gets the text of the source document 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="GetTextAsync(CancellationToken)"/>.
/// </remarks>
public virtual Task<SourceText> GetTextAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(this.TryGetText(out SourceText? text) ? text : this.GetText(cancellationToken));
}
/// <summary>
/// Gets the root of the syntax tree if it is available.
/// </summary>
public bool TryGetRoot([NotNullWhen(true)] out SyntaxNode? root)
{
return TryGetRootCore(out root);
}
/// <summary>
/// Gets the root of the syntax tree if it is available.
/// </summary>
protected abstract bool TryGetRootCore([NotNullWhen(true)] out SyntaxNode? root);
/// <summary>
/// Gets the root node of the syntax tree, causing computation if necessary.
/// </summary>
public SyntaxNode GetRoot(CancellationToken cancellationToken = default)
{
return GetRootCore(cancellationToken);
}
/// <summary>
/// Gets the root node of the syntax tree, causing computation if necessary.
/// </summary>
protected abstract SyntaxNode GetRootCore(CancellationToken cancellationToken);
/// <summary>
/// Gets the root node of the syntax tree asynchronously.
/// </summary>
public Task<SyntaxNode> GetRootAsync(CancellationToken cancellationToken = default)
{
return GetRootAsyncCore(cancellationToken);
}
/// <summary>
/// Gets the root node of the syntax tree asynchronously.
/// </summary>
[SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Public API.")]
protected abstract Task<SyntaxNode> GetRootAsyncCore(CancellationToken cancellationToken);
/// <summary>
/// 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.
/// </summary>
public abstract SyntaxTree WithChangedText(SourceText newText);
/// <summary>
/// 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.
/// </summary>
public abstract IEnumerable<Diagnostic> GetDiagnostics(CancellationToken cancellationToken = default);
/// <summary>
/// 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.
/// </summary>
public abstract IEnumerable<Diagnostic> GetDiagnostics(SyntaxNode node);
/// <summary>
/// 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.
/// </summary>
public abstract IEnumerable<Diagnostic> GetDiagnostics(SyntaxToken token);
/// <summary>
/// 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.
/// </summary>
public abstract IEnumerable<Diagnostic> GetDiagnostics(SyntaxTrivia trivia);
/// <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.
/// This method does not filter diagnostics based on #pragmas and compiler options
/// like nowarn, warnaserror etc.
/// </summary>
public abstract IEnumerable<Diagnostic> GetDiagnostics(SyntaxNodeOrToken nodeOrToken);
/// <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>
/// A valid <see cref="FileLinePositionSpan"/> that contains path, line and column information.
/// The values are not affected by line mapping directives (<c>#line</c>).
/// </returns>
public abstract FileLinePositionSpan GetLineSpan(TextSpan span, CancellationToken cancellationToken = default);
/// <summary>
/// Gets the location in terms of path, line and column after applying source line mapping directives
/// (<c>#line</c> in C# or <c>#ExternalSource</c> in VB).
/// </summary>
/// <param name="span">Span within the tree.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// A valid <see cref="FileLinePositionSpan"/> that contains path, line and column information.
///
/// 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"/>.
///
/// 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.
/// </returns>
public abstract FileLinePositionSpan GetMappedLineSpan(TextSpan span, CancellationToken cancellationToken = default);
/// <summary>
/// Returns the visibility for the line at the given position.
/// </summary>
/// <param name="position">The position to check.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public virtual LineVisibility GetLineVisibility(int position, CancellationToken cancellationToken = default)
{
return LineVisibility.Visible;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="span"></param>
/// <param name="isHiddenPosition">Returns a boolean indicating whether this span is considered hidden or not.</param>
/// <remarks>This function is being called only in the context of sequence point creation and therefore interprets the
/// LineVisibility accordingly (BeforeFirstRemappingDirective -> Visible).</remarks>
internal virtual FileLinePositionSpan GetMappedLineSpanAndVisibility(TextSpan span, out bool isHiddenPosition)
{
isHiddenPosition = GetLineVisibility(span.Start) == LineVisibility.Hidden;
return GetMappedLineSpan(span);
}
/// <summary>
/// Returns a path for particular location in source that is presented to the user.
/// </summary>
/// <remarks>
/// Used for implementation of <see cref="System.Runtime.CompilerServices.CallerFilePathAttribute"/>
/// or for embedding source paths in error messages.
///
/// Unlike Dev12 we do account for #line and #ExternalSource directives when determining value for
/// <see cref="System.Runtime.CompilerServices.CallerFilePathAttribute"/>.
/// </remarks>
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;
}
/// <summary>
/// Returns a line number for particular location in source that is presented to the user.
/// </summary>
/// <remarks>
/// Used for implementation of <see cref="System.Runtime.CompilerServices.CallerLineNumberAttribute"/>
/// or for embedding source line numbers in error messages.
///
/// Unlike Dev12 we do account for #line and #ExternalSource directives when determining value for
/// <see cref="System.Runtime.CompilerServices.CallerLineNumberAttribute"/>.
/// </remarks>
internal int GetDisplayLineNumber(TextSpan span)
{
// display line numbers are 1-based
return GetMappedLineSpan(span).StartLinePosition.Line + 1;
}
/// <summary>
/// Are there any hidden regions in the tree?
/// </summary>
/// <returns>True if there is at least one hidden region.</returns>
public abstract bool HasHiddenRegions();
/// <summary>
/// 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.
/// </summary>
public abstract IList<TextSpan> GetChangedSpans(SyntaxTree syntaxTree);
/// <summary>
/// Gets a location for the specified text span.
/// </summary>
public abstract Location GetLocation(TextSpan span);
/// <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 abstract bool IsEquivalentTo(SyntaxTree tree, bool topLevel = false);
/// <summary>
/// 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.
/// </summary>
public abstract SyntaxReference GetReference(SyntaxNode node);
/// <summary>
/// Gets a list of text changes that when applied to the old tree produce this tree.
/// </summary>
/// <param name="oldTree">The old tree.</param>
/// <remarks>The list of changes may be different than the original changes that produced
/// this tree.</remarks>
public abstract IList<TextChange> GetChanges(SyntaxTree oldTree);
/// <summary>
/// Gets the checksum + algorithm id to use in the PDB.
/// </summary>
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);
}
/// <summary>
/// Returns a new tree whose root and options are as specified and other properties are copied from the current tree.
/// </summary>
public abstract SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options);
/// <summary>
/// Returns a new tree whose <see cref="FilePath"/> is the specified node and other properties are copied from the current tree.
/// </summary>
public abstract SyntaxTree WithFilePath(string path);
/// <summary>
/// Returns a new tree whose <see cref="DiagnosticOptions" /> are the specified value and other properties are copied
/// from the current tree.
/// </summary>
/// <param name="options">
/// A mapping from diagnostic id to diagnostic reporting level. The diagnostic ID may be case-sensitive depending
/// on the language.
/// </param>
public virtual SyntaxTree WithDiagnosticOptions(ImmutableDictionary<string, ReportDiagnostic> options)
{
throw new NotImplementedException();
}
/// <summary>
/// Returns a <see cref="String" /> that represents the entire source text of this <see cref="SyntaxTree"/>.
/// </summary>
public override string ToString()
{
return this.GetText(CancellationToken.None).ToString();
}
internal virtual bool SupportsLocations
{
get { return this.HasCompilationUnitRoot; }
}
}
}