|
// 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 disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Test.Utilities
{
public sealed class TestOperationVisitor : OperationVisitor
{
public static readonly TestOperationVisitor Singleton = new TestOperationVisitor();
private TestOperationVisitor()
: base()
{ }
public override void DefaultVisit(IOperation operation)
{
throw new NotImplementedException(operation.GetType().ToString());
}
internal override void VisitNoneOperation(IOperation operation)
{
#if Test_IOperation_None_Kind
Assert.True(false, "Encountered an IOperation with `Kind == OperationKind.None` while walking the operation tree.");
#endif
Assert.Equal(OperationKind.None, operation.Kind);
}
public override void Visit(IOperation operation)
{
if (operation != null)
{
var syntax = operation.Syntax;
var type = operation.Type;
var constantValue = operation.ConstantValue;
Assert.NotNull(syntax);
// operation.Language can throw due to https://github.com/dotnet/roslyn/issues/23821
// Conditional logic below should be removed once the issue is fixed
if (syntax is Microsoft.CodeAnalysis.Syntax.SyntaxList)
{
Assert.Equal(OperationKind.None, operation.Kind);
Assert.Equal(LanguageNames.CSharp, operation.Parent.Language);
}
else
{
var language = operation.Language;
}
var isImplicit = operation.IsImplicit;
var count = ((Operation)operation).ChildOperationsCount;
var builder = count == 0 ? null : ArrayBuilder<IOperation>.GetInstance(count);
foreach (IOperation child in operation.ChildOperations)
{
Assert.NotNull(child);
builder.Add(child);
}
Assert.Equal(count, builder?.Count ?? 0);
if (count > 0)
{
Assert.Same(builder[0], operation.ChildOperations.First());
Assert.Same(builder[^1], operation.ChildOperations.Last());
var forwards = operation.ChildOperations.GetEnumerator();
Assert.True(forwards.MoveNext());
var first = forwards.Current;
forwards.Reset();
Assert.True(forwards.MoveNext());
Assert.Same(first, forwards.Current);
var reversed = operation.ChildOperations.Reverse().GetEnumerator();
Assert.True(reversed.MoveNext());
var last = reversed.Current;
reversed.Reset();
Assert.True(reversed.MoveNext());
Assert.Same(last, reversed.Current);
}
else
{
Assert.Throws<InvalidOperationException>(() => operation.ChildOperations.First());
Assert.Throws<InvalidOperationException>(() => operation.ChildOperations.Last());
}
foreach (IOperation child in operation.ChildOperations.Reverse())
{
Assert.NotNull(child);
Assert.Same(child, builder[--count]);
}
Assert.Equal(0, count);
builder?.Free();
if (operation.SemanticModel != null)
{
Assert.Same(operation.SemanticModel, operation.SemanticModel.ContainingPublicModelOrSelf);
}
}
base.Visit(operation);
}
public override void VisitBlock(IBlockOperation operation)
{
Assert.Equal(OperationKind.Block, operation.Kind);
VisitLocals(operation.Locals);
AssertEx.Equal(operation.Operations, operation.ChildOperations);
}
public override void VisitVariableDeclarationGroup(IVariableDeclarationGroupOperation operation)
{
Assert.Equal(OperationKind.VariableDeclarationGroup, operation.Kind);
AssertEx.Equal(operation.Declarations, operation.ChildOperations);
}
public override void VisitVariableDeclarator(IVariableDeclaratorOperation operation)
{
Assert.Equal(OperationKind.VariableDeclarator, operation.Kind);
Assert.NotNull(operation.Symbol);
IEnumerable<IOperation> children = operation.IgnoredArguments;
var initializer = operation.Initializer;
if (initializer != null)
{
children = children.Concat(new[] { initializer });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitVariableDeclaration(IVariableDeclarationOperation operation)
{
Assert.Equal(OperationKind.VariableDeclaration, operation.Kind);
IEnumerable<IOperation> children = operation.IgnoredDimensions.Concat(operation.Declarators);
var initializer = operation.Initializer;
if (initializer != null)
{
children = children.Concat(new[] { initializer });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitSwitch(ISwitchOperation operation)
{
Assert.Equal(OperationKind.Switch, operation.Kind);
VisitLocals(operation.Locals);
Assert.NotNull(operation.ExitLabel);
AssertEx.Equal(new[] { operation.Value }.Concat(operation.Cases), operation.ChildOperations);
}
public override void VisitSwitchCase(ISwitchCaseOperation operation)
{
Assert.Equal(OperationKind.SwitchCase, operation.Kind);
VisitLocals(operation.Locals);
AssertEx.Equal(operation.Clauses.Concat(operation.Body), operation.ChildOperations);
VerifySubTree(((SwitchCaseOperation)operation).Condition, hasNonNullParent: true);
}
internal static void VerifySubTree(IOperation root, bool hasNonNullParent = false)
{
if (root != null)
{
if (hasNonNullParent)
{
// This is only ever true for ISwitchCaseOperation.Condition.
Assert.NotNull(root.Parent);
Assert.Same(root, ((SwitchCaseOperation)root.Parent).Condition);
}
else
{
Assert.Null(root.Parent);
}
var explicitNodeMap = new Dictionary<SyntaxNode, IOperation>();
foreach (IOperation descendant in root.DescendantsAndSelf())
{
if (!descendant.IsImplicit)
{
try
{
explicitNodeMap.Add(descendant.Syntax, descendant);
}
catch (ArgumentException)
{
Assert.False(true, $"Duplicate explicit node for syntax ({descendant.Syntax.RawKind}): {descendant.Syntax.ToString()}");
}
}
Singleton.Visit(descendant);
}
}
}
public override void VisitSingleValueCaseClause(ISingleValueCaseClauseOperation operation)
{
VisitCaseClauseOperation(operation);
Assert.Equal(CaseKind.SingleValue, operation.CaseKind);
AssertEx.Equal(new[] { operation.Value }, operation.ChildOperations);
}
private static void VisitCaseClauseOperation(ICaseClauseOperation operation)
{
Assert.Equal(OperationKind.CaseClause, operation.Kind);
_ = operation.Label;
}
public override void VisitRelationalCaseClause(IRelationalCaseClauseOperation operation)
{
VisitCaseClauseOperation(operation);
Assert.Equal(CaseKind.Relational, operation.CaseKind);
var relation = operation.Relation;
AssertEx.Equal(new[] { operation.Value }, operation.ChildOperations);
}
public override void VisitDefaultCaseClause(IDefaultCaseClauseOperation operation)
{
VisitCaseClauseOperation(operation);
Assert.Equal(CaseKind.Default, operation.CaseKind);
Assert.Empty(operation.ChildOperations);
}
private static void VisitLocals(ImmutableArray<ILocalSymbol> locals)
{
foreach (var local in locals)
{
Assert.NotNull(local);
}
}
public override void VisitWhileLoop(IWhileLoopOperation operation)
{
VisitLoop(operation);
Assert.Equal(LoopKind.While, operation.LoopKind);
var conditionIsTop = operation.ConditionIsTop;
var conditionIsUntil = operation.ConditionIsUntil;
IEnumerable<IOperation> children;
if (conditionIsTop)
{
if (operation.IgnoredCondition != null)
{
children = new[] { operation.Condition, operation.Body, operation.IgnoredCondition };
}
else
{
children = new[] { operation.Condition, operation.Body };
}
}
else
{
Assert.Null(operation.IgnoredCondition);
if (operation.Condition != null)
{
children = new[] { operation.Body, operation.Condition };
}
else
{
children = new[] { operation.Body };
}
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitForLoop(IForLoopOperation operation)
{
VisitLoop(operation);
Assert.Equal(LoopKind.For, operation.LoopKind);
VisitLocals(operation.Locals);
VisitLocals(operation.ConditionLocals);
IEnumerable<IOperation> children = operation.Before;
if (operation.Condition != null)
{
children = children.Concat(new[] { operation.Condition });
}
children = children.Concat(new[] { operation.Body });
children = children.Concat(operation.AtLoopBottom);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitForToLoop(IForToLoopOperation operation)
{
VisitLoop(operation);
Assert.Equal(LoopKind.ForTo, operation.LoopKind);
_ = operation.IsChecked;
(ILocalSymbol loopObject, ForToLoopOperationUserDefinedInfo userDefinedInfo) = ((ForToLoopOperation)operation).Info;
if (userDefinedInfo != null)
{
VerifySubTree(userDefinedInfo.Addition);
VerifySubTree(userDefinedInfo.Subtraction);
VerifySubTree(userDefinedInfo.LessThanOrEqual);
VerifySubTree(userDefinedInfo.GreaterThanOrEqual);
}
IEnumerable<IOperation> children;
children = new[] { operation.LoopControlVariable, operation.InitialValue, operation.LimitValue, operation.StepValue, operation.Body };
children = children.Concat(operation.NextVariables);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitForEachLoop(IForEachLoopOperation operation)
{
VisitLoop(operation);
Assert.Equal(LoopKind.ForEach, operation.LoopKind);
IEnumerable<IOperation> children = new[] { operation.Collection, operation.LoopControlVariable, operation.Body }.Concat(operation.NextVariables);
AssertEx.Equal(children, operation.ChildOperations);
ForEachLoopOperationInfo info = ((ForEachLoopOperation)operation).Info;
if (info != null)
{
visitArguments(info.GetEnumeratorArguments);
visitArguments(info.MoveNextArguments);
visitArguments(info.CurrentArguments);
visitArguments(info.DisposeArguments);
}
void visitArguments(ImmutableArray<IArgumentOperation> arguments)
{
if (arguments != null)
{
foreach (IArgumentOperation arg in arguments)
{
VerifySubTree(arg);
}
}
}
}
private static void VisitLoop(ILoopOperation operation)
{
Assert.Equal(OperationKind.Loop, operation.Kind);
VisitLocals(operation.Locals);
Assert.NotNull(operation.ContinueLabel);
Assert.NotNull(operation.ExitLabel);
}
public override void VisitLabeled(ILabeledOperation operation)
{
Assert.Equal(OperationKind.Labeled, operation.Kind);
Assert.NotNull(operation.Label);
if (operation.Operation == null)
{
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.Operation, operation.ChildOperations.Single());
}
}
public override void VisitBranch(IBranchOperation operation)
{
Assert.Equal(OperationKind.Branch, operation.Kind);
Assert.NotNull(operation.Target);
switch (operation.BranchKind)
{
case BranchKind.Break:
case BranchKind.Continue:
case BranchKind.GoTo:
break;
default:
Assert.False(true);
break;
}
Assert.Empty(operation.ChildOperations);
}
public override void VisitEmpty(IEmptyOperation operation)
{
Assert.Equal(OperationKind.Empty, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitReturn(IReturnOperation operation)
{
Assert.Contains(operation.Kind, new[] { OperationKind.Return, OperationKind.YieldReturn, OperationKind.YieldBreak });
if (operation.ReturnedValue == null)
{
Assert.NotEqual(OperationKind.YieldReturn, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.ReturnedValue, operation.ChildOperations.Single());
}
}
public override void VisitLock(ILockOperation operation)
{
Assert.Equal(OperationKind.Lock, operation.Kind);
AssertEx.Equal(new[] { operation.LockedValue, operation.Body }, operation.ChildOperations);
}
public override void VisitTry(ITryOperation operation)
{
Assert.Equal(OperationKind.Try, operation.Kind);
IEnumerable<IOperation> children = new[] { operation.Body };
_ = operation.ExitLabel;
children = children.Concat(operation.Catches);
if (operation.Finally != null)
{
children = children.Concat(new[] { operation.Finally });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitCatchClause(ICatchClauseOperation operation)
{
Assert.Equal(OperationKind.CatchClause, operation.Kind);
var exceptionType = operation.ExceptionType;
VisitLocals(operation.Locals);
IEnumerable<IOperation> children = Array.Empty<IOperation>();
if (operation.ExceptionDeclarationOrExpression != null)
{
children = children.Concat(new[] { operation.ExceptionDeclarationOrExpression });
}
if (operation.Filter != null)
{
children = children.Concat(new[] { operation.Filter });
}
children = children.Concat(new[] { operation.Handler });
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitUsing(IUsingOperation operation)
{
Assert.Equal(OperationKind.Using, operation.Kind);
VisitLocals(operation.Locals);
AssertEx.Equal(new[] { operation.Resources, operation.Body }, operation.ChildOperations);
Assert.NotEqual(OperationKind.VariableDeclaration, operation.Resources.Kind);
Assert.NotEqual(OperationKind.VariableDeclarator, operation.Resources.Kind);
_ = ((UsingOperation)operation).DisposeInfo.DisposeMethod;
var disposeArgs = ((UsingOperation)operation).DisposeInfo.DisposeArguments;
if (!disposeArgs.IsDefaultOrEmpty)
{
foreach (var arg in disposeArgs)
{
VerifySubTree(arg);
}
}
}
// https://github.com/dotnet/roslyn/issues/21281
internal override void VisitFixed(IFixedOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
VisitLocals(operation.Locals);
AssertEx.Equal(new[] { operation.Variables, operation.Body }, operation.ChildOperations);
}
public override void VisitCollectionExpression(ICollectionExpressionOperation operation)
{
Assert.Equal(OperationKind.CollectionExpression, operation.Kind);
AssertEx.Equal(operation.Elements, operation.ChildOperations);
}
public override void VisitSpread(ISpreadOperation operation)
{
Assert.Equal(OperationKind.Spread, operation.Kind);
Assert.Same(operation.Operand, operation.ChildOperations.Single());
}
internal override void VisitAggregateQuery(IAggregateQueryOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
AssertEx.Equal(new[] { operation.Group, operation.Aggregation }, operation.ChildOperations);
}
public override void VisitExpressionStatement(IExpressionStatementOperation operation)
{
Assert.Equal(OperationKind.ExpressionStatement, operation.Kind);
Assert.Same(operation.Operation, operation.ChildOperations.Single());
}
internal override void VisitWithStatement(IWithStatementOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
AssertEx.Equal(new[] { operation.Value, operation.Body }, operation.ChildOperations);
}
public override void VisitStop(IStopOperation operation)
{
Assert.Equal(OperationKind.Stop, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitEnd(IEndOperation operation)
{
Assert.Equal(OperationKind.End, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitInvocation(IInvocationOperation operation)
{
Assert.Equal(OperationKind.Invocation, operation.Kind);
Assert.NotNull(operation.TargetMethod);
var isVirtual = operation.IsVirtual;
AssertConstrainedToType(operation.TargetMethod, operation.ConstrainedToType);
if (operation.ConstrainedToType is not null)
{
Assert.True(isVirtual);
}
IEnumerable<IOperation> children;
if (operation.Instance != null)
{
children = new[] { operation.Instance }.Concat(operation.Arguments);
}
else
{
children = operation.Arguments;
}
AssertEx.Equal(children, operation.ChildOperations);
// Make sure that all static member references or invocations of static methods do not have implicit IInstanceReferenceOperations
// as their receivers
if (operation.TargetMethod.IsStatic &&
operation.Instance is IInstanceReferenceOperation)
{
Assert.False(operation.Instance.IsImplicit, $"Implicit {nameof(IInstanceReferenceOperation)} on {operation.Syntax}");
}
}
public override void VisitFunctionPointerInvocation(IFunctionPointerInvocationOperation operation)
{
Assert.Equal(OperationKind.FunctionPointerInvocation, operation.Kind);
Assert.NotNull(operation.Target);
IEnumerable<IOperation> children = new[] { operation.Target }.Concat(operation.Arguments);
var signature = operation.GetFunctionPointerSignature();
Assert.NotNull(signature);
Assert.Same(((IFunctionPointerTypeSymbol)operation.Target.Type).Signature, signature);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitArgument(IArgumentOperation operation)
{
Assert.Equal(OperationKind.Argument, operation.Kind);
Assert.Contains(operation.ArgumentKind, new[] { ArgumentKind.DefaultValue, ArgumentKind.Explicit, ArgumentKind.ParamArray, ArgumentKind.ParamCollection });
var parameter = operation.Parameter;
Assert.Same(operation.Value, operation.ChildOperations.Single());
var inConversion = operation.InConversion;
var outConversion = operation.OutConversion;
if (operation.ArgumentKind == ArgumentKind.DefaultValue)
{
Assert.True(operation.Descendants().All(n => n.IsImplicit), $"Explicit node in default argument value ({operation.Syntax.RawKind}): {operation.Syntax.ToString()}");
}
}
public override void VisitOmittedArgument(IOmittedArgumentOperation operation)
{
Assert.Equal(OperationKind.OmittedArgument, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitArrayElementReference(IArrayElementReferenceOperation operation)
{
Assert.Equal(OperationKind.ArrayElementReference, operation.Kind);
AssertEx.Equal(new[] { operation.ArrayReference }.Concat(operation.Indices), operation.ChildOperations);
}
public override void VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation)
{
Assert.Equal(OperationKind.ImplicitIndexerReference, operation.Kind);
AssertEx.Equal(new[] { operation.Instance, operation.Argument }, operation.ChildOperations);
Assert.NotNull(operation.LengthSymbol);
Assert.NotNull(operation.IndexerSymbol);
}
public override void VisitInlineArrayAccess(IInlineArrayAccessOperation operation)
{
Assert.Equal(OperationKind.InlineArrayAccess, operation.Kind);
AssertEx.Equal(new[] { operation.Instance, operation.Argument }, operation.ChildOperations);
}
internal override void VisitPointerIndirectionReference(IPointerIndirectionReferenceOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
Assert.Same(operation.Pointer, operation.ChildOperations.Single());
}
public override void VisitLocalReference(ILocalReferenceOperation operation)
{
Assert.Equal(OperationKind.LocalReference, operation.Kind);
Assert.NotNull(operation.Local);
var isDeclaration = operation.IsDeclaration;
Assert.Empty(operation.ChildOperations);
}
public override void VisitParameterReference(IParameterReferenceOperation operation)
{
Assert.Equal(OperationKind.ParameterReference, operation.Kind);
Assert.NotNull(operation.Parameter);
Assert.Empty(operation.ChildOperations);
}
public override void VisitInstanceReference(IInstanceReferenceOperation operation)
{
Assert.Equal(OperationKind.InstanceReference, operation.Kind);
Assert.Empty(operation.ChildOperations);
var referenceKind = operation.ReferenceKind;
}
private void VisitMemberReference(IMemberReferenceOperation operation)
{
VisitMemberReference(operation, Array.Empty<IOperation>());
}
private void VisitMemberReference(IMemberReferenceOperation operation, IEnumerable<IOperation> additionalChildren)
{
Assert.NotNull(operation.Member);
AssertConstrainedToType(operation.Member, operation.ConstrainedToType);
IEnumerable<IOperation> children;
if (operation.Instance != null)
{
children = new[] { operation.Instance }.Concat(additionalChildren);
// Make sure that all static member references or invocations of static methods do not have implicit IInstanceReferenceOperations
// as their receivers
if (operation.Member.IsStatic &&
operation.Instance is IInstanceReferenceOperation)
{
Assert.False(operation.Instance.IsImplicit, $"Implicit {nameof(IInstanceReferenceOperation)} on {operation.Syntax}");
}
}
else
{
children = additionalChildren;
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitFieldReference(IFieldReferenceOperation operation)
{
Assert.Equal(OperationKind.FieldReference, operation.Kind);
VisitMemberReference(operation);
Assert.Null(operation.ConstrainedToType);
Assert.Same(operation.Member, operation.Field);
var isDeclaration = operation.IsDeclaration;
}
public override void VisitMethodReference(IMethodReferenceOperation operation)
{
Assert.Equal(OperationKind.MethodReference, operation.Kind);
VisitMemberReference(operation);
Assert.Same(operation.Member, operation.Method);
var isVirtual = operation.IsVirtual;
if (operation.ConstrainedToType is not null)
{
Assert.True(isVirtual);
}
}
public override void VisitPropertyReference(IPropertyReferenceOperation operation)
{
Assert.Equal(OperationKind.PropertyReference, operation.Kind);
VisitMemberReference(operation, operation.Arguments);
Assert.Same(operation.Member, operation.Property);
}
public override void VisitEventReference(IEventReferenceOperation operation)
{
Assert.Equal(OperationKind.EventReference, operation.Kind);
VisitMemberReference(operation);
Assert.Same(operation.Member, operation.Event);
}
public override void VisitEventAssignment(IEventAssignmentOperation operation)
{
Assert.Equal(OperationKind.EventAssignment, operation.Kind);
var adds = operation.Adds;
AssertEx.Equal(new[] { operation.EventReference, operation.HandlerValue }, operation.ChildOperations);
}
public override void VisitConditionalAccess(IConditionalAccessOperation operation)
{
Assert.Equal(OperationKind.ConditionalAccess, operation.Kind);
Assert.NotNull(operation.Type);
AssertEx.Equal(new[] { operation.Operation, operation.WhenNotNull }, operation.ChildOperations);
}
public override void VisitConditionalAccessInstance(IConditionalAccessInstanceOperation operation)
{
Assert.Equal(OperationKind.ConditionalAccessInstance, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
internal override void VisitPlaceholder(IPlaceholderOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitUnaryOperator(IUnaryOperation operation)
{
Assert.Equal(OperationKind.UnaryOperator, operation.Kind);
Assert.Equal(OperationKind.Unary, operation.Kind);
var operatorMethod = operation.OperatorMethod;
var unaryOperationKind = operation.OperatorKind;
var isLifted = operation.IsLifted;
var isChecked = operation.IsChecked;
AssertConstrainedToType(operatorMethod, operation.ConstrainedToType);
Assert.Same(operation.Operand, operation.ChildOperations.Single());
CheckOperators(operation.SemanticModel, operation.Syntax);
}
public override void VisitBinaryOperator(IBinaryOperation operation)
{
Assert.Equal(OperationKind.BinaryOperator, operation.Kind);
Assert.Equal(OperationKind.Binary, operation.Kind);
var operatorMethod = operation.OperatorMethod;
var unaryOperatorMethod = ((BinaryOperation)operation).UnaryOperatorMethod;
var binaryOperationKind = operation.OperatorKind;
var isLifted = operation.IsLifted;
var isChecked = operation.IsChecked;
var isCompareText = operation.IsCompareText;
var constrainedToType = operation.ConstrainedToType;
if (binaryOperationKind is BinaryOperatorKind.ConditionalAnd or BinaryOperatorKind.ConditionalOr)
{
if ((operatorMethod is null || !operatorMethod.IsStatic || (!operatorMethod.IsVirtual && !operatorMethod.IsAbstract)) &&
(unaryOperatorMethod is null || !unaryOperatorMethod.IsStatic || (!unaryOperatorMethod.IsVirtual && !unaryOperatorMethod.IsAbstract)))
{
Assert.Null(constrainedToType);
}
else if (constrainedToType is not null) // In error cases we might not have the type parameter
{
Assert.IsAssignableFrom<ITypeParameterSymbol>(constrainedToType);
}
}
else
{
Assert.Null(unaryOperatorMethod);
AssertConstrainedToType(operatorMethod, constrainedToType);
}
AssertEx.Equal(new[] { operation.LeftOperand, operation.RightOperand }, operation.ChildOperations);
CheckOperators(operation.SemanticModel, operation.Syntax);
}
private static void CheckOperators(SemanticModel semanticModel, SyntaxNode syntax)
{
// Directly get the symbol for this operator from the semantic model. This allows us to exercise
// potentially creating synthesized intrinsic operators.
var symbolInfo = semanticModel?.GetSymbolInfo(syntax) ?? default;
foreach (var symbol in symbolInfo.GetAllSymbols())
{
if (symbol is IMethodSymbol method)
{
VisualBasic.SymbolDisplay.ToDisplayString(method, SymbolDisplayFormat.TestFormat);
VisualBasic.SymbolDisplay.ToDisplayString(method);
CSharp.SymbolDisplay.ToDisplayString(method, SymbolDisplayFormat.TestFormat);
CSharp.SymbolDisplay.ToDisplayString(method);
if (method.MethodKind == MethodKind.BuiltinOperator)
{
switch (method.Parameters.Length)
{
case 1:
semanticModel.Compilation.CreateBuiltinOperator(symbol.Name, method.ReturnType, method.Parameters[0].Type);
break;
case 2:
semanticModel.Compilation.CreateBuiltinOperator(symbol.Name, method.ReturnType, method.Parameters[0].Type, method.Parameters[1].Type);
break;
default:
AssertEx.Fail($"Unexpected parameter count for built in method: {method.ToDisplayString()}");
break;
}
}
}
}
}
public override void VisitTupleBinaryOperator(ITupleBinaryOperation operation)
{
Assert.Equal(OperationKind.TupleBinaryOperator, operation.Kind);
Assert.Equal(OperationKind.TupleBinary, operation.Kind);
var binaryOperationKind = operation.OperatorKind;
AssertEx.Equal(new[] { operation.LeftOperand, operation.RightOperand }, operation.ChildOperations);
}
public override void VisitConversion(IConversionOperation operation)
{
Assert.Equal(OperationKind.Conversion, operation.Kind);
var operatorMethod = operation.OperatorMethod;
var conversion = operation.Conversion;
var isChecked = operation.IsChecked;
var isTryCast = operation.IsTryCast;
AssertConstrainedToType(operatorMethod, operation.ConstrainedToType);
switch (operation.Language)
{
case LanguageNames.CSharp:
CSharp.Conversion csharpConversion = CSharp.CSharpExtensions.GetConversion(operation);
Assert.Throws<ArgumentException>(() => VisualBasic.VisualBasicExtensions.GetConversion(operation));
break;
case LanguageNames.VisualBasic:
VisualBasic.Conversion visualBasicConversion = VisualBasic.VisualBasicExtensions.GetConversion(operation);
Assert.Throws<ArgumentException>(() => CSharp.CSharpExtensions.GetConversion(operation));
break;
default:
Debug.Fail($"Language {operation.Language} is unknown!");
break;
}
Assert.Same(operation.Operand, operation.ChildOperations.Single());
if (operatorMethod != null)
{
VisualBasic.SymbolDisplay.ToDisplayString(operatorMethod, SymbolDisplayFormat.TestFormat);
VisualBasic.SymbolDisplay.ToDisplayString(operatorMethod);
CSharp.SymbolDisplay.ToDisplayString(operatorMethod, SymbolDisplayFormat.TestFormat);
CSharp.SymbolDisplay.ToDisplayString(operatorMethod);
}
}
private static void AssertConstrainedToType(ISymbol member, ITypeSymbol constrainedToType)
{
if (member is null || !member.IsStatic || (!member.IsVirtual && !member.IsAbstract))
{
Assert.Null(constrainedToType);
}
else if (constrainedToType is not null) // In error cases we might not have the type parameter
{
Assert.IsAssignableFrom<ITypeParameterSymbol>(constrainedToType);
}
}
public override void VisitConditional(IConditionalOperation operation)
{
Assert.Equal(OperationKind.Conditional, operation.Kind);
bool isRef = operation.IsRef;
if (operation.WhenFalse != null)
{
AssertEx.Equal(new[] { operation.Condition, operation.WhenTrue, operation.WhenFalse }, operation.ChildOperations);
}
else
{
AssertEx.Equal(new[] { operation.Condition, operation.WhenTrue }, operation.ChildOperations);
}
}
public override void VisitCoalesce(ICoalesceOperation operation)
{
Assert.Equal(OperationKind.Coalesce, operation.Kind);
AssertEx.Equal(new[] { operation.Value, operation.WhenNull }, operation.ChildOperations);
var valueConversion = operation.ValueConversion;
}
public override void VisitCoalesceAssignment(ICoalesceAssignmentOperation operation)
{
Assert.Equal(OperationKind.CoalesceAssignment, operation.Kind);
AssertEx.Equal(new[] { operation.Target, operation.Value }, operation.ChildOperations);
}
public override void VisitIsType(IIsTypeOperation operation)
{
Assert.Equal(OperationKind.IsType, operation.Kind);
Assert.NotNull(operation.TypeOperand);
bool isNegated = operation.IsNegated;
Assert.Same(operation.ValueOperand, operation.ChildOperations.Single());
}
public override void VisitSizeOf(ISizeOfOperation operation)
{
Assert.Equal(OperationKind.SizeOf, operation.Kind);
Assert.NotNull(operation.TypeOperand);
Assert.Empty(operation.ChildOperations);
}
public override void VisitTypeOf(ITypeOfOperation operation)
{
Assert.Equal(OperationKind.TypeOf, operation.Kind);
Assert.NotNull(operation.TypeOperand);
Assert.Empty(operation.ChildOperations);
}
public override void VisitAnonymousFunction(IAnonymousFunctionOperation operation)
{
Assert.Equal(OperationKind.AnonymousFunction, operation.Kind);
Assert.NotNull(operation.Symbol);
Assert.Same(operation.Body, operation.ChildOperations.Single());
}
public override void VisitFlowAnonymousFunction(IFlowAnonymousFunctionOperation operation)
{
Assert.Equal(OperationKind.FlowAnonymousFunction, operation.Kind);
Assert.NotNull(operation.Symbol);
Assert.Empty(operation.ChildOperations);
}
public override void VisitLocalFunction(ILocalFunctionOperation operation)
{
Assert.Equal(OperationKind.LocalFunction, operation.Kind);
Assert.NotNull(operation.Symbol);
if (operation.Body != null)
{
var children = operation.ChildOperations.ToImmutableArray();
Assert.Same(operation.Body, children[0]);
if (operation.IgnoredBody != null)
{
Assert.Same(operation.IgnoredBody, children[1]);
Assert.Equal(2, children.Length);
}
else
{
Assert.Equal(1, children.Length);
}
}
else
{
Assert.Null(operation.IgnoredBody);
Assert.Empty(operation.ChildOperations);
}
}
public override void VisitLiteral(ILiteralOperation operation)
{
Assert.Equal(OperationKind.Literal, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitUtf8String(IUtf8StringOperation operation)
{
Assert.Equal(OperationKind.Utf8String, operation.Kind);
Assert.Empty(operation.ChildOperations);
Assert.NotNull(operation.Value);
}
public override void VisitAwait(IAwaitOperation operation)
{
Assert.Equal(OperationKind.Await, operation.Kind);
Assert.Same(operation.Operation, operation.ChildOperations.Single());
}
public override void VisitNameOf(INameOfOperation operation)
{
Assert.Equal(OperationKind.NameOf, operation.Kind);
Assert.Same(operation.Argument, operation.ChildOperations.Single());
}
public override void VisitThrow(IThrowOperation operation)
{
Assert.Equal(OperationKind.Throw, operation.Kind);
if (operation.Exception == null)
{
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.Exception, operation.ChildOperations.Single());
}
}
public override void VisitAddressOf(IAddressOfOperation operation)
{
Assert.Equal(OperationKind.AddressOf, operation.Kind);
Assert.Same(operation.Reference, operation.ChildOperations.Single());
}
public override void VisitObjectCreation(IObjectCreationOperation operation)
{
Assert.Equal(OperationKind.ObjectCreation, operation.Kind);
var constructor = operation.Constructor;
// When parameter-less struct constructor is inaccessible, the constructor symbol is null
if (!operation.Type.IsValueType)
{
Assert.NotNull(constructor);
if (constructor == null)
{
Assert.Empty(operation.Arguments);
}
}
IEnumerable<IOperation> children = operation.Arguments;
if (operation.Initializer != null)
{
children = children.Concat(new[] { operation.Initializer });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation)
{
Assert.Equal(OperationKind.AnonymousObjectCreation, operation.Kind);
AssertEx.Equal(operation.Initializers, operation.ChildOperations);
foreach (var initializer in operation.Initializers)
{
var simpleAssignment = (ISimpleAssignmentOperation)initializer;
var propertyReference = (IPropertyReferenceOperation)simpleAssignment.Target;
Assert.Empty(propertyReference.Arguments);
Assert.Equal(OperationKind.InstanceReference, propertyReference.Instance.Kind);
Assert.Equal(InstanceReferenceKind.ImplicitReceiver, ((IInstanceReferenceOperation)propertyReference.Instance).ReferenceKind);
}
}
public override void VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation)
{
Assert.Equal(OperationKind.DynamicObjectCreation, operation.Kind);
IEnumerable<IOperation> children = operation.Arguments;
if (operation.Initializer != null)
{
children = children.Concat(new[] { operation.Initializer });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitDynamicInvocation(IDynamicInvocationOperation operation)
{
Assert.Equal(OperationKind.DynamicInvocation, operation.Kind);
AssertEx.Equal(new[] { operation.Operation }.Concat(operation.Arguments), operation.ChildOperations);
}
public override void VisitDynamicIndexerAccess(IDynamicIndexerAccessOperation operation)
{
Assert.Equal(OperationKind.DynamicIndexerAccess, operation.Kind);
AssertEx.Equal(new[] { operation.Operation }.Concat(operation.Arguments), operation.ChildOperations);
}
public override void VisitObjectOrCollectionInitializer(IObjectOrCollectionInitializerOperation operation)
{
Assert.Equal(OperationKind.ObjectOrCollectionInitializer, operation.Kind);
AssertEx.Equal(operation.Initializers, operation.ChildOperations);
}
public override void VisitMemberInitializer(IMemberInitializerOperation operation)
{
Assert.Equal(OperationKind.MemberInitializer, operation.Kind);
AssertEx.Equal(new[] { operation.InitializedMember, operation.Initializer }, operation.ChildOperations);
}
private void VisitSymbolInitializer(ISymbolInitializerOperation operation)
{
VisitLocals(operation.Locals);
Assert.Same(operation.Value, operation.ChildOperations.Single());
}
public override void VisitFieldInitializer(IFieldInitializerOperation operation)
{
Assert.Equal(OperationKind.FieldInitializer, operation.Kind);
foreach (var field in operation.InitializedFields)
{
Assert.NotNull(field);
}
VisitSymbolInitializer(operation);
}
public override void VisitVariableInitializer(IVariableInitializerOperation operation)
{
Assert.Equal(OperationKind.VariableInitializer, operation.Kind);
Assert.Empty(operation.Locals);
VisitSymbolInitializer(operation);
}
public override void VisitPropertyInitializer(IPropertyInitializerOperation operation)
{
Assert.Equal(OperationKind.PropertyInitializer, operation.Kind);
foreach (var property in operation.InitializedProperties)
{
Assert.NotNull(property);
}
VisitSymbolInitializer(operation);
}
public override void VisitParameterInitializer(IParameterInitializerOperation operation)
{
Assert.Equal(OperationKind.ParameterInitializer, operation.Kind);
Assert.NotNull(operation.Parameter);
VisitSymbolInitializer(operation);
}
public override void VisitArrayCreation(IArrayCreationOperation operation)
{
Assert.Equal(OperationKind.ArrayCreation, operation.Kind);
IEnumerable<IOperation> children = operation.DimensionSizes;
if (operation.Initializer != null)
{
children = children.Concat(new[] { operation.Initializer });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitArrayInitializer(IArrayInitializerOperation operation)
{
Assert.Equal(OperationKind.ArrayInitializer, operation.Kind);
Assert.Null(operation.Type);
AssertEx.Equal(operation.ElementValues, operation.ChildOperations);
}
private void VisitAssignment(IAssignmentOperation operation)
{
AssertEx.Equal(new[] { operation.Target, operation.Value }, operation.ChildOperations);
}
public override void VisitSimpleAssignment(ISimpleAssignmentOperation operation)
{
Assert.Equal(OperationKind.SimpleAssignment, operation.Kind);
bool isRef = operation.IsRef;
VisitAssignment(operation);
}
public override void VisitCompoundAssignment(ICompoundAssignmentOperation operation)
{
Assert.Equal(OperationKind.CompoundAssignment, operation.Kind);
var operatorMethod = operation.OperatorMethod;
var binaryOperationKind = operation.OperatorKind;
var inConversion = operation.InConversion;
var outConversion = operation.OutConversion;
if (operation.Syntax.Language == LanguageNames.CSharp)
{
Assert.Throws<ArgumentException>("compoundAssignment", () => VisualBasic.VisualBasicExtensions.GetInConversion(operation));
Assert.Throws<ArgumentException>("compoundAssignment", () => VisualBasic.VisualBasicExtensions.GetOutConversion(operation));
var inConversionInternal = CSharp.CSharpExtensions.GetInConversion(operation);
var outConversionInternal = CSharp.CSharpExtensions.GetOutConversion(operation);
}
else
{
Assert.Throws<ArgumentException>("compoundAssignment", () => CSharp.CSharpExtensions.GetInConversion(operation));
Assert.Throws<ArgumentException>("compoundAssignment", () => CSharp.CSharpExtensions.GetOutConversion(operation));
var inConversionInternal = VisualBasic.VisualBasicExtensions.GetInConversion(operation);
var outConversionInternal = VisualBasic.VisualBasicExtensions.GetOutConversion(operation);
}
var isLifted = operation.IsLifted;
var isChecked = operation.IsChecked;
AssertConstrainedToType(operatorMethod, operation.ConstrainedToType);
VisitAssignment(operation);
}
public override void VisitIncrementOrDecrement(IIncrementOrDecrementOperation operation)
{
Assert.Contains(operation.Kind, new[] { OperationKind.Increment, OperationKind.Decrement });
var operatorMethod = operation.OperatorMethod;
var isPostFix = operation.IsPostfix;
var isLifted = operation.IsLifted;
var isChecked = operation.IsChecked;
AssertConstrainedToType(operatorMethod, operation.ConstrainedToType);
Assert.Same(operation.Target, operation.ChildOperations.Single());
}
public override void VisitParenthesized(IParenthesizedOperation operation)
{
Assert.Equal(OperationKind.Parenthesized, operation.Kind);
Assert.Same(operation.Operand, operation.ChildOperations.Single());
}
public override void VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation)
{
Assert.Equal(OperationKind.DynamicMemberReference, operation.Kind);
Assert.NotNull(operation.MemberName);
foreach (var typeArg in operation.TypeArguments)
{
Assert.NotNull(typeArg);
}
var containingType = operation.ContainingType;
if (operation.Instance == null)
{
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.Instance, operation.ChildOperations.Single());
}
}
public override void VisitDefaultValue(IDefaultValueOperation operation)
{
Assert.Equal(OperationKind.DefaultValue, operation.Kind);
Assert.Empty(operation.ChildOperations);
}
public override void VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation)
{
Assert.Equal(OperationKind.TypeParameterObjectCreation, operation.Kind);
if (operation.Initializer == null)
{
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.Initializer, operation.ChildOperations.Single());
}
}
internal override void VisitNoPiaObjectCreation(INoPiaObjectCreationOperation operation)
{
Assert.Equal(OperationKind.None, operation.Kind);
if (operation.Initializer == null)
{
Assert.Empty(operation.ChildOperations);
}
else
{
Assert.Same(operation.Initializer, operation.ChildOperations.Single());
}
}
public override void VisitInvalid(IInvalidOperation operation)
{
Assert.Equal(OperationKind.Invalid, operation.Kind);
}
public override void VisitTuple(ITupleOperation operation)
{
Assert.Equal(OperationKind.Tuple, operation.Kind);
var naturalType = operation.NaturalType;
AssertEx.Equal(operation.Elements, operation.ChildOperations);
}
public override void VisitInterpolatedString(IInterpolatedStringOperation operation)
{
Assert.Equal(OperationKind.InterpolatedString, operation.Kind);
AssertEx.Equal(operation.Parts, operation.ChildOperations);
}
public override void VisitInterpolatedStringText(IInterpolatedStringTextOperation operation)
{
Assert.Equal(OperationKind.InterpolatedStringText, operation.Kind);
if (operation.Text.Kind != OperationKind.Literal)
{
Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.Text).Operand.Kind);
}
Assert.Same(operation.Text, operation.ChildOperations.Single());
}
public override void VisitInterpolation(IInterpolationOperation operation)
{
Assert.Equal(OperationKind.Interpolation, operation.Kind);
IEnumerable<IOperation> children = new[] { operation.Expression };
if (operation.Alignment != null)
{
children = children.Concat(new[] { operation.Alignment });
}
if (operation.FormatString != null)
{
if (operation.FormatString.Kind != OperationKind.Literal)
{
Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.FormatString).Operand.Kind);
}
children = children.Concat(new[] { operation.FormatString });
}
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitInterpolatedStringHandlerCreation(IInterpolatedStringHandlerCreationOperation operation)
{
Assert.Equal(OperationKind.InterpolatedStringHandlerCreation, operation.Kind);
IEnumerable<IOperation> children = new[] { operation.HandlerCreation, operation.Content };
AssertEx.Equal(children, operation.ChildOperations);
Assert.True(operation.HandlerCreation is IObjectCreationOperation or IDynamicObjectCreationOperation or IInvalidOperation);
Assert.True(operation.Content is IInterpolatedStringAdditionOperation or IInterpolatedStringOperation);
_ = operation.HandlerCreationHasSuccessParameter;
_ = operation.HandlerAppendCallsReturnBool;
}
public override void VisitInterpolatedStringAddition(IInterpolatedStringAdditionOperation operation)
{
Assert.Equal(OperationKind.InterpolatedStringAddition, operation.Kind);
AssertEx.Equal(new[] { operation.Left, operation.Right }, operation.ChildOperations);
Assert.True(operation.Left is IInterpolatedStringAdditionOperation or IInterpolatedStringOperation);
Assert.True(operation.Right is IInterpolatedStringAdditionOperation or IInterpolatedStringOperation);
}
public override void VisitInterpolatedStringHandlerArgumentPlaceholder(IInterpolatedStringHandlerArgumentPlaceholderOperation operation)
{
Assert.Equal(OperationKind.InterpolatedStringHandlerArgumentPlaceholder, operation.Kind);
if (operation.PlaceholderKind is InterpolatedStringArgumentPlaceholderKind.CallsiteReceiver or InterpolatedStringArgumentPlaceholderKind.TrailingValidityArgument)
{
Assert.Equal(-1, operation.ArgumentIndex);
}
else
{
Assert.Equal(InterpolatedStringArgumentPlaceholderKind.CallsiteArgument, operation.PlaceholderKind);
Assert.True(operation.ArgumentIndex >= 0);
}
}
public override void VisitInterpolatedStringAppend(IInterpolatedStringAppendOperation operation)
{
Assert.True(operation.Kind is OperationKind.InterpolatedStringAppendFormatted or OperationKind.InterpolatedStringAppendLiteral or OperationKind.InterpolatedStringAppendInvalid);
Assert.True(operation.AppendCall is IInvocationOperation or IDynamicInvocationOperation or IInvalidOperation);
}
private void VisitPatternCommon(IPatternOperation pattern)
{
Assert.NotNull(pattern.InputType);
Assert.NotNull(pattern.NarrowedType);
Assert.Null(pattern.Type);
Assert.False(pattern.ConstantValue.HasValue);
}
public override void VisitConstantPattern(IConstantPatternOperation operation)
{
Assert.Equal(OperationKind.ConstantPattern, operation.Kind);
VisitPatternCommon(operation);
Assert.Same(operation.Value, operation.ChildOperations.Single());
}
public override void VisitRelationalPattern(IRelationalPatternOperation operation)
{
Assert.Equal(OperationKind.RelationalPattern, operation.Kind);
Assert.True(operation.OperatorKind is Operations.BinaryOperatorKind.LessThan or
Operations.BinaryOperatorKind.LessThanOrEqual or
Operations.BinaryOperatorKind.GreaterThan or
Operations.BinaryOperatorKind.GreaterThanOrEqual or
Operations.BinaryOperatorKind.Equals or // Error cases
Operations.BinaryOperatorKind.NotEquals);
VisitPatternCommon(operation);
Assert.Same(operation.Value, operation.ChildOperations.Single());
}
public override void VisitBinaryPattern(IBinaryPatternOperation operation)
{
Assert.Equal(OperationKind.BinaryPattern, operation.Kind);
VisitPatternCommon(operation);
Assert.True(operation.OperatorKind switch { Operations.BinaryOperatorKind.Or => true, Operations.BinaryOperatorKind.And => true, _ => false });
var children = operation.ChildOperations.ToArray();
Assert.Equal(2, children.Length);
Assert.Same(operation.LeftPattern, children[0]);
Assert.Same(operation.RightPattern, children[1]);
}
public override void VisitNegatedPattern(INegatedPatternOperation operation)
{
Assert.Equal(OperationKind.NegatedPattern, operation.Kind);
VisitPatternCommon(operation);
Assert.Same(operation.Pattern, operation.ChildOperations.Single());
}
public override void VisitTypePattern(ITypePatternOperation operation)
{
Assert.Equal(OperationKind.TypePattern, operation.Kind);
Assert.NotNull(operation.MatchedType);
VisitPatternCommon(operation);
Assert.Empty(operation.ChildOperations);
}
public override void VisitDeclarationPattern(IDeclarationPatternOperation operation)
{
Assert.Equal(OperationKind.DeclarationPattern, operation.Kind);
VisitPatternCommon(operation);
if (operation.Syntax.IsKind(CSharp.SyntaxKind.VarPattern) ||
// in `var (x, y)`, the syntax here is the designation `x`.
operation.Syntax.IsKind(CSharp.SyntaxKind.SingleVariableDesignation))
{
Assert.True(operation.MatchesNull);
Assert.Null(operation.MatchedType);
}
else
{
Assert.False(operation.MatchesNull);
Assert.NotNull(operation.MatchedType);
}
var designation =
(operation.Syntax as CSharp.Syntax.DeclarationPatternSyntax)?.Designation ??
(operation.Syntax as CSharp.Syntax.VarPatternSyntax)?.Designation ??
(operation.Syntax as CSharp.Syntax.VariableDesignationSyntax);
if (designation.IsKind(CSharp.SyntaxKind.SingleVariableDesignation))
{
Assert.NotNull(operation.DeclaredSymbol);
}
else
{
Assert.Null(operation.DeclaredSymbol);
}
Assert.Empty(operation.ChildOperations);
}
public override void VisitSlicePattern(ISlicePatternOperation operation)
{
Assert.Equal(OperationKind.SlicePattern, operation.Kind);
VisitPatternCommon(operation);
if (operation.Pattern != null)
{
Assert.Same(operation.Pattern, operation.ChildOperations.Single());
}
else
{
Assert.Empty(operation.ChildOperations);
}
}
public override void VisitListPattern(IListPatternOperation operation)
{
Assert.Equal(OperationKind.ListPattern, operation.Kind);
VisitPatternCommon(operation);
var designation = (operation.Syntax as CSharp.Syntax.ListPatternSyntax)?.Designation;
if (designation.IsKind(CSharp.SyntaxKind.SingleVariableDesignation))
{
Assert.NotNull(operation.DeclaredSymbol);
}
else
{
Assert.Null(operation.DeclaredSymbol);
}
IEnumerable<IOperation> children = operation.Patterns.Cast<IOperation>();
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitRecursivePattern(IRecursivePatternOperation operation)
{
Assert.Equal(OperationKind.RecursivePattern, operation.Kind);
VisitPatternCommon(operation);
Assert.NotNull(operation.MatchedType);
switch (operation.DeconstructSymbol)
{
case IErrorTypeSymbol error:
case null: // OK: indicates deconstruction of a tuple, or an error case
break;
case IMethodSymbol method:
// when we have a method, it is a `Deconstruct` method
Assert.Equal("Deconstruct", method.Name);
break;
case ITypeSymbol type:
// when we have a type, it is the type "ITuple"
Assert.Equal("ITuple", type.Name);
break;
default:
Assert.True(false, $"Unexpected symbol {operation.DeconstructSymbol}");
break;
}
var designation = (operation.Syntax as CSharp.Syntax.RecursivePatternSyntax)?.Designation;
if (designation.IsKind(CSharp.SyntaxKind.SingleVariableDesignation))
{
Assert.NotNull(operation.DeclaredSymbol);
}
else
{
Assert.Null(operation.DeclaredSymbol);
}
foreach (var subpat in operation.PropertySubpatterns)
{
Assert.True(subpat is IPropertySubpatternOperation);
}
IEnumerable<IOperation> children = operation.DeconstructionSubpatterns.Cast<IOperation>();
children = children.Concat(operation.PropertySubpatterns);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitPropertySubpattern(IPropertySubpatternOperation operation)
{
Assert.NotNull(operation.Pattern);
var children = new IOperation[] { operation.Member, operation.Pattern };
AssertEx.Equal(children, operation.ChildOperations);
if (operation.Member.Kind == OperationKind.Invalid)
{
return;
}
Assert.True(operation.Member is IMemberReferenceOperation);
var member = (IMemberReferenceOperation)operation.Member;
switch (member.Member)
{
case IFieldSymbol field:
case IPropertySymbol prop:
break;
case var symbol:
Assert.True(false, $"Unexpected symbol {symbol}");
break;
}
}
public override void VisitSwitchExpression(ISwitchExpressionOperation operation)
{
//force the existence of IsExhaustive
_ = operation.IsExhaustive;
Assert.NotNull(operation.Type);
Assert.False(operation.ConstantValue.HasValue);
Assert.Equal(OperationKind.SwitchExpression, operation.Kind);
Assert.NotNull(operation.Value);
var children = operation.Arms.Cast<IOperation>().Prepend(operation.Value);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitSwitchExpressionArm(ISwitchExpressionArmOperation operation)
{
Assert.Null(operation.Type);
Assert.False(operation.ConstantValue.HasValue);
Assert.NotNull(operation.Pattern);
_ = operation.Guard;
Assert.NotNull(operation.Value);
VisitLocals(operation.Locals);
var children = operation.Guard == null
? new[] { operation.Pattern, operation.Value }
: new[] { operation.Pattern, operation.Guard, operation.Value };
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitIsPattern(IIsPatternOperation operation)
{
Assert.Equal(OperationKind.IsPattern, operation.Kind);
AssertEx.Equal(new[] { operation.Value, operation.Pattern }, operation.ChildOperations);
}
public override void VisitPatternCaseClause(IPatternCaseClauseOperation operation)
{
VisitCaseClauseOperation(operation);
Assert.Equal(CaseKind.Pattern, operation.CaseKind);
Assert.Same(((ICaseClauseOperation)operation).Label, operation.Label);
if (operation.Guard != null)
{
AssertEx.Equal(new[] { operation.Pattern, operation.Guard }, operation.ChildOperations);
}
else
{
Assert.Same(operation.Pattern, operation.ChildOperations.Single());
}
}
public override void VisitTranslatedQuery(ITranslatedQueryOperation operation)
{
Assert.Equal(OperationKind.TranslatedQuery, operation.Kind);
Assert.Same(operation.Operation, operation.ChildOperations.Single());
}
public override void VisitDeclarationExpression(IDeclarationExpressionOperation operation)
{
Assert.Equal(OperationKind.DeclarationExpression, operation.Kind);
Assert.Same(operation.Expression, operation.ChildOperations.Single());
}
public override void VisitDeconstructionAssignment(IDeconstructionAssignmentOperation operation)
{
Assert.Equal(OperationKind.DeconstructionAssignment, operation.Kind);
VisitAssignment(operation);
}
public override void VisitDelegateCreation(IDelegateCreationOperation operation)
{
Assert.Equal(OperationKind.DelegateCreation, operation.Kind);
Assert.Same(operation.Target, operation.ChildOperations.Single());
}
public override void VisitRaiseEvent(IRaiseEventOperation operation)
{
Assert.Equal(OperationKind.RaiseEvent, operation.Kind);
AssertEx.Equal(new IOperation[] { operation.EventReference }.Concat(operation.Arguments), operation.ChildOperations);
}
public override void VisitRangeCaseClause(IRangeCaseClauseOperation operation)
{
VisitCaseClauseOperation(operation);
Assert.Equal(CaseKind.Range, operation.CaseKind);
AssertEx.Equal(new[] { operation.MinimumValue, operation.MaximumValue }, operation.ChildOperations);
}
public override void VisitConstructorBodyOperation(IConstructorBodyOperation operation)
{
Assert.Equal(OperationKind.ConstructorBodyOperation, operation.Kind);
Assert.Equal(OperationKind.ConstructorBody, operation.Kind);
VisitLocals(operation.Locals);
var builder = ArrayBuilder<IOperation>.GetInstance();
if (operation.Initializer != null)
{
builder.Add(operation.Initializer);
}
if (operation.BlockBody != null)
{
builder.Add(operation.BlockBody);
}
if (operation.ExpressionBody != null)
{
builder.Add(operation.ExpressionBody);
}
AssertEx.Equal(builder, operation.ChildOperations);
builder.Free();
}
public override void VisitMethodBodyOperation(IMethodBodyOperation operation)
{
Assert.Equal(OperationKind.MethodBodyOperation, operation.Kind);
Assert.Equal(OperationKind.MethodBody, operation.Kind);
if (operation.BlockBody != null)
{
if (operation.ExpressionBody != null)
{
AssertEx.Equal(new[] { operation.BlockBody, operation.ExpressionBody }, operation.ChildOperations);
}
else
{
Assert.Same(operation.BlockBody, operation.ChildOperations.Single());
}
}
else if (operation.ExpressionBody != null)
{
Assert.Same(operation.ExpressionBody, operation.ChildOperations.Single());
}
else
{
Assert.Empty(operation.ChildOperations);
}
}
public override void VisitDiscardOperation(IDiscardOperation operation)
{
Assert.Equal(OperationKind.Discard, operation.Kind);
Assert.Empty(operation.ChildOperations);
var discardSymbol = operation.DiscardSymbol;
Assert.Equal(operation.Type, discardSymbol.Type);
}
public override void VisitDiscardPattern(IDiscardPatternOperation operation)
{
Assert.Equal(OperationKind.DiscardPattern, operation.Kind);
VisitPatternCommon(operation);
Assert.Empty(operation.ChildOperations);
}
public override void VisitFlowCapture(IFlowCaptureOperation operation)
{
Assert.Equal(OperationKind.FlowCapture, operation.Kind);
Assert.True(operation.IsImplicit);
Assert.Same(operation.Value, operation.ChildOperations.Single());
switch (operation.Value.Kind)
{
case OperationKind.Invalid:
case OperationKind.None:
case OperationKind.AnonymousFunction:
case OperationKind.FlowCaptureReference:
case OperationKind.DefaultValue:
case OperationKind.FlowAnonymousFunction: // must be an error case like in Microsoft.CodeAnalysis.CSharp.UnitTests.ConditionalOperatorTests.TestBothUntyped unit-test
break;
case OperationKind.OmittedArgument:
case OperationKind.DeclarationExpression:
case OperationKind.Discard:
Assert.False(true, $"A {operation.Value.Kind} node should not be spilled or captured.");
break;
default:
// Only values can be spilled/captured
if (!operation.Value.ConstantValue.HasValue || operation.Value.ConstantValue.Value != null)
{
Assert.NotNull(operation.Value.Type);
}
break;
}
}
public override void VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation)
{
Assert.Equal(OperationKind.FlowCaptureReference, operation.Kind);
Assert.True(operation.IsImplicit);
Assert.Empty(operation.ChildOperations);
}
public override void VisitIsNull(IIsNullOperation operation)
{
Assert.Equal(OperationKind.IsNull, operation.Kind);
Assert.True(operation.IsImplicit);
Assert.Same(operation.Operand, operation.ChildOperations.Single());
}
public override void VisitCaughtException(ICaughtExceptionOperation operation)
{
Assert.Equal(OperationKind.CaughtException, operation.Kind);
Assert.True(operation.IsImplicit);
Assert.Empty(operation.ChildOperations);
}
public override void VisitStaticLocalInitializationSemaphore(IStaticLocalInitializationSemaphoreOperation operation)
{
Assert.Equal(OperationKind.StaticLocalInitializationSemaphore, operation.Kind);
Assert.True(operation.IsImplicit);
Assert.Empty(operation.ChildOperations);
Assert.NotNull(operation.Local);
Assert.True(operation.Local.IsStatic);
}
public override void VisitRangeOperation(IRangeOperation operation)
{
Assert.Equal(OperationKind.Range, operation.Kind);
IOperation[] children = operation.ChildOperations.ToArray();
int index = 0;
if (operation.LeftOperand != null)
{
Assert.Same(operation.LeftOperand, children[index++]);
}
if (operation.RightOperand != null)
{
Assert.Same(operation.RightOperand, children[index++]);
}
Assert.Equal(index, children.Length);
}
public override void VisitReDim(IReDimOperation operation)
{
Assert.Equal(OperationKind.ReDim, operation.Kind);
AssertEx.Equal(operation.Clauses, operation.ChildOperations);
var preserve = operation.Preserve;
}
public override void VisitReDimClause(IReDimClauseOperation operation)
{
Assert.Equal(OperationKind.ReDimClause, operation.Kind);
AssertEx.Equal(SpecializedCollections.SingletonEnumerable(operation.Operand).Concat(operation.DimensionSizes), operation.ChildOperations);
}
public override void VisitUsingDeclaration(IUsingDeclarationOperation operation)
{
Assert.NotNull(operation.DeclarationGroup);
AssertEx.Equal(SpecializedCollections.SingletonEnumerable(operation.DeclarationGroup), operation.ChildOperations);
Assert.True(operation.DeclarationGroup.IsImplicit);
Assert.Null(operation.Type);
Assert.False(operation.ConstantValue.HasValue);
_ = operation.IsAsynchronous;
_ = operation.IsImplicit;
_ = ((UsingDeclarationOperation)operation).DisposeInfo.DisposeMethod;
var disposeArgs = ((UsingDeclarationOperation)operation).DisposeInfo.DisposeArguments;
if (!disposeArgs.IsDefaultOrEmpty)
{
foreach (var arg in disposeArgs)
{
VerifySubTree(arg);
}
}
}
public override void VisitWith(IWithOperation operation)
{
Assert.Equal(OperationKind.With, operation.Kind);
_ = operation.CloneMethod;
IEnumerable<IOperation> children = SpecializedCollections.SingletonEnumerable(operation.Operand).Concat(operation.Initializer);
AssertEx.Equal(children, operation.ChildOperations);
}
public override void VisitAttribute(IAttributeOperation operation)
{
Assert.Equal(OperationKind.Attribute, operation.Kind);
Assert.False(operation.ConstantValue.HasValue);
}
}
}
|