/
OperationExtensions.cs
370 lines (316 loc) · 15.4 KB
/
OperationExtensions.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
// Copyright (c) Microsoft. 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.Operations
{
public static partial class OperationExtensions
{
/// <summary>
/// This will check whether context around the operation has any error such as syntax or semantic error
/// </summary>
internal static bool HasErrors(this IOperation operation, Compilation compilation, CancellationToken cancellationToken = default(CancellationToken))
{
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
if (compilation == null)
{
throw new ArgumentNullException(nameof(compilation));
}
// once we made sure every operation has Syntax, we will remove this condition
if (operation.Syntax == null)
{
return true;
}
// if wrong compilation is given, GetSemanticModel will throw due to tree not belong to the given compilation.
var model = operation.SemanticModel ?? compilation.GetSemanticModel(operation.Syntax.SyntaxTree);
if (model.IsSpeculativeSemanticModel)
{
// GetDiagnostics not supported for speculative semantic model.
// https://github.com/dotnet/roslyn/issues/28075
return false;
}
return model.GetDiagnostics(operation.Syntax.Span, cancellationToken).Any(d => d.DefaultSeverity == DiagnosticSeverity.Error);
}
/// <summary>
/// Returns all the descendant operations of the given <paramref name="operation"/> in evaluation order.
/// </summary>
/// <param name="operation">Operation whose descendants are to be fetched.</param>
public static IEnumerable<IOperation> Descendants(this IOperation operation)
{
return Descendants(operation, includeSelf: false);
}
/// <summary>
/// Returns all the descendant operations of the given <paramref name="operation"/> including the given <paramref name="operation"/> in evaluation order.
/// </summary>
/// <param name="operation">Operation whose descendants are to be fetched.</param>
public static IEnumerable<IOperation> DescendantsAndSelf(this IOperation operation)
{
return Descendants(operation, includeSelf: true);
}
private static IEnumerable<IOperation> Descendants(IOperation operation, bool includeSelf)
{
if (operation == null)
{
yield break;
}
if (includeSelf)
{
yield return operation;
}
var stack = ArrayBuilder<IEnumerator<IOperation>>.GetInstance();
stack.Push(operation.Children.GetEnumerator());
while (stack.Any())
{
var iterator = stack.Pop();
if (!iterator.MoveNext())
{
continue;
}
var current = iterator.Current;
// push current iterator back in to the stack
stack.Push(iterator);
// push children iterator to the stack
if (current != null)
{
yield return current;
stack.Push(current.Children.GetEnumerator());
}
}
stack.Free();
}
/// <summary>
/// Gets all the declared local variables in the given <paramref name="declarationGroup"/>.
/// </summary>
/// <param name="declarationGroup">Variable declaration group</param>
public static ImmutableArray<ILocalSymbol> GetDeclaredVariables(this IVariableDeclarationGroupOperation declarationGroup)
{
if (declarationGroup == null)
{
throw new ArgumentNullException(nameof(declarationGroup));
}
var arrayBuilder = ArrayBuilder<ILocalSymbol>.GetInstance();
foreach (IVariableDeclarationOperation group in declarationGroup.Declarations)
{
group.GetDeclaredVariables(arrayBuilder);
}
return arrayBuilder.ToImmutableAndFree();
}
/// <summary>
/// Gets all the declared local variables in the given <paramref name="declaration"/>.
/// </summary>
/// <param name="declaration">Variable declaration</param>
public static ImmutableArray<ILocalSymbol> GetDeclaredVariables(this IVariableDeclarationOperation declaration)
{
if (declaration == null)
{
throw new ArgumentNullException(nameof(declaration));
}
var arrayBuilder = ArrayBuilder<ILocalSymbol>.GetInstance();
declaration.GetDeclaredVariables(arrayBuilder);
return arrayBuilder.ToImmutableAndFree();
}
private static void GetDeclaredVariables(this IVariableDeclarationOperation declaration, ArrayBuilder<ILocalSymbol> arrayBuilder)
{
foreach (var decl in declaration.Declarators)
{
arrayBuilder.Add(decl.Symbol);
}
}
/// <summary>
/// Gets the variable initializer for the given <paramref name="declarationOperation"/>, checking to see if there is a parent initializer
/// if the single variable initializer is null.
/// </summary>
/// <param name="declarationOperation">Single variable declaration to retrieve initializer for.</param>
public static IVariableInitializerOperation GetVariableInitializer(this IVariableDeclaratorOperation declarationOperation)
{
if (declarationOperation == null)
{
throw new ArgumentNullException(nameof(declarationOperation));
}
return declarationOperation.Initializer ?? (declarationOperation.Parent as IVariableDeclarationOperation)?.Initializer;
}
/// <summary>
/// Get an optional argument name for a named argument to the given <paramref name="dynamicOperation"/> at the given <paramref name="index"/>.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static string GetArgumentName(this IDynamicInvocationOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentName((HasDynamicArgumentsExpression)dynamicOperation, index);
}
/// <summary>
/// Get an optional argument name for a named argument to the given <paramref name="dynamicOperation"/> at the given <paramref name="index"/>.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static string GetArgumentName(this IDynamicIndexerAccessOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentName((HasDynamicArgumentsExpression)dynamicOperation, index);
}
/// <summary>
/// Get an optional argument name for a named argument to the given <paramref name="dynamicOperation"/> at the given <paramref name="index"/>.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static string GetArgumentName(this IDynamicObjectCreationOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentName((HasDynamicArgumentsExpression)dynamicOperation, index);
}
/// <summary>
/// Get an optional argument name for a named argument to the given <paramref name="dynamicOperation"/> at the given <paramref name="index"/>.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
internal static string GetArgumentName(this HasDynamicArgumentsExpression dynamicOperation, int index)
{
if (dynamicOperation.Arguments.IsDefaultOrEmpty)
{
throw new InvalidOperationException();
}
if (index < 0 || index >= dynamicOperation.Arguments.Length)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
var argumentNames = dynamicOperation.ArgumentNames;
return argumentNames.IsDefaultOrEmpty ? null : argumentNames[index];
}
/// <summary>
/// Get an optional argument <see cref="RefKind"/> for an argument at the given <paramref name="index"/> to the given <paramref name="dynamicOperation"/>.
/// Returns a non-null argument <see cref="RefKind"/> for C#.
/// Always returns null for VB as <see cref="RefKind"/> cannot be specified for an the argument in VB.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static RefKind? GetArgumentRefKind(this IDynamicInvocationOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentRefKind((HasDynamicArgumentsExpression)dynamicOperation, index);
}
/// <summary>
/// Get an optional argument <see cref="RefKind"/> for an argument at the given <paramref name="index"/> to the given <paramref name="dynamicOperation"/>.
/// Returns a non-null argument <see cref="RefKind"/> for C#.
/// Always returns null for VB as <see cref="RefKind"/> cannot be specified for an the argument in VB.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static RefKind? GetArgumentRefKind(this IDynamicIndexerAccessOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentRefKind((HasDynamicArgumentsExpression)dynamicOperation, index);
}
/// <summary>
/// Get an optional argument <see cref="RefKind"/> for an argument at the given <paramref name="index"/> to the given <paramref name="dynamicOperation"/>.
/// Returns a non-null argument <see cref="RefKind"/> for C#.
/// Always returns null for VB as <see cref="RefKind"/> cannot be specified for an the argument in VB.
/// </summary>
/// <param name="dynamicOperation">Dynamic or late bound operation.</param>
/// <param name="index">Argument index.</param>
public static RefKind? GetArgumentRefKind(this IDynamicObjectCreationOperation dynamicOperation, int index)
{
if (dynamicOperation == null)
{
throw new ArgumentNullException(nameof(dynamicOperation));
}
return GetArgumentRefKind((HasDynamicArgumentsExpression)dynamicOperation, index);
}
internal static RefKind? GetArgumentRefKind(this HasDynamicArgumentsExpression dynamicOperation, int index)
{
if (dynamicOperation.Arguments.IsDefaultOrEmpty)
{
throw new InvalidOperationException();
}
if (index < 0 || index >= dynamicOperation.Arguments.Length)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
var argumentRefKinds = dynamicOperation.ArgumentRefKinds;
if (argumentRefKinds.IsDefault)
{
// VB case, arguments cannot have RefKind.
return null;
}
if (argumentRefKinds.IsEmpty)
{
// C# case where no explicit RefKind was specified for any argument, hence all arguments have RefKind.None.
return RefKind.None;
}
return argumentRefKinds[index];
}
/// <summary>
/// Gets the root operation for the <see cref="IOperation"/> tree containing the given <paramref name="operation"/>.
/// </summary>
/// <param name="operation">Operation whose root is requested.</param>
internal static IOperation GetRootOperation(this IOperation operation)
{
Debug.Assert(operation != null);
while (operation.Parent != null)
{
operation = operation.Parent;
}
return operation;
}
/// <summary>
/// Gets either a loop or a switch operation that corresponds to the given branch operation.
/// </summary>
/// <param name="operation">The branch operation for which a corresponding operation is looked up</param>
/// <returns>The corresponding operation or <c>null</c> in case not found (e.g. no loop or switch syntax, or the branch is not a break or continue)</returns>
/// <exception cref="ArgumentNullException"><paramref name="operation"/> is null</exception>
/// <exception cref="InvalidOperationException">The operation is a part of Control Flow Graph</exception>
public static IOperation GetCorrespondingOperation(this IBranchOperation operation)
{
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
if (operation.SemanticModel == null)
{
throw new InvalidOperationException(CodeAnalysisResources.OperationMustNotBeControlFlowGraphPart);
}
if (operation.BranchKind != BranchKind.Break && operation.BranchKind != BranchKind.Continue)
{
return null;
}
if (operation.Target == null)
{
return null;
}
for (IOperation current = operation; current.Parent != null; current = current.Parent)
{
switch (current)
{
case ILoopOperation correspondingLoop when operation.Target.Equals(correspondingLoop.ExitLabel) ||
operation.Target.Equals(correspondingLoop.ContinueLabel):
return correspondingLoop;
case ISwitchOperation correspondingSwitch when operation.Target.Equals(correspondingSwitch.ExitLabel):
return correspondingSwitch;
}
}
return default;
}
}
}