-
Notifications
You must be signed in to change notification settings - Fork 4k
/
SyntaxExtensions.cs
491 lines (457 loc) · 21.6 KB
/
SyntaxExtensions.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
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
public static class SyntaxExtensions
{
/// <summary>
/// Gets the expression-body syntax from an expression-bodied member. The
/// given syntax must be for a member which could contain an expression-body.
/// </summary>
internal static ArrowExpressionClauseSyntax? GetExpressionBodySyntax(this CSharpSyntaxNode node)
{
ArrowExpressionClauseSyntax? arrowExpr = null;
switch (node.Kind())
{
// The ArrowExpressionClause is the declaring syntax for the
// 'get' SourcePropertyAccessorSymbol of properties and indexers.
case SyntaxKind.ArrowExpressionClause:
arrowExpr = (ArrowExpressionClauseSyntax)node;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
arrowExpr = ((BaseMethodDeclarationSyntax)node).ExpressionBody;
break;
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
case SyntaxKind.InitAccessorDeclaration:
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
case SyntaxKind.UnknownAccessorDeclaration:
arrowExpr = ((AccessorDeclarationSyntax)node).ExpressionBody;
break;
case SyntaxKind.PropertyDeclaration:
arrowExpr = ((PropertyDeclarationSyntax)node).ExpressionBody;
break;
case SyntaxKind.IndexerDeclaration:
arrowExpr = ((IndexerDeclarationSyntax)node).ExpressionBody;
break;
default:
// Don't throw, just use for the assert in case this is used in the semantic model
ExceptionUtilities.UnexpectedValue(node.Kind());
break;
}
return arrowExpr;
}
/// <summary>
/// Creates a new syntax token with all whitespace and end of line trivia replaced with
/// regularly formatted trivia.
/// </summary>
/// <param name="token">The token to normalize.</param>
/// <param name="indentation">A sequence of whitespace characters that defines a single level of indentation.</param>
/// <param name="elasticTrivia">If true the replaced trivia is elastic trivia.</param>
public static SyntaxToken NormalizeWhitespace(this SyntaxToken token, string indentation, bool elasticTrivia)
{
return SyntaxNormalizer.Normalize(token, indentation, CodeAnalysis.SyntaxNodeExtensions.DefaultEOL, elasticTrivia);
}
/// <summary>
/// Return the identifier of an out declaration argument expression.
/// </summary>
internal static SyntaxToken Identifier(this DeclarationExpressionSyntax self)
{
return ((SingleVariableDesignationSyntax)self.Designation).Identifier;
}
/// <summary>
/// Creates a new syntax token with all whitespace and end of line trivia replaced with
/// regularly formatted trivia.
/// </summary>
/// <param name="token">The token to normalize.</param>
/// <param name="indentation">An optional sequence of whitespace characters that defines a
/// single level of indentation.</param>
/// <param name="eol">An optional sequence of whitespace characters used for end of line.</param>
/// <param name="elasticTrivia">If true the replaced trivia is elastic trivia.</param>
public static SyntaxToken NormalizeWhitespace(this SyntaxToken token,
string indentation = CodeAnalysis.SyntaxNodeExtensions.DefaultIndentation,
string eol = CodeAnalysis.SyntaxNodeExtensions.DefaultEOL,
bool elasticTrivia = false)
{
return SyntaxNormalizer.Normalize(token, indentation, eol, elasticTrivia);
}
/// <summary>
/// Creates a new syntax trivia list with all whitespace and end of line trivia replaced with
/// regularly formatted trivia.
/// </summary>
/// <param name="list">The trivia list to normalize.</param>
/// <param name="indentation">A sequence of whitespace characters that defines a single level of indentation.</param>
/// <param name="elasticTrivia">If true the replaced trivia is elastic trivia.</param>
public static SyntaxTriviaList NormalizeWhitespace(this SyntaxTriviaList list, string indentation, bool elasticTrivia)
{
return SyntaxNormalizer.Normalize(list, indentation, CodeAnalysis.SyntaxNodeExtensions.DefaultEOL, elasticTrivia);
}
/// <summary>
/// Creates a new syntax trivia list with all whitespace and end of line trivia replaced with
/// regularly formatted trivia.
/// </summary>
/// <param name="list">The trivia list to normalize.</param>
/// <param name="indentation">An optional sequence of whitespace characters that defines a
/// single level of indentation.</param>
/// <param name="eol">An optional sequence of whitespace characters used for end of line.</param>
/// <param name="elasticTrivia">If true the replaced trivia is elastic trivia.</param>
public static SyntaxTriviaList NormalizeWhitespace(this SyntaxTriviaList list,
string indentation = CodeAnalysis.SyntaxNodeExtensions.DefaultIndentation,
string eol = CodeAnalysis.SyntaxNodeExtensions.DefaultEOL,
bool elasticTrivia = false)
{
return SyntaxNormalizer.Normalize(list, indentation, eol, elasticTrivia);
}
public static SyntaxTriviaList ToSyntaxTriviaList(this IEnumerable<SyntaxTrivia> sequence)
{
return SyntaxFactory.TriviaList(sequence);
}
internal static XmlNameAttributeElementKind GetElementKind(this XmlNameAttributeSyntax attributeSyntax)
{
Debug.Assert(attributeSyntax.Parent is object);
CSharpSyntaxNode parentSyntax = attributeSyntax.Parent;
SyntaxKind parentKind = parentSyntax.Kind();
string parentName;
if (parentKind == SyntaxKind.XmlEmptyElement)
{
var parent = (XmlEmptyElementSyntax)parentSyntax;
parentName = parent.Name.LocalName.ValueText;
Debug.Assert(parent.Name.Prefix is null);
}
else if (parentKind == SyntaxKind.XmlElementStartTag)
{
var parent = (XmlElementStartTagSyntax)parentSyntax;
parentName = parent.Name.LocalName.ValueText;
Debug.Assert(parent.Name.Prefix is null);
}
else
{
throw ExceptionUtilities.UnexpectedValue(parentKind);
}
if (DocumentationCommentXmlNames.ElementEquals(parentName, DocumentationCommentXmlNames.ParameterElementName))
{
return XmlNameAttributeElementKind.Parameter;
}
else if (DocumentationCommentXmlNames.ElementEquals(parentName, DocumentationCommentXmlNames.ParameterReferenceElementName))
{
return XmlNameAttributeElementKind.ParameterReference;
}
else if (DocumentationCommentXmlNames.ElementEquals(parentName, DocumentationCommentXmlNames.TypeParameterElementName))
{
return XmlNameAttributeElementKind.TypeParameter;
}
else if (DocumentationCommentXmlNames.ElementEquals(parentName, DocumentationCommentXmlNames.TypeParameterReferenceElementName))
{
return XmlNameAttributeElementKind.TypeParameterReference;
}
else
{
throw ExceptionUtilities.UnexpectedValue(parentName);
}
}
internal static bool ReportDocumentationCommentDiagnostics(this SyntaxTree tree)
{
return tree.Options.DocumentationMode >= DocumentationMode.Diagnose;
}
/// <summary>
/// Updates the given SimpleNameSyntax node with the given identifier token.
/// This function is a wrapper that calls WithIdentifier on derived syntax nodes.
/// </summary>
/// <param name="simpleName"></param>
/// <param name="identifier"></param>
/// <returns>The given simple name updated with the given identifier.</returns>
public static SimpleNameSyntax WithIdentifier(this SimpleNameSyntax simpleName, SyntaxToken identifier)
{
return simpleName.Kind() == SyntaxKind.IdentifierName
? (SimpleNameSyntax)((IdentifierNameSyntax)simpleName).WithIdentifier(identifier)
: (SimpleNameSyntax)((GenericNameSyntax)simpleName).WithIdentifier(identifier);
}
internal static bool IsTypeInContextWhichNeedsDynamicAttribute(this IdentifierNameSyntax typeNode)
{
Debug.Assert(typeNode != null);
return SyntaxFacts.IsInTypeOnlyContext(typeNode) && IsInContextWhichNeedsDynamicAttribute(typeNode);
}
internal static ExpressionSyntax SkipParens(this ExpressionSyntax expression)
{
while (expression.Kind() == SyntaxKind.ParenthesizedExpression)
{
expression = ((ParenthesizedExpressionSyntax)expression).Expression;
}
return expression;
}
/// <summary>
/// Returns true if the expression on the left-hand-side of an assignment causes the assignment to be a deconstruction.
/// </summary>
internal static bool IsDeconstructionLeft(this ExpressionSyntax node)
{
switch (node.Kind())
{
case SyntaxKind.TupleExpression:
return true;
case SyntaxKind.DeclarationExpression:
return ((DeclarationExpressionSyntax)node).Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation;
default:
return false;
}
}
internal static bool IsDeconstruction(this AssignmentExpressionSyntax self)
{
return self.Left.IsDeconstructionLeft();
}
private static bool IsInContextWhichNeedsDynamicAttribute(CSharpSyntaxNode node)
{
Debug.Assert(node != null);
switch (node.Kind())
{
case SyntaxKind.Parameter:
case SyntaxKind.FieldDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.IndexerDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.EventDeclaration:
case SyntaxKind.EventFieldDeclaration:
case SyntaxKind.BaseList:
case SyntaxKind.SimpleBaseType:
case SyntaxKind.PrimaryConstructorBaseType:
return true;
case SyntaxKind.Block:
case SyntaxKind.VariableDeclarator:
case SyntaxKind.TypeParameterConstraintClause:
case SyntaxKind.Attribute:
case SyntaxKind.EqualsValueClause:
return false;
default:
return node.Parent != null && IsInContextWhichNeedsDynamicAttribute(node.Parent);
}
}
public static IndexerDeclarationSyntax Update(
this IndexerDeclarationSyntax syntax,
SyntaxList<AttributeListSyntax> attributeLists,
SyntaxTokenList modifiers,
TypeSyntax type,
ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier,
SyntaxToken thisKeyword,
BracketedParameterListSyntax parameterList,
AccessorListSyntax accessorList)
{
return syntax.Update(
attributeLists,
modifiers,
type,
explicitInterfaceSpecifier,
thisKeyword,
parameterList,
accessorList,
expressionBody: null,
semicolonToken: default);
}
public static OperatorDeclarationSyntax Update(
this OperatorDeclarationSyntax syntax,
SyntaxList<AttributeListSyntax> attributeLists,
SyntaxTokenList modifiers,
TypeSyntax returnType,
SyntaxToken operatorKeyword,
SyntaxToken operatorToken,
ParameterListSyntax parameterList,
BlockSyntax block,
SyntaxToken semicolonToken)
{
return syntax.Update(
attributeLists,
modifiers,
returnType,
operatorKeyword,
operatorToken,
parameterList,
block,
expressionBody: null,
semicolonToken);
}
public static MethodDeclarationSyntax Update(
this MethodDeclarationSyntax syntax,
SyntaxList<AttributeListSyntax> attributeLists,
SyntaxTokenList modifiers,
TypeSyntax returnType,
ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier,
SyntaxToken identifier,
TypeParameterListSyntax typeParameterList,
ParameterListSyntax parameterList,
SyntaxList<TypeParameterConstraintClauseSyntax> constraintClauses,
BlockSyntax block,
SyntaxToken semicolonToken)
{
return syntax.Update(
attributeLists,
modifiers,
returnType,
explicitInterfaceSpecifier,
identifier,
typeParameterList,
parameterList,
constraintClauses,
block,
expressionBody: null,
semicolonToken);
}
/// <summary>
/// If this declaration or identifier is part of a deconstruction, find the deconstruction.
/// If found, returns either an assignment expression or a foreach variable statement.
/// Returns null otherwise.
/// </summary>
internal static CSharpSyntaxNode? GetContainingDeconstruction(this ExpressionSyntax expr)
{
var kind = expr.Kind();
if (kind != SyntaxKind.TupleExpression && kind != SyntaxKind.DeclarationExpression && kind != SyntaxKind.IdentifierName)
{
return null;
}
while (true)
{
Debug.Assert(expr.Kind() == SyntaxKind.TupleExpression || expr.Kind() == SyntaxKind.DeclarationExpression || expr.Kind() == SyntaxKind.IdentifierName);
var parent = expr.Parent;
if (parent == null) { return null; }
switch (parent.Kind())
{
case SyntaxKind.Argument:
if (parent.Parent?.Kind() == SyntaxKind.TupleExpression)
{
expr = (TupleExpressionSyntax)parent.Parent;
continue;
}
return null;
case SyntaxKind.SimpleAssignmentExpression:
if ((object)((AssignmentExpressionSyntax)parent).Left == expr)
{
return parent;
}
return null;
case SyntaxKind.ForEachVariableStatement:
if ((object)((ForEachVariableStatementSyntax)parent).Variable == expr)
{
return parent;
}
return null;
default:
return null;
}
}
}
internal static bool IsOutDeclaration(this DeclarationExpressionSyntax p)
{
return p.Parent?.Kind() == SyntaxKind.Argument
&& ((ArgumentSyntax)p.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword;
}
internal static bool IsOutVarDeclaration(this DeclarationExpressionSyntax p)
{
return p.Designation.Kind() == SyntaxKind.SingleVariableDesignation && p.IsOutDeclaration();
}
#nullable enable
/// <summary>
/// Visits all the ArrayRankSpecifiers of a typeSyntax, invoking an action on each one in turn.
/// </summary>
/// <param name="type"></param>
/// <param name="action"></param>
/// <param name="argument">The argument that is passed to the action whenever it is invoked</param>
internal static void VisitRankSpecifiers<TArg>(this TypeSyntax type, Action<ArrayRankSpecifierSyntax, TArg> action, in TArg argument)
{
// Use a manual stack here to avoid deeply nested recursion which can blow the real stack
var stack = ArrayBuilder<SyntaxNode>.GetInstance();
stack.Push(type);
while (stack.Count > 0)
{
var current = stack.Pop();
if (current is ArrayRankSpecifierSyntax rankSpecifier)
{
action(rankSpecifier, argument);
continue;
}
else
{
type = (TypeSyntax)current;
}
switch (type.Kind())
{
case SyntaxKind.ArrayType:
var arrayTypeSyntax = (ArrayTypeSyntax)type;
for (int i = arrayTypeSyntax.RankSpecifiers.Count - 1; i >= 0; i--)
{
stack.Push(arrayTypeSyntax.RankSpecifiers[i]);
}
stack.Push(arrayTypeSyntax.ElementType);
break;
case SyntaxKind.NullableType:
var nullableTypeSyntax = (NullableTypeSyntax)type;
stack.Push(nullableTypeSyntax.ElementType);
break;
case SyntaxKind.PointerType:
var pointerTypeSyntax = (PointerTypeSyntax)type;
stack.Push(pointerTypeSyntax.ElementType);
break;
case SyntaxKind.FunctionPointerType:
var functionPointerTypeSyntax = (FunctionPointerTypeSyntax)type;
for (int i = functionPointerTypeSyntax.ParameterList.Parameters.Count - 1; i >= 0; i--)
{
TypeSyntax? paramType = functionPointerTypeSyntax.ParameterList.Parameters[i].Type;
Debug.Assert(paramType is object);
stack.Push(paramType);
}
break;
case SyntaxKind.TupleType:
var tupleTypeSyntax = (TupleTypeSyntax)type;
for (int i = tupleTypeSyntax.Elements.Count - 1; i >= 0; i--)
{
stack.Push(tupleTypeSyntax.Elements[i].Type);
}
break;
case SyntaxKind.RefType:
var refTypeSyntax = (RefTypeSyntax)type;
stack.Push(refTypeSyntax.Type);
break;
case SyntaxKind.ScopedType:
var scopedTypeSyntax = (ScopedTypeSyntax)type;
stack.Push(scopedTypeSyntax.Type);
break;
case SyntaxKind.GenericName:
var genericNameSyntax = (GenericNameSyntax)type;
for (int i = genericNameSyntax.TypeArgumentList.Arguments.Count - 1; i >= 0; i--)
{
stack.Push(genericNameSyntax.TypeArgumentList.Arguments[i]);
}
break;
case SyntaxKind.QualifiedName:
var qualifiedNameSyntax = (QualifiedNameSyntax)type;
stack.Push(qualifiedNameSyntax.Right);
stack.Push(qualifiedNameSyntax.Left);
break;
case SyntaxKind.AliasQualifiedName:
var aliasQualifiedNameSyntax = (AliasQualifiedNameSyntax)type;
stack.Push(aliasQualifiedNameSyntax.Name);
break;
case SyntaxKind.IdentifierName:
case SyntaxKind.OmittedTypeArgument:
case SyntaxKind.PredefinedType:
break;
default:
throw ExceptionUtilities.UnexpectedValue(type.Kind());
}
}
stack.Free();
}
}
}