/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs
2 203 строки
80 KB
Jason Malinowski
Switch code that was using Assert.True/False as a replacement for .Fail
21 апр 2026, 23:28
21 апр 2026, 23:28
3bf59ba
Код
Авторство
О чём код?
// 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.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Operations; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Test.Utilities { public class OperationTreeVerifier : OperationWalker { protected readonly Compilation _compilation; protected readonly IOperation _root; protected readonly StringBuilder _builder; private readonly Dictionary<SyntaxNode, IOperation> _explicitNodeMap; private readonly Dictionary<ILabelSymbol, uint> _labelIdMap; private const string indent = " "; protected string _currentIndent; private bool _pendingIndent; private uint _currentLabelId = 0; public OperationTreeVerifier(Compilation compilation, IOperation root, int initialIndent) { _compilation = compilation; _root = root; _builder = new StringBuilder(); _currentIndent = new string(' ', initialIndent); _pendingIndent = true; _explicitNodeMap = new Dictionary<SyntaxNode, IOperation>(); _labelIdMap = new Dictionary<ILabelSymbol, uint>(); } public static string GetOperationTree(Compilation compilation, IOperation operation, int initialIndent = 0) { var walker = new OperationTreeVerifier(compilation, operation, initialIndent); walker.Visit(operation); var visitor = TestOperationVisitor.Singleton; foreach (var op in operation.DescendantsAndSelf()) { visitor.Visit(op); } return walker._builder.ToString(); } public static void Verify(string expectedOperationTree, string actualOperationTree) { char[] newLineChars = Environment.NewLine.ToCharArray(); string actual = actualOperationTree.Trim(newLineChars); actual = actual.Replace(" \n", "\n").Replace(" \r", "\r"); expectedOperationTree = expectedOperationTree.Trim(newLineChars); expectedOperationTree = expectedOperationTree.Replace("\r\n", "\n").Replace(" \n", "\n").Replace("\n", Environment.NewLine); AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedOperationTree, actual); } #region Logging helpers private void LogPatternPropertiesAndNewLine(IPatternOperation operation) { LogPatternProperties(operation); LogString(")"); LogNewLine(); } private void LogPatternProperties(IPatternOperation operation) { LogCommonProperties(operation); LogString(" ("); LogType(operation.InputType, $"{nameof(operation.InputType)}"); LogString(", "); LogType(operation.NarrowedType, $"{nameof(operation.NarrowedType)}"); } private void LogCommonPropertiesAndNewLine(IOperation operation) { LogCommonProperties(operation); LogNewLine(); } private void LogCommonProperties(IOperation operation) { LogString(" ("); // Kind LogString($"{nameof(OperationKind)}.{GetKindText(operation.Kind)}"); // Type LogString(", "); LogType(operation.Type); // ConstantValue if (operation.ConstantValue.HasValue) { LogString(", "); LogConstant(operation.ConstantValue); } // IsInvalid if (operation.HasErrors(_compilation)) { LogString(", IsInvalid"); } // IsImplicit if (operation.IsImplicit) { LogString(", IsImplicit"); } LogString(")"); // Syntax Assert.NotNull(operation.Syntax); LogString($" (Syntax: {GetSnippetFromSyntax(operation.Syntax)})"); // Some of these kinds were inconsistent in the first release, and in standardizing them the // string output isn't guaranteed to be one or the other. So standardize manually. string GetKindText(OperationKind kind) { switch (kind) { case OperationKind.Unary: return "Unary"; case OperationKind.Binary: return "Binary"; case OperationKind.TupleBinary: return "TupleBinary"; case OperationKind.MethodBody: return "MethodBody"; case OperationKind.ConstructorBody: return "ConstructorBody"; default: return kind.ToString(); } } } private static string GetSnippetFromSyntax(SyntaxNode syntax) { if (syntax == null) { return "null"; } var text = syntax.ToString().Trim(Environment.NewLine.ToCharArray()); var lines = text.Split(new[] { Environment.NewLine, "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(l => l.Trim()).ToArray(); if (lines.Length <= 1 && text.Length < 25) { return $"'{text}'"; } const int maxTokenLength = 11; var firstLine = lines[0]; var lastLine = lines[lines.Length - 1]; var prefix = firstLine.Length <= maxTokenLength ? firstLine : firstLine.Substring(0, maxTokenLength); var suffix = lastLine.Length <= maxTokenLength ? lastLine : lastLine.Substring(lastLine.Length - maxTokenLength, maxTokenLength); return $"'{prefix} ... {suffix}'"; } protected void LogString(string str) { if (_pendingIndent) { str = _currentIndent + str; _pendingIndent = false; } _builder.Append(str); } protected void LogNewLine() { LogString(Environment.NewLine); _pendingIndent = true; } private void Indent() { _currentIndent += indent; } private void Unindent() { _currentIndent = _currentIndent.Substring(indent.Length); } private void LogConstant(Optional<object> constant, string header = "Constant") { if (constant.HasValue) { LogConstant(constant.Value, header); } } private static string ConstantToString(object constant) { switch (constant) { case null: return "null"; case string s: s = s.Replace("\"", "\"\""); return @"""" + s + @""""; case IFormattable formattable: return formattable.ToString(null, CultureInfo.InvariantCulture).Replace("\"", "\"\""); default: return constant.ToString().Replace("\"", "\"\""); } } private void LogConstant(object constant, string header = "Constant") { string valueStr = ConstantToString(constant); LogString($"{header}: {valueStr}"); } private void LogConversion(CommonConversion conversion, string header = "Conversion") { var exists = FormatBoolProperty(nameof(conversion.Exists), conversion.Exists); var isIdentity = FormatBoolProperty(nameof(conversion.IsIdentity), conversion.IsIdentity); var isNumeric = FormatBoolProperty(nameof(conversion.IsNumeric), conversion.IsNumeric); var isReference = FormatBoolProperty(nameof(conversion.IsReference), conversion.IsReference); var isUserDefined = FormatBoolProperty(nameof(conversion.IsUserDefined), conversion.IsUserDefined); string isUnion = ""; if (conversion.IsUnion) { isUnion = ", " + FormatBoolProperty(nameof(conversion.IsUnion), conversion.IsUnion); } LogString($"{header}: {nameof(CommonConversion)} ({exists}, {isIdentity}, {isNumeric}, {isReference}, {isUserDefined}{isUnion}) ("); LogSymbol(conversion.MethodSymbol, nameof(conversion.MethodSymbol)); LogString(")"); } private void LogSymbol(ISymbol symbol, string header, bool logDisplayString = true) { if (!string.IsNullOrEmpty(header)) { LogString($"{header}: "); } var symbolStr = symbol != null ? (logDisplayString ? symbol.ToTestDisplayString() : symbol.Name) : "null"; LogString($"{symbolStr}"); } private void LogType(ITypeSymbol type, string header = "Type") { var typeStr = type != null ? type.ToTestDisplayString() : "null"; LogString($"{header}: {typeStr}"); } private uint GetLabelId(ILabelSymbol symbol) { if (_labelIdMap.TryGetValue(symbol, out var id)) { return id; } id = _currentLabelId++; _labelIdMap[symbol] = id; return id; } private static string FormatBoolProperty(string propertyName, bool value) => $"{propertyName}: {(value ? "True" : "False")}"; #endregion #region Visit methods public override void Visit(IOperation operation) { if (operation == null) { Indent(); LogString("null"); LogNewLine(); Unindent(); return; } if (!operation.IsImplicit) { try { _explicitNodeMap.Add(operation.Syntax, operation); } catch (ArgumentException) { Assert.Fail($"Duplicate explicit node for syntax ({operation.Syntax.RawKind}): {operation.Syntax.ToString()}"); } } Assert.True(operation.Type == null || !operation.MustHaveNullType(), $"Unexpected non-null type: {operation.Type}"); if (operation != _root) { Indent(); } base.Visit(operation); if (operation != _root) { Unindent(); } Assert.True(operation.Syntax.Language == operation.Language); } private void Visit(IOperation operation, string header) { Debug.Assert(!string.IsNullOrEmpty(header)); Indent(); LogString($"{header}: "); LogNewLine(); Visit(operation); Unindent(); } private void VisitArrayCommon<T>(ImmutableArray<T> list, string header, bool logElementCount, bool logNullForDefault, Action<T> arrayElementVisitor) { Debug.Assert(!string.IsNullOrEmpty(header)); Indent(); if (!list.IsDefaultOrEmpty) { var elementCount = logElementCount ? $"({list.Count()})" : string.Empty; LogString($"{header}{elementCount}:"); LogNewLine(); Indent(); foreach (var element in list) { arrayElementVisitor(element); } Unindent(); } else { var suffix = logNullForDefault && list.IsDefault ? ": null" : "(0)"; LogString($"{header}{suffix}"); LogNewLine(); } Unindent(); } internal void VisitSymbolArrayElement(ISymbol element) { LogSymbol(element, header: "Symbol"); LogNewLine(); } internal void VisitStringArrayElement(string element) { var valueStr = element != null ? element.ToString() : "null"; valueStr = @"""" + valueStr + @""""; LogString(valueStr); LogNewLine(); } internal void VisitRefKindArrayElement(RefKind element) { LogString(element.ToString()); LogNewLine(); } private void VisitChildren(IOperation operation) { Debug.Assert(operation.ChildOperations.All(o => o != null)); var children = operation.ChildOperations.ToImmutableArray(); if (!children.IsEmpty || operation.Kind != OperationKind.None) { VisitArray(children, "Children", logElementCount: true); } } private void VisitArray<T>(ImmutableArray<T> list, string header, bool logElementCount, bool logNullForDefault = false) where T : IOperation { VisitArrayCommon(list, header, logElementCount, logNullForDefault, o => Visit(o)); } private void VisitArray(ImmutableArray<string> list, string header, bool logElementCount, bool logNullForDefault = false) { VisitArrayCommon(list, header, logElementCount, logNullForDefault, VisitStringArrayElement); } private void VisitArray(ImmutableArray<RefKind> list, string header, bool logElementCount, bool logNullForDefault = false) { VisitArrayCommon(list, header, logElementCount, logNullForDefault, VisitRefKindArrayElement); } private void VisitInstance(IOperation instance) { Visit(instance, header: "Instance Receiver"); } internal override void VisitNoneOperation(IOperation operation) { LogString("IOperation: "); LogCommonPropertiesAndNewLine(operation); VisitChildren(operation); } public override void VisitBlock(IBlockOperation operation) { LogString(nameof(IBlockOperation)); var statementsStr = $"{operation.Operations.Length} statements"; var localStr = !operation.Locals.IsEmpty ? $", {operation.Locals.Length} locals" : string.Empty; LogString($" ({statementsStr}{localStr})"); LogCommonPropertiesAndNewLine(operation); if (operation.Operations.IsEmpty) { return; } LogLocals(operation.Locals); base.VisitBlock(operation); } public override void VisitVariableDeclarationGroup(IVariableDeclarationGroupOperation operation) { var variablesCountStr = $"{operation.Declarations.Length} declarations"; LogString($"{nameof(IVariableDeclarationGroupOperation)} ({variablesCountStr})"); LogCommonPropertiesAndNewLine(operation); base.VisitVariableDeclarationGroup(operation); } public override void VisitUsingDeclaration(IUsingDeclarationOperation operation) { LogString($"{nameof(IUsingDeclarationOperation)}"); LogString($"(IsAsynchronous: {operation.IsAsynchronous}"); var disposeMethod = ((UsingDeclarationOperation)operation).DisposeInfo.DisposeMethod; if (disposeMethod is object) { LogSymbol(disposeMethod, ", DisposeMethod"); } LogString(")"); LogCommonPropertiesAndNewLine(operation); Visit(operation.DeclarationGroup, "DeclarationGroup"); var disposeArgs = ((UsingDeclarationOperation)operation).DisposeInfo.DisposeArguments; if (!disposeArgs.IsDefaultOrEmpty) { VisitArray(disposeArgs, "DisposeArguments", logElementCount: true); } } public override void VisitVariableDeclarator(IVariableDeclaratorOperation operation) { LogString($"{nameof(IVariableDeclaratorOperation)} ("); LogSymbol(operation.Symbol, "Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Initializer, "Initializer"); if (!operation.IgnoredArguments.IsEmpty) { VisitArray(operation.IgnoredArguments, "IgnoredArguments", logElementCount: true); } } public override void VisitVariableDeclaration(IVariableDeclarationOperation operation) { var variableCount = operation.Declarators.Length; LogString($"{nameof(IVariableDeclarationOperation)} ({variableCount} declarators)"); LogCommonPropertiesAndNewLine(operation); if (!operation.IgnoredDimensions.IsEmpty) { VisitArray(operation.IgnoredDimensions, "Ignored Dimensions", true); } VisitArray(operation.Declarators, "Declarators", false); Visit(operation.Initializer, "Initializer"); } public override void VisitSwitch(ISwitchOperation operation) { var caseCountStr = $"{operation.Cases.Length} cases"; var exitLabelStr = $", Exit Label Id: {GetLabelId(operation.ExitLabel)}"; LogString($"{nameof(ISwitchOperation)} ({caseCountStr}{exitLabelStr})"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, header: "Switch expression"); LogLocals(operation.Locals); foreach (ISwitchCaseOperation section in operation.Cases) { foreach (ICaseClauseOperation c in section.Clauses) { if (c.Label != null) { GetLabelId(c.Label); } } } VisitArray(operation.Cases, "Sections", logElementCount: false); } public override void VisitSwitchCase(ISwitchCaseOperation operation) { var caseClauseCountStr = $"{operation.Clauses.Length} case clauses"; var statementCountStr = $"{operation.Body.Length} statements"; LogString($"{nameof(ISwitchCaseOperation)} ({caseClauseCountStr}, {statementCountStr})"); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); Indent(); VisitArray(operation.Clauses, "Clauses", logElementCount: false); VisitArray(operation.Body, "Body", logElementCount: false); Unindent(); _ = ((SwitchCaseOperation)operation).Condition; } public override void VisitWhileLoop(IWhileLoopOperation operation) { LogString(nameof(IWhileLoopOperation)); LogString($" (ConditionIsTop: {operation.ConditionIsTop}, ConditionIsUntil: {operation.ConditionIsUntil})"); LogLoopStatementHeader(operation); Visit(operation.Condition, "Condition"); Visit(operation.Body, "Body"); Visit(operation.IgnoredCondition, "IgnoredCondition"); } public override void VisitForLoop(IForLoopOperation operation) { LogString(nameof(IForLoopOperation)); LogLoopStatementHeader(operation); LogLocals(operation.ConditionLocals, header: nameof(operation.ConditionLocals)); Visit(operation.Condition, "Condition"); VisitArray(operation.Before, "Before", logElementCount: false); VisitArray(operation.AtLoopBottom, "AtLoopBottom", logElementCount: false); Visit(operation.Body, "Body"); } public override void VisitForToLoop(IForToLoopOperation operation) { LogString(nameof(IForToLoopOperation)); LogLoopStatementHeader(operation, operation.IsChecked); Visit(operation.LoopControlVariable, "LoopControlVariable"); Visit(operation.InitialValue, "InitialValue"); Visit(operation.LimitValue, "LimitValue"); Visit(operation.StepValue, "StepValue"); Visit(operation.Body, "Body"); VisitArray(operation.NextVariables, "NextVariables", logElementCount: true); (ILocalSymbol loopObject, ForToLoopOperationUserDefinedInfo userDefinedInfo) = ((ForToLoopOperation)operation).Info; if (userDefinedInfo != null) { _ = userDefinedInfo.Addition; _ = userDefinedInfo.Subtraction; _ = userDefinedInfo.LessThanOrEqual; _ = userDefinedInfo.GreaterThanOrEqual; } } private void LogLocals(IEnumerable<ILocalSymbol> locals, string header = "Locals") { if (!locals.Any()) { return; } Indent(); LogString($"{header}: "); Indent(); int localIndex = 1; foreach (var local in locals) { LogSymbol(local, header: $"Local_{localIndex++}"); LogNewLine(); } Unindent(); Unindent(); } private void LogLoopStatementHeader(ILoopOperation operation, bool? isChecked = null) { Assert.Equal(OperationKind.Loop, operation.Kind); var propertyStringBuilder = new StringBuilder(); propertyStringBuilder.Append(" ("); propertyStringBuilder.Append($"{nameof(LoopKind)}.{operation.LoopKind}"); if (operation is IForEachLoopOperation { IsAsynchronous: true }) { propertyStringBuilder.Append($", IsAsynchronous"); } propertyStringBuilder.Append($", Continue Label Id: {GetLabelId(operation.ContinueLabel)}"); propertyStringBuilder.Append($", Exit Label Id: {GetLabelId(operation.ExitLabel)}"); if (isChecked.GetValueOrDefault()) { propertyStringBuilder.Append($", Checked"); } propertyStringBuilder.Append(')'); LogString(propertyStringBuilder.ToString()); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); } public override void VisitForEachLoop(IForEachLoopOperation operation) { LogString(nameof(IForEachLoopOperation)); LogLoopStatementHeader(operation); Assert.NotNull(operation.LoopControlVariable); Visit(operation.LoopControlVariable, "LoopControlVariable"); Visit(operation.Collection, "Collection"); Visit(operation.Body, "Body"); VisitArray(operation.NextVariables, "NextVariables", logElementCount: true); ForEachLoopOperationInfo info = ((ForEachLoopOperation)operation).Info; } public override void VisitLabeled(ILabeledOperation operation) { LogString(nameof(ILabeledOperation)); if (!operation.Label.IsImplicitlyDeclared) { LogString($" (Label: {operation.Label.Name})"); } else { LogString($" (Label Id: {GetLabelId(operation.Label)})"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Statement"); } public override void VisitBranch(IBranchOperation operation) { LogString(nameof(IBranchOperation)); var kindStr = $"{nameof(BranchKind)}.{operation.BranchKind}"; // If the label is implicit, or if it has been assigned an id (such as VB Exit Do/While/Switch labels) then print the id, instead of the name. var labelStr = !(operation.Target.IsImplicitlyDeclared || _labelIdMap.ContainsKey(operation.Target)) ? $", Label: {operation.Target.Name}" : $", Label Id: {GetLabelId(operation.Target)}"; LogString($" ({kindStr}{labelStr})"); LogCommonPropertiesAndNewLine(operation); base.VisitBranch(operation); } public override void VisitEmpty(IEmptyOperation operation) { LogString(nameof(IEmptyOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitReturn(IReturnOperation operation) { LogString(nameof(IReturnOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.ReturnedValue, "ReturnedValue"); } public override void VisitLock(ILockOperation operation) { LogString(nameof(ILockOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.LockedValue, "Expression"); Visit(operation.Body, "Body"); } public override void VisitTry(ITryOperation operation) { LogString(nameof(ITryOperation)); if (operation.ExitLabel != null) { LogString($" (Exit Label Id: {GetLabelId(operation.ExitLabel)})"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.Body, "Body"); VisitArray(operation.Catches, "Catch clauses", logElementCount: true); Visit(operation.Finally, "Finally"); } public override void VisitCatchClause(ICatchClauseOperation operation) { LogString(nameof(ICatchClauseOperation)); var exceptionTypeStr = operation.ExceptionType != null ? operation.ExceptionType.ToTestDisplayString() : "null"; LogString($" (Exception type: {exceptionTypeStr})"); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); Visit(operation.ExceptionDeclarationOrExpression, "ExceptionDeclarationOrExpression"); Visit(operation.Filter, "Filter"); Visit(operation.Handler, "Handler"); } public override void VisitUsing(IUsingOperation operation) { LogString(nameof(IUsingOperation)); if (operation.IsAsynchronous) { LogString($" (IsAsynchronous)"); } var disposeMethod = ((UsingOperation)operation).DisposeInfo.DisposeMethod; if (disposeMethod is object) { LogSymbol(disposeMethod, " (DisposeMethod"); LogString(")"); } LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); Visit(operation.Resources, "Resources"); Visit(operation.Body, "Body"); Assert.NotEqual(OperationKind.VariableDeclaration, operation.Resources.Kind); Assert.NotEqual(OperationKind.VariableDeclarator, operation.Resources.Kind); var disposeArgs = ((UsingOperation)operation).DisposeInfo.DisposeArguments; if (!disposeArgs.IsDefaultOrEmpty) { VisitArray(disposeArgs, "DisposeArguments", logElementCount: true); } } // https://github.com/dotnet/roslyn/issues/21281 internal override void VisitFixed(IFixedOperation operation) { LogString(nameof(IFixedOperation)); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); Visit(operation.Variables, "Declaration"); Visit(operation.Body, "Body"); } internal override void VisitAggregateQuery(IAggregateQueryOperation operation) { LogString(nameof(IAggregateQueryOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Group, "Group"); Visit(operation.Aggregation, "Aggregation"); } public override void VisitExpressionStatement(IExpressionStatementOperation operation) { LogString(nameof(IExpressionStatementOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Expression"); } internal override void VisitWithStatement(IWithStatementOperation operation) { LogString(nameof(IWithStatementOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, "Value"); Visit(operation.Body, "Body"); } public override void VisitStop(IStopOperation operation) { LogString(nameof(IStopOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitEnd(IEndOperation operation) { LogString(nameof(IEndOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitInvocation(IInvocationOperation operation) { LogString(nameof(IInvocationOperation)); var isVirtualStr = operation.IsVirtual ? "virtual " : string.Empty; var spacing = !operation.IsVirtual && operation.Instance != null ? " " : string.Empty; LogString($" ({isVirtualStr}{spacing}"); LogSymbol(operation.TargetMethod, header: string.Empty); if (operation.ConstrainedToType is not null) { LogType(operation.ConstrainedToType, header: " ConstrainedToType"); } LogString(")"); LogCommonPropertiesAndNewLine(operation); VisitInstance(operation.Instance); VisitArguments(operation.Arguments); } public override void VisitFunctionPointerInvocation(IFunctionPointerInvocationOperation operation) { LogString(nameof(IFunctionPointerInvocationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, "Target"); VisitArguments(operation.Arguments); } private void VisitArguments(ImmutableArray<IArgumentOperation> arguments) { VisitArray(arguments, "Arguments", logElementCount: true); } private void VisitDynamicArguments(HasDynamicArgumentsExpression operation) { VisitArray(operation.Arguments, "Arguments", logElementCount: true); VisitArray(operation.ArgumentNames, "ArgumentNames", logElementCount: true); VisitArray(operation.ArgumentRefKinds, "ArgumentRefKinds", logElementCount: true, logNullForDefault: true); VerifyGetArgumentNamePublicApi(operation, operation.ArgumentNames); VerifyGetArgumentRefKindPublicApi(operation, operation.ArgumentRefKinds); } private static void VerifyGetArgumentNamePublicApi(HasDynamicArgumentsExpression operation, ImmutableArray<string> argumentNames) { var length = operation.Arguments.Length; if (argumentNames.IsDefaultOrEmpty) { for (int i = 0; i < length; i++) { Assert.Null(operation.GetArgumentName(i)); } } else { Assert.Equal(length, argumentNames.Length); for (var i = 0; i < length; i++) { Assert.Equal(argumentNames[i], operation.GetArgumentName(i)); } } } private static void VerifyGetArgumentRefKindPublicApi(HasDynamicArgumentsExpression operation, ImmutableArray<RefKind> argumentRefKinds) { var length = operation.Arguments.Length; if (argumentRefKinds.IsDefault) { for (int i = 0; i < length; i++) { Assert.Null(operation.GetArgumentRefKind(i)); } } else if (argumentRefKinds.IsEmpty) { for (int i = 0; i < length; i++) { Assert.Equal(RefKind.None, operation.GetArgumentRefKind(i)); } } else { Assert.Equal(length, argumentRefKinds.Length); for (var i = 0; i < length; i++) { Assert.Equal(argumentRefKinds[i], operation.GetArgumentRefKind(i)); } } } public override void VisitArgument(IArgumentOperation operation) { LogString($"{nameof(IArgumentOperation)} ("); LogString($"{nameof(ArgumentKind)}.{operation.ArgumentKind}, "); LogSymbol(operation.Parameter, header: "Matching Parameter", logDisplayString: false); LogString(")"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value); Indent(); LogConversion(operation.InConversion, "InConversion"); LogNewLine(); LogConversion(operation.OutConversion, "OutConversion"); LogNewLine(); Unindent(); } public override void VisitOmittedArgument(IOmittedArgumentOperation operation) { LogString(nameof(IOmittedArgumentOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitArrayElementReference(IArrayElementReferenceOperation operation) { LogString(nameof(IArrayElementReferenceOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.ArrayReference, "Array reference"); VisitArray(operation.Indices, "Indices", logElementCount: true); } public override void VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation) { LogString(nameof(IImplicitIndexerReferenceOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Instance, "Instance"); Visit(operation.Argument, "Argument"); Indent(); LogSymbol(operation.LengthSymbol, $"{nameof(operation.LengthSymbol)}"); LogNewLine(); LogSymbol(operation.IndexerSymbol, $"{nameof(operation.IndexerSymbol)}"); LogNewLine(); Unindent(); } public override void VisitInlineArrayAccess(IInlineArrayAccessOperation operation) { LogString(nameof(IInlineArrayAccessOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Instance, "Instance"); Visit(operation.Argument, "Argument"); } internal override void VisitPointerIndirectionReference(IPointerIndirectionReferenceOperation operation) { LogString(nameof(IPointerIndirectionReferenceOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Pointer, "Pointer"); } public override void VisitLocalReference(ILocalReferenceOperation operation) { LogString(nameof(ILocalReferenceOperation)); LogString($": {operation.Local.Name}"); if (operation.IsDeclaration) { LogString($" (IsDeclaration: {operation.IsDeclaration})"); } LogCommonPropertiesAndNewLine(operation); } public override void VisitFlowCapture(IFlowCaptureOperation operation) { LogString(nameof(IFlowCaptureOperation)); LogString($": {operation.Id.Value}"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, "Value"); TestOperationVisitor.Singleton.VisitFlowCapture(operation); } public override void VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation) { LogString(nameof(IFlowCaptureReferenceOperation)); LogString($": {operation.Id.Value}"); if (operation.IsInitialization) { LogString(" (IsInitialization)"); } LogCommonPropertiesAndNewLine(operation); } public override void VisitIsNull(IIsNullOperation operation) { LogString(nameof(IIsNullOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, "Operand"); } public override void VisitCaughtException(ICaughtExceptionOperation operation) { LogString(nameof(ICaughtExceptionOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitParameterReference(IParameterReferenceOperation operation) { LogString(nameof(IParameterReferenceOperation)); LogString($": {operation.Parameter.Name}"); LogCommonPropertiesAndNewLine(operation); } public override void VisitInstanceReference(IInstanceReferenceOperation operation) { LogString(nameof(IInstanceReferenceOperation)); LogString($" (ReferenceKind: {operation.ReferenceKind})"); LogCommonPropertiesAndNewLine(operation); if (operation.IsImplicit) { if (operation.Parent is IMemberReferenceOperation memberReference && memberReference.Instance == operation) { Assert.False(memberReference.Member.IsStatic && !operation.HasErrors(this._compilation)); } else if (operation.Parent is IInvocationOperation invocation && invocation.Instance == operation) { Assert.False(invocation.TargetMethod.IsStatic); } } } private void VisitMemberReferenceExpressionCommon(IMemberReferenceOperation operation) { if (operation.Member.IsStatic) { LogString(" (Static)"); } LogCommonPropertiesAndNewLine(operation); VisitInstance(operation.Instance); } public override void VisitFieldReference(IFieldReferenceOperation operation) { LogString(nameof(IFieldReferenceOperation)); LogString($": {operation.Field.ToTestDisplayString()}"); if (operation.IsDeclaration) { LogString($" (IsDeclaration: {operation.IsDeclaration})"); } Assert.Null(operation.ConstrainedToType); VisitMemberReferenceExpressionCommon(operation); } public override void VisitMethodReference(IMethodReferenceOperation operation) { LogString(nameof(IMethodReferenceOperation)); LogString($": {operation.Method.ToTestDisplayString()}"); if (operation.ConstrainedToType is not null) { LogType(operation.ConstrainedToType, header: " (ConstrainedToType"); LogString(")"); } if (operation.IsVirtual) { LogString(" (IsVirtual)"); } Assert.Null(operation.Type); VisitMemberReferenceExpressionCommon(operation); } public override void VisitPropertyReference(IPropertyReferenceOperation operation) { LogString(nameof(IPropertyReferenceOperation)); LogString($": {operation.Property.ToTestDisplayString()}"); if (operation.ConstrainedToType is not null) { LogType(operation.ConstrainedToType, header: " (ConstrainedToType"); LogString(")"); } VisitMemberReferenceExpressionCommon(operation); if (operation.Arguments.Length > 0) { VisitArguments(operation.Arguments); } } public override void VisitEventReference(IEventReferenceOperation operation) { LogString(nameof(IEventReferenceOperation)); LogString($": {operation.Event.ToTestDisplayString()}"); if (operation.ConstrainedToType is not null) { LogType(operation.ConstrainedToType, header: " (ConstrainedToType"); LogString(")"); } VisitMemberReferenceExpressionCommon(operation); } public override void VisitEventAssignment(IEventAssignmentOperation operation) { var kindStr = operation.Adds ? "EventAdd" : "EventRemove"; LogString($"{nameof(IEventAssignmentOperation)} ({kindStr})"); LogCommonPropertiesAndNewLine(operation); Assert.NotNull(operation.EventReference); Visit(operation.EventReference, header: "Event Reference"); Visit(operation.HandlerValue, header: "Handler"); } public override void VisitConditionalAccess(IConditionalAccessOperation operation) { LogString(nameof(IConditionalAccessOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, header: nameof(operation.Operation)); Visit(operation.WhenNotNull, header: nameof(operation.WhenNotNull)); Assert.NotNull(operation.Type); } public override void VisitConditionalAccessInstance(IConditionalAccessInstanceOperation operation) { LogString(nameof(IConditionalAccessInstanceOperation)); LogCommonPropertiesAndNewLine(operation); } internal override void VisitPlaceholder(IPlaceholderOperation operation) { LogString(nameof(IPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); Assert.Equal(PlaceholderKind.AggregationGroup, operation.PlaceholderKind); } public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) { LogString(nameof(ICollectionExpressionElementsPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitUnaryOperator(IUnaryOperation operation) { LogString(nameof(IUnaryOperation)); var kindStr = $"{nameof(UnaryOperatorKind)}.{operation.OperatorKind}"; if (operation.IsLifted) { kindStr += ", IsLifted"; } if (operation.IsChecked) { kindStr += ", Checked"; } LogString($" ({kindStr})"); LogHasOperatorMethodExpressionCommon(operation.OperatorMethod, operation.ConstrainedToType); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, "Operand"); } public override void VisitBinaryOperator(IBinaryOperation operation) { LogString(nameof(IBinaryOperation)); var kindStr = $"{nameof(BinaryOperatorKind)}.{operation.OperatorKind}"; if (operation.IsLifted) { kindStr += ", IsLifted"; } if (operation.IsChecked) { kindStr += ", Checked"; } if (operation.IsCompareText) { kindStr += ", CompareText"; } LogString($" ({kindStr})"); LogHasOperatorMethodExpressionCommon(operation.OperatorMethod, operation.ConstrainedToType); var unaryOperatorMethod = ((BinaryOperation)operation).UnaryOperatorMethod; LogCommonPropertiesAndNewLine(operation); Visit(operation.LeftOperand, "Left"); Visit(operation.RightOperand, "Right"); } public override void VisitTupleBinaryOperator(ITupleBinaryOperation operation) { LogString(nameof(ITupleBinaryOperation)); var kindStr = $"{nameof(BinaryOperatorKind)}.{operation.OperatorKind}"; LogString($" ({kindStr})"); LogCommonPropertiesAndNewLine(operation); Visit(operation.LeftOperand, "Left"); Visit(operation.RightOperand, "Right"); } private void LogHasOperatorMethodExpressionCommon(IMethodSymbol operatorMethodOpt, ITypeSymbol constrainedToTypeOpt) { if (operatorMethodOpt != null) { LogSymbol(operatorMethodOpt, header: " (OperatorMethod"); if (constrainedToTypeOpt is not null) { LogType(constrainedToTypeOpt, header: " ConstrainedToType"); } LogString(")"); } else { Assert.Null(constrainedToTypeOpt); } } public override void VisitConversion(IConversionOperation operation) { LogString(nameof(IConversionOperation)); var isTryCast = $"TryCast: {(operation.IsTryCast ? "True" : "False")}"; var isChecked = operation.IsChecked ? "Checked" : "Unchecked"; LogString($" ({isTryCast}, {isChecked})"); LogHasOperatorMethodExpressionCommon(operation.OperatorMethod, operation.ConstrainedToType); LogCommonPropertiesAndNewLine(operation); Indent(); LogConversion(operation.Conversion); if (((Operation)operation).OwningSemanticModel == null) { LogNewLine(); Indent(); LogString($"({((ConversionOperation)operation).ConversionConvertible})"); Unindent(); } Unindent(); LogNewLine(); Visit(operation.Operand, "Operand"); } public override void VisitConditional(IConditionalOperation operation) { LogString(nameof(IConditionalOperation)); if (operation.IsRef) { LogString(" (IsRef)"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.Condition, "Condition"); Visit(operation.WhenTrue, "WhenTrue"); Visit(operation.WhenFalse, "WhenFalse"); } public override void VisitCoalesce(ICoalesceOperation operation) { LogString(nameof(ICoalesceOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, "Expression"); Indent(); LogConversion(operation.ValueConversion, "ValueConversion"); LogNewLine(); Indent(); LogString($"({((CoalesceOperation)operation).ValueConversionConvertible})"); Unindent(); LogNewLine(); Unindent(); Visit(operation.WhenNull, "WhenNull"); } public override void VisitCoalesceAssignment(ICoalesceAssignmentOperation operation) { LogString(nameof(ICoalesceAssignmentOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, nameof(operation.Target)); Visit(operation.Value, nameof(operation.Value)); } public override void VisitIsType(IIsTypeOperation operation) { LogString(nameof(IIsTypeOperation)); if (operation.IsNegated) { LogString(" (IsNotExpression)"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.ValueOperand, "Operand"); Indent(); LogType(operation.TypeOperand, "IsType"); LogNewLine(); Unindent(); } public override void VisitSizeOf(ISizeOfOperation operation) { LogString(nameof(ISizeOfOperation)); LogCommonPropertiesAndNewLine(operation); Indent(); LogType(operation.TypeOperand, "TypeOperand"); LogNewLine(); Unindent(); } public override void VisitTypeOf(ITypeOfOperation operation) { LogString(nameof(ITypeOfOperation)); LogCommonPropertiesAndNewLine(operation); Indent(); LogType(operation.TypeOperand, "TypeOperand"); LogNewLine(); Unindent(); } public override void VisitAnonymousFunction(IAnonymousFunctionOperation operation) { LogString(nameof(IAnonymousFunctionOperation)); // For C# this prints "lambda expression", which is not very helpful if we want to tell lambdas apart. // That is how symbol display is implemented for C#. // https://github.com/dotnet/roslyn/issues/22559#issuecomment-393667316 tracks improving the output. LogSymbol(operation.Symbol, header: " (Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); base.VisitAnonymousFunction(operation); } public override void VisitFlowAnonymousFunction(IFlowAnonymousFunctionOperation operation) { LogString(nameof(IFlowAnonymousFunctionOperation)); // For C# this prints "lambda expression", which is not very helpful if we want to tell lambdas apart. // That is how symbol display is implemented for C#. // https://github.com/dotnet/roslyn/issues/22559#issuecomment-393667316 tracks improving the output. LogSymbol(operation.Symbol, header: " (Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); base.VisitFlowAnonymousFunction(operation); } public override void VisitDelegateCreation(IDelegateCreationOperation operation) { LogString(nameof(IDelegateCreationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, nameof(operation.Target)); } public override void VisitLiteral(ILiteralOperation operation) { LogString(nameof(ILiteralOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitUtf8String(IUtf8StringOperation operation) { LogString(nameof(IUtf8StringOperation)); LogString(" ("); LogString(operation.Value); LogString(")"); LogCommonPropertiesAndNewLine(operation); } public override void VisitAwait(IAwaitOperation operation) { LogString(nameof(IAwaitOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Expression"); } public override void VisitNameOf(INameOfOperation operation) { LogString(nameof(INameOfOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Argument); } public override void VisitThrow(IThrowOperation operation) { LogString(nameof(IThrowOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Exception); } public override void VisitAddressOf(IAddressOfOperation operation) { LogString(nameof(IAddressOfOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Reference, "Reference"); } public override void VisitObjectCreation(IObjectCreationOperation operation) { LogString(nameof(IObjectCreationOperation)); LogString($" (Constructor: {operation.Constructor?.ToTestDisplayString() ?? "<null>"})"); LogCommonPropertiesAndNewLine(operation); VisitArguments(operation.Arguments); Visit(operation.Initializer, "Initializer"); } public override void VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation) { LogString(nameof(IAnonymousObjectCreationOperation)); LogCommonPropertiesAndNewLine(operation); 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); } VisitArray(operation.Initializers, "Initializers", logElementCount: true); } public override void VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation) { LogString(nameof(IDynamicObjectCreationOperation)); LogCommonPropertiesAndNewLine(operation); VisitDynamicArguments((HasDynamicArgumentsExpression)operation); Visit(operation.Initializer, "Initializer"); } public override void VisitDynamicInvocation(IDynamicInvocationOperation operation) { LogString(nameof(IDynamicInvocationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Expression"); VisitDynamicArguments((HasDynamicArgumentsExpression)operation); } public override void VisitDynamicIndexerAccess(IDynamicIndexerAccessOperation operation) { LogString(nameof(IDynamicIndexerAccessOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Expression"); VisitDynamicArguments((HasDynamicArgumentsExpression)operation); } public override void VisitObjectOrCollectionInitializer(IObjectOrCollectionInitializerOperation operation) { LogString(nameof(IObjectOrCollectionInitializerOperation)); LogCommonPropertiesAndNewLine(operation); VisitArray(operation.Initializers, "Initializers", logElementCount: true); } public override void VisitMemberInitializer(IMemberInitializerOperation operation) { LogString(nameof(IMemberInitializerOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.InitializedMember, "InitializedMember"); Visit(operation.Initializer, "Initializer"); } [Obsolete("ICollectionElementInitializerOperation has been replaced with IInvocationOperation and IDynamicInvocationOperation", error: true)] public override void VisitCollectionElementInitializer(ICollectionElementInitializerOperation operation) { // Kept to ensure that it's never called, as we can't override DefaultVisit in this visitor throw ExceptionUtilities.Unreachable(); } public override void VisitFieldInitializer(IFieldInitializerOperation operation) { LogString(nameof(IFieldInitializerOperation)); if (operation.InitializedFields.Length <= 1) { if (operation.InitializedFields.Length == 1) { LogSymbol(operation.InitializedFields[0], header: " (Field"); LogString(")"); } LogCommonPropertiesAndNewLine(operation); } else { LogString($" ({operation.InitializedFields.Length} initialized fields)"); LogCommonPropertiesAndNewLine(operation); Indent(); int index = 1; foreach (var local in operation.InitializedFields) { LogSymbol(local, header: $"Field_{index++}"); LogNewLine(); } Unindent(); } LogLocals(operation.Locals); base.VisitFieldInitializer(operation); } public override void VisitVariableInitializer(IVariableInitializerOperation operation) { LogString(nameof(IVariableInitializerOperation)); LogCommonPropertiesAndNewLine(operation); Assert.Empty(operation.Locals); base.VisitVariableInitializer(operation); } public override void VisitPropertyInitializer(IPropertyInitializerOperation operation) { LogString(nameof(IPropertyInitializerOperation)); if (operation.InitializedProperties.Length <= 1) { if (operation.InitializedProperties.Length == 1) { LogSymbol(operation.InitializedProperties[0], header: " (Property"); LogString(")"); } LogCommonPropertiesAndNewLine(operation); } else { LogString($" ({operation.InitializedProperties.Length} initialized properties)"); LogCommonPropertiesAndNewLine(operation); Indent(); int index = 1; foreach (var property in operation.InitializedProperties) { LogSymbol(property, header: $"Property_{index++}"); LogNewLine(); } Unindent(); } LogLocals(operation.Locals); base.VisitPropertyInitializer(operation); } public override void VisitParameterInitializer(IParameterInitializerOperation operation) { LogString(nameof(IParameterInitializerOperation)); LogSymbol(operation.Parameter, header: " (Parameter"); LogString(")"); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); base.VisitParameterInitializer(operation); } public override void VisitArrayCreation(IArrayCreationOperation operation) { LogString(nameof(IArrayCreationOperation)); LogCommonPropertiesAndNewLine(operation); VisitArray(operation.DimensionSizes, "Dimension Sizes", logElementCount: true); Visit(operation.Initializer, "Initializer"); } public override void VisitArrayInitializer(IArrayInitializerOperation operation) { LogString(nameof(IArrayInitializerOperation)); LogString($" ({operation.ElementValues.Length} elements)"); LogCommonPropertiesAndNewLine(operation); Assert.Null(operation.Type); VisitArray(operation.ElementValues, "Element Values", logElementCount: true); } public override void VisitCollectionExpression(ICollectionExpressionOperation operation) { LogString(nameof(ICollectionExpressionOperation)); LogString($" ({operation.Elements.Length} elements"); LogSymbol(operation.ConstructMethod, $", {nameof(operation.ConstructMethod)}"); LogString(")"); LogCommonPropertiesAndNewLine(operation); if (operation.ConstructArguments.Length > 0) VisitArray(operation.ConstructArguments, nameof(operation.ConstructArguments), logElementCount: true); VisitArray(operation.Elements, nameof(operation.Elements), logElementCount: true); } public override void VisitSpread(ISpreadOperation operation) { LogString(nameof(ISpreadOperation)); LogSymbol(operation.ElementType, $" ({nameof(operation.ElementType)}"); LogString(")"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, nameof(operation.Operand)); Indent(); LogConversion(operation.ElementConversion, nameof(operation.ElementConversion)); LogNewLine(); Indent(); LogString($"({((SpreadOperation)operation).ElementConversionConvertible})"); Unindent(); LogNewLine(); Unindent(); } public override void VisitSimpleAssignment(ISimpleAssignmentOperation operation) { LogString(nameof(ISimpleAssignmentOperation)); if (operation.IsRef) { LogString(" (IsRef)"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, "Left"); Visit(operation.Value, "Right"); } public override void VisitDeconstructionAssignment(IDeconstructionAssignmentOperation operation) { LogString(nameof(IDeconstructionAssignmentOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, "Left"); Visit(operation.Value, "Right"); } public override void VisitDeclarationExpression(IDeclarationExpressionOperation operation) { LogString(nameof(IDeclarationExpressionOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Expression); } public override void VisitCompoundAssignment(ICompoundAssignmentOperation operation) { LogString(nameof(ICompoundAssignmentOperation)); var kindStr = $"{nameof(BinaryOperatorKind)}.{operation.OperatorKind}"; if (operation.IsLifted) { kindStr += ", IsLifted"; } if (operation.IsChecked) { kindStr += ", Checked"; } LogString($" ({kindStr})"); LogHasOperatorMethodExpressionCommon(operation.OperatorMethod, operation.ConstrainedToType); LogCommonPropertiesAndNewLine(operation); Indent(); LogConversion(operation.InConversion, "InConversion"); LogNewLine(); LogConversion(operation.OutConversion, "OutConversion"); LogNewLine(); Unindent(); Visit(operation.Target, "Left"); Visit(operation.Value, "Right"); } public override void VisitIncrementOrDecrement(IIncrementOrDecrementOperation operation) { LogString(nameof(IIncrementOrDecrementOperation)); var kindStr = operation.IsPostfix ? "Postfix" : "Prefix"; if (operation.IsLifted) { kindStr += ", IsLifted"; } if (operation.IsChecked) { kindStr += ", Checked"; } LogString($" ({kindStr})"); LogHasOperatorMethodExpressionCommon(operation.OperatorMethod, operation.ConstrainedToType); LogCommonPropertiesAndNewLine(operation); Visit(operation.Target, "Target"); } public override void VisitParenthesized(IParenthesizedOperation operation) { LogString(nameof(IParenthesizedOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, "Operand"); } public override void VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation) { LogString(nameof(IDynamicMemberReferenceOperation)); // (Member Name: "quoted name", Containing Type: type) LogString(" ("); LogConstant((object)operation.MemberName, "Member Name"); LogString(", "); LogType(operation.ContainingType, "Containing Type"); LogString(")"); LogCommonPropertiesAndNewLine(operation); VisitArrayCommon(operation.TypeArguments, "Type Arguments", logElementCount: true, logNullForDefault: false, arrayElementVisitor: VisitSymbolArrayElement); VisitInstance(operation.Instance); } public override void VisitDefaultValue(IDefaultValueOperation operation) { LogString(nameof(IDefaultValueOperation)); LogCommonPropertiesAndNewLine(operation); } public override void VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation) { LogString(nameof(ITypeParameterObjectCreationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Initializer, "Initializer"); } internal override void VisitNoPiaObjectCreation(INoPiaObjectCreationOperation operation) { LogString(nameof(INoPiaObjectCreationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Initializer, "Initializer"); } public override void VisitInvalid(IInvalidOperation operation) { LogString(nameof(IInvalidOperation)); LogCommonPropertiesAndNewLine(operation); VisitChildren(operation); } public override void VisitLocalFunction(ILocalFunctionOperation operation) { LogString(nameof(ILocalFunctionOperation)); LogSymbol(operation.Symbol, header: " (Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); if (operation.Body != null) { if (operation.IgnoredBody != null) { Visit(operation.Body, "Body"); Visit(operation.IgnoredBody, "IgnoredBody"); } else { Visit(operation.Body); } } else { Assert.Null(operation.IgnoredBody); } } private void LogCaseClauseCommon(ICaseClauseOperation operation) { Assert.Equal(OperationKind.CaseClause, operation.Kind); if (operation.Label != null) { LogString($" (Label Id: {GetLabelId(operation.Label)})"); } var kindStr = $"{nameof(CaseKind)}.{operation.CaseKind}"; LogString($" ({kindStr})"); LogCommonPropertiesAndNewLine(operation); } public override void VisitSingleValueCaseClause(ISingleValueCaseClauseOperation operation) { LogString(nameof(ISingleValueCaseClauseOperation)); LogCaseClauseCommon(operation); Visit(operation.Value, "Value"); } public override void VisitRelationalCaseClause(IRelationalCaseClauseOperation operation) { LogString(nameof(IRelationalCaseClauseOperation)); var kindStr = $"{nameof(BinaryOperatorKind)}.{operation.Relation}"; LogString($" (Relational operator kind: {kindStr})"); LogCaseClauseCommon(operation); Visit(operation.Value, "Value"); } public override void VisitRangeCaseClause(IRangeCaseClauseOperation operation) { LogString(nameof(IRangeCaseClauseOperation)); LogCaseClauseCommon(operation); Visit(operation.MinimumValue, "Min"); Visit(operation.MaximumValue, "Max"); } public override void VisitDefaultCaseClause(IDefaultCaseClauseOperation operation) { LogString(nameof(IDefaultCaseClauseOperation)); LogCaseClauseCommon(operation); } public override void VisitTuple(ITupleOperation operation) { LogString(nameof(ITupleOperation)); LogCommonPropertiesAndNewLine(operation); Indent(); LogType(operation.NaturalType, nameof(operation.NaturalType)); LogNewLine(); Unindent(); VisitArray(operation.Elements, "Elements", logElementCount: true); } public override void VisitInterpolatedString(IInterpolatedStringOperation operation) { LogString(nameof(IInterpolatedStringOperation)); LogCommonPropertiesAndNewLine(operation); VisitArray(operation.Parts, "Parts", logElementCount: true); } public override void VisitInterpolatedStringHandlerCreation(IInterpolatedStringHandlerCreationOperation operation) { LogString(nameof(IInterpolatedStringHandlerCreationOperation)); LogString($" (HandlerAppendCallsReturnBool: {operation.HandlerAppendCallsReturnBool}, HandlerCreationHasSuccessParameter: {operation.HandlerCreationHasSuccessParameter})"); LogCommonPropertiesAndNewLine(operation); Visit(operation.HandlerCreation, "Creation"); Visit(operation.Content, "Content"); } public override void VisitInterpolatedStringAddition(IInterpolatedStringAdditionOperation operation) { LogString(nameof(IInterpolatedStringAdditionOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Left, "Left"); Visit(operation.Right, "Right"); } public override void VisitInterpolatedStringText(IInterpolatedStringTextOperation operation) { LogString(nameof(IInterpolatedStringTextOperation)); LogCommonPropertiesAndNewLine(operation); if (operation.Text.Kind != OperationKind.Literal) { Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.Text).Operand.Kind); } Visit(operation.Text, "Text"); } public override void VisitInterpolation(IInterpolationOperation operation) { LogString(nameof(IInterpolationOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Expression, "Expression"); Visit(operation.Alignment, "Alignment"); Visit(operation.FormatString, "FormatString"); if (operation.FormatString != null && operation.FormatString.Kind != OperationKind.Literal) { Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.FormatString).Operand.Kind); } } public override void VisitInterpolatedStringAppend(IInterpolatedStringAppendOperation operation) { LogString(nameof(IInterpolatedStringAppendOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.AppendCall, "AppendCall"); } public override void VisitInterpolatedStringHandlerArgumentPlaceholder(IInterpolatedStringHandlerArgumentPlaceholderOperation operation) { LogString(nameof(IInterpolatedStringHandlerArgumentPlaceholderOperation)); if (operation.PlaceholderKind is InterpolatedStringArgumentPlaceholderKind.CallsiteArgument) { LogString($" (ArgumentIndex: {operation.ArgumentIndex})"); } else { LogString($" ({operation.PlaceholderKind})"); } LogCommonPropertiesAndNewLine(operation); } public override void VisitConstantPattern(IConstantPatternOperation operation) { LogString(nameof(IConstantPatternOperation)); LogPatternPropertiesAndNewLine(operation); Visit(operation.Value, "Value"); } public override void VisitRelationalPattern(IRelationalPatternOperation operation) { LogString(nameof(IRelationalPatternOperation)); LogString($" ({nameof(BinaryOperatorKind)}.{operation.OperatorKind})"); LogPatternPropertiesAndNewLine(operation); Visit(operation.Value, "Value"); } public override void VisitNegatedPattern(INegatedPatternOperation operation) { LogString(nameof(INegatedPatternOperation)); LogPatternPropertiesAndNewLine(operation); Visit(operation.Pattern, "Pattern"); } public override void VisitBinaryPattern(IBinaryPatternOperation operation) { LogString(nameof(IBinaryPatternOperation)); LogString($" ({nameof(BinaryOperatorKind)}.{operation.OperatorKind})"); LogPatternPropertiesAndNewLine(operation); Visit(operation.LeftPattern, "LeftPattern"); Visit(operation.RightPattern, "RightPattern"); } public override void VisitTypePattern(ITypePatternOperation operation) { LogString(nameof(ITypePatternOperation)); LogPatternProperties(operation); LogSymbol(operation.MatchedType, $", {nameof(operation.MatchedType)}"); LogString(")"); LogNewLine(); } public override void VisitDeclarationPattern(IDeclarationPatternOperation operation) { LogString(nameof(IDeclarationPatternOperation)); LogPatternProperties(operation); LogSymbol(operation.DeclaredSymbol, $", {nameof(operation.DeclaredSymbol)}"); LogConstant((object)operation.MatchesNull, $", {nameof(operation.MatchesNull)}"); LogString(")"); LogNewLine(); } public override void VisitSlicePattern(ISlicePatternOperation operation) { LogString(nameof(ISlicePatternOperation)); LogPatternProperties(operation); LogSymbol(operation.SliceSymbol, $", {nameof(operation.SliceSymbol)}"); LogNewLine(); Visit(operation.Pattern, $"{nameof(operation.Pattern)}"); } public override void VisitListPattern(IListPatternOperation operation) { LogString(nameof(IListPatternOperation)); LogPatternProperties(operation); LogSymbol(operation.DeclaredSymbol, $", {nameof(operation.DeclaredSymbol)}"); LogSymbol(operation.LengthSymbol, $", {nameof(operation.LengthSymbol)}"); LogSymbol(operation.IndexerSymbol, $", {nameof(operation.IndexerSymbol)}"); LogString(")"); LogNewLine(); VisitArray(operation.Patterns, $"{nameof(operation.Patterns)} ", true, true); } public override void VisitRecursivePattern(IRecursivePatternOperation operation) { LogString(nameof(IRecursivePatternOperation)); LogPatternProperties(operation); LogSymbol(operation.DeclaredSymbol, $", {nameof(operation.DeclaredSymbol)}"); LogType(operation.MatchedType, $", {nameof(operation.MatchedType)}"); LogSymbol(operation.DeconstructSymbol, $", {nameof(operation.DeconstructSymbol)}"); LogString(")"); LogNewLine(); VisitArray(operation.DeconstructionSubpatterns, $"{nameof(operation.DeconstructionSubpatterns)} ", true, true); VisitArray(operation.PropertySubpatterns, $"{nameof(operation.PropertySubpatterns)} ", true, true); } public override void VisitPropertySubpattern(IPropertySubpatternOperation operation) { LogString(nameof(IPropertySubpatternOperation)); LogCommonProperties(operation); LogNewLine(); Visit(operation.Member, $"{nameof(operation.Member)}"); Visit(operation.Pattern, $"{nameof(operation.Pattern)}"); } public override void VisitIsPattern(IIsPatternOperation operation) { LogString(nameof(IIsPatternOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, $"{nameof(operation.Value)}"); Visit(operation.Pattern, "Pattern"); } public override void VisitPatternCaseClause(IPatternCaseClauseOperation operation) { LogString(nameof(IPatternCaseClauseOperation)); LogCaseClauseCommon(operation); Assert.Same(((ICaseClauseOperation)operation).Label, operation.Label); Visit(operation.Pattern, "Pattern"); if (operation.Guard != null) Visit(operation.Guard, nameof(operation.Guard)); } public override void VisitTranslatedQuery(ITranslatedQueryOperation operation) { LogString(nameof(ITranslatedQueryOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation, "Expression"); } public override void VisitRaiseEvent(IRaiseEventOperation operation) { LogString(nameof(IRaiseEventOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.EventReference, header: "Event Reference"); VisitArguments(operation.Arguments); } public override void VisitConstructorBodyOperation(IConstructorBodyOperation operation) { LogString(nameof(IConstructorBodyOperation)); LogCommonPropertiesAndNewLine(operation); LogLocals(operation.Locals); Visit(operation.Initializer, "Initializer"); Visit(operation.BlockBody, "BlockBody"); Visit(operation.ExpressionBody, "ExpressionBody"); } public override void VisitMethodBodyOperation(IMethodBodyOperation operation) { LogString(nameof(IMethodBodyOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.BlockBody, "BlockBody"); Visit(operation.ExpressionBody, "ExpressionBody"); } public override void VisitDiscardOperation(IDiscardOperation operation) { LogString(nameof(IDiscardOperation)); LogString(" ("); LogSymbol(operation.DiscardSymbol, "Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); } public override void VisitDiscardPattern(IDiscardPatternOperation operation) { LogString(nameof(IDiscardPatternOperation)); LogPatternPropertiesAndNewLine(operation); } public override void VisitSwitchExpression(ISwitchExpressionOperation operation) { LogString($"{nameof(ISwitchExpressionOperation)} ({operation.Arms.Length} arms, IsExhaustive: {operation.IsExhaustive})"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Value, nameof(operation.Value)); VisitArray(operation.Arms, nameof(operation.Arms), logElementCount: true); } public override void VisitSwitchExpressionArm(ISwitchExpressionArmOperation operation) { LogString($"{nameof(ISwitchExpressionArmOperation)} ({operation.Locals.Length} locals)"); LogCommonPropertiesAndNewLine(operation); Visit(operation.Pattern, nameof(operation.Pattern)); if (operation.Guard != null) Visit(operation.Guard, nameof(operation.Guard)); Visit(operation.Value, nameof(operation.Value)); LogLocals(operation.Locals); } public override void VisitStaticLocalInitializationSemaphore(IStaticLocalInitializationSemaphoreOperation operation) { LogString(nameof(IStaticLocalInitializationSemaphoreOperation)); LogSymbol(operation.Local, " (Local Symbol"); LogString(")"); LogCommonPropertiesAndNewLine(operation); } public override void VisitRangeOperation(IRangeOperation operation) { LogString(nameof(IRangeOperation)); if (operation.IsLifted) { LogString(" (IsLifted)"); } LogCommonPropertiesAndNewLine(operation); Visit(operation.LeftOperand, nameof(operation.LeftOperand)); Visit(operation.RightOperand, nameof(operation.RightOperand)); } public override void VisitReDim(IReDimOperation operation) { LogString(nameof(IReDimOperation)); if (operation.Preserve) { LogString(" (Preserve)"); } LogCommonPropertiesAndNewLine(operation); VisitArray(operation.Clauses, "Clauses", logElementCount: true); } public override void VisitReDimClause(IReDimClauseOperation operation) { LogString(nameof(IReDimClauseOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, "Operand"); VisitArray(operation.DimensionSizes, "DimensionSizes", logElementCount: true); } public override void VisitWith(IWithOperation operation) { LogString(nameof(IWithOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operand, "Operand"); Indent(); LogSymbol(operation.CloneMethod, nameof(operation.CloneMethod)); LogNewLine(); Unindent(); Visit(operation.Initializer, "Initializer"); } public override void VisitAttribute(IAttributeOperation operation) { LogString(nameof(IAttributeOperation)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Operation); } #endregion } }