diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.Arguments.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.Arguments.cs new file mode 100644 index 00000000..5e990db1 --- /dev/null +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.Arguments.cs @@ -0,0 +1,402 @@ +using System.Collections.Immutable; +//using System.Data; // Not directly used by these methods +using System.Globalization; +//using System.Linq.Expressions; // Not directly used +//using System.Runtime.CompilerServices; // Not directly used +//using System.Xml.Linq; // Not used +//using ICSharpCode.CodeConverter.CSharp.Replacements; // Not directly used +using ICSharpCode.CodeConverter.Util.FromRoslyn; // For .Yield() +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; // For IPropertyReferenceOperation in IsRefArrayAcces +//using Microsoft.CodeAnalysis.Simplification; // Not directly used +using Microsoft.VisualBasic; // For Strings in RequiresStringCompareMethodToBeAppended +//using Microsoft.VisualBasic.CompilerServices; // Not directly used +//using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; // Not used +using System.Threading.Tasks; +using System; +using System.Linq; +using System.Collections.Generic; +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; + +namespace ICSharpCode.CodeConverter.CSharp; + +internal partial class ExpressionNodeVisitor // Must be partial +{ + public override async Task VisitArgumentList(VBasic.Syntax.ArgumentListSyntax node) + { + if (node.Parent.IsKind(VBasic.SyntaxKind.Attribute)) { + return CommonConversions.CreateAttributeArgumentList(await node.Arguments.SelectAsync(ToAttributeArgumentAsync)); + } + var argumentSyntaxes = await ConvertArgumentsAsync(node); + return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(argumentSyntaxes)); + } + + public override async Task VisitSimpleArgument(VBasic.Syntax.SimpleArgumentSyntax node) + { + var argList = (VBasic.Syntax.ArgumentListSyntax)node.Parent; + var invocation = argList.Parent; + if (invocation is VBasic.Syntax.ArrayCreationExpressionSyntax) + return await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + var symbol = GetInvocationSymbol(invocation); + SyntaxToken token = default(SyntaxToken); + var convertedArgExpression = (await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)).SkipIntoParens(); + var typeConversionAnalyzer = CommonConversions.TypeConversionAnalyzer; + var baseSymbol = symbol?.OriginalDefinition.GetBaseSymbol(); + var possibleParameters = (CommonConversions.GetCsOriginalSymbolOrNull(baseSymbol) ?? symbol)?.GetParameters(); + if (possibleParameters.HasValue) { + var refType = GetRefConversionType(node, argList, possibleParameters.Value, out var argName, out var refKind); + token = GetRefToken(refKind); + if (refType != RefConversion.Inline) { + convertedArgExpression = HoistByRefDeclaration(node, convertedArgExpression, refType, argName, refKind); + } else { + convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression, defaultToCast: refKind != RefKind.None); + } + } else { + convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression); + } + + var nameColon = node.IsNamed ? SyntaxFactory.NameColon(await node.NameColonEquals.Name.AcceptAsync(TriviaConvertingExpressionVisitor)) : null; + return SyntaxFactory.Argument(nameColon, token, convertedArgExpression); + } + + private ExpressionSyntax HoistByRefDeclaration(VBSyntax.SimpleArgumentSyntax node, ExpressionSyntax refLValue, RefConversion refType, string argName, RefKind refKind) + { + string prefix = $"arg{argName}"; + var expressionTypeInfo = _semanticModel.GetTypeInfo(node.Expression); + bool useVar = expressionTypeInfo.Type?.Equals(expressionTypeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability) == true && !CommonConversions.ShouldPreferExplicitType(node.Expression, expressionTypeInfo.ConvertedType, out var _); + var typeSyntax = CommonConversions.GetTypeSyntax(expressionTypeInfo.ConvertedType, useVar); + + if (refLValue is ElementAccessExpressionSyntax eae) { + var tmpContainer = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration("tmp", eae.Expression, ValidSyntaxFactory.VarType)); + refLValue = eae.WithExpression(tmpContainer.IdentifierName); + } + + var withCast = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, refLValue, defaultToCast: refKind != RefKind.None); + var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, withCast, typeSyntax)); + + if (refType == RefConversion.PreAndPostAssignment) { + var convertedLocalIdentifier = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, local.IdentifierName, forceSourceType: expressionTypeInfo.ConvertedType, forceTargetType: expressionTypeInfo.Type); + _typeContext.PerScopeState.Hoist(new AdditionalAssignment(refLValue, convertedLocalIdentifier)); + } + return local.IdentifierName; + } + + private static SyntaxToken GetRefToken(RefKind refKind) + { + SyntaxToken token; + switch (refKind) { + case RefKind.None: token = default(SyntaxToken); break; + case RefKind.Ref: token = SyntaxFactory.Token(SyntaxKind.RefKeyword); break; + case RefKind.Out: token = SyntaxFactory.Token(SyntaxKind.OutKeyword); break; + default: throw new ArgumentOutOfRangeException(nameof(refKind), refKind, null); + } + return token; + } + + private RefConversion GetRefConversionType(VBSyntax.ArgumentSyntax node, VBSyntax.ArgumentListSyntax argList, ImmutableArray parameters, out string argName, out RefKind refKind) + { + var parameter = node.IsNamed && node is VBSyntax.SimpleArgumentSyntax sas + ? parameters.FirstOrDefault(p => p.Name.Equals(sas.NameColonEquals.Name.Identifier.Text, StringComparison.OrdinalIgnoreCase)) + : parameters.ElementAtOrDefault(argList.Arguments.IndexOf(node)); + if (parameter != null) { + refKind = parameter.RefKind; + argName = parameter.Name; + } else { + refKind = RefKind.None; + argName = null; + } + return NeedsVariableForArgument(node, refKind); + } + + private async Task> ConvertArgumentsAsync(VBasic.Syntax.ArgumentListSyntax node) + { + ISymbol invocationSymbol = GetInvocationSymbol(node.Parent); + var forceNamedParameters = false; + var invocationHasOverloads = invocationSymbol.HasOverloads(); + + var processedParameters = new HashSet(StringComparer.OrdinalIgnoreCase); + var argumentSyntaxs = (await node.Arguments.SelectAsync(ConvertArg)).Where(a => a != null); + return argumentSyntaxs.Concat(GetAdditionalRequiredArgs(node.Arguments, processedParameters, invocationSymbol, invocationHasOverloads)); + + async Task ConvertArg(VBSyntax.ArgumentSyntax arg, int argIndex) + { + var argName = arg is VBSyntax.SimpleArgumentSyntax { IsNamed: true } namedArg ? namedArg.NameColonEquals.Name.Identifier.Text : null; + var parameterSymbol = invocationSymbol?.GetParameters().GetArgument(argName, argIndex); + var convertedArg = await ConvertArgForParameter(arg, parameterSymbol); + + if (convertedArg is not null && parameterSymbol is not null) { + processedParameters.Add(parameterSymbol.Name); + } + return convertedArg; + } + + async Task ConvertArgForParameter(VBSyntax.ArgumentSyntax arg, IParameterSymbol parameterSymbol) + { + if (arg.IsOmitted) { + if (invocationSymbol != null && !invocationHasOverloads) { + forceNamedParameters = true; + return null; + } + return ConvertOmittedArgument(parameterSymbol); + } + + var argSyntax = await arg.AcceptAsync(TriviaConvertingExpressionVisitor); + if (forceNamedParameters && !arg.IsNamed && parameterSymbol != null) { + return argSyntax.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(CommonConversions.CsEscapedIdentifier(parameterSymbol.Name)))); + } + return argSyntax; + } + + ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter) + { + if (parameter == null) { + return SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression)); + } + var csRefKind = CommonConversions.GetCsRefKind(parameter); + return csRefKind != RefKind.None + ? CreateOptionalRefArg(parameter, csRefKind) + : SyntaxFactory.Argument(CommonConversions.Literal(parameter.ExplicitDefaultValue)); + } + } + + private IEnumerable GetAdditionalRequiredArgs( + IEnumerable arguments, + ICollection processedParametersNames, + ISymbol invocationSymbol, + bool invocationHasOverloads) + { + if (invocationSymbol is null) { + yield break; + } + + var invocationHasOmittedArgs = arguments.Any(t => t.IsOmitted); + var expandOptionalArgs = invocationHasOmittedArgs && invocationHasOverloads; + var missingArgs = invocationSymbol.GetParameters().Where(t => processedParametersNames is null || !processedParametersNames.Contains(t.Name)); + var requiresCompareMethod = _visualBasicEqualityComparison.OptionCompareTextCaseInsensitive && RequiresStringCompareMethodToBeAppended(invocationSymbol); + + foreach (var parameterSymbol in missingArgs) { + var extraArg = CreateExtraArgOrNull(parameterSymbol, requiresCompareMethod, expandOptionalArgs); + if (extraArg != null) { + yield return extraArg; + } + } + } + + private static bool RequiresStringCompareMethodToBeAppended(ISymbol symbol) => + symbol?.ContainingType.Name == nameof(Strings) && + symbol.ContainingType.ContainingNamespace.Name == nameof(Microsoft.VisualBasic) && + symbol.ContainingType.ContainingNamespace.ContainingNamespace.Name == nameof(Microsoft) && + symbol.Name is "InStr" or "InStrRev" or "Replace" or "Split" or "StrComp"; + + private ArgumentSyntax CreateExtraArgOrNull(IParameterSymbol p, bool requiresCompareMethod, bool expandOptionalArgs) + { + var csRefKind = CommonConversions.GetCsRefKind(p); + if (csRefKind != RefKind.None) { + return CreateOptionalRefArg(p, csRefKind); + } + + if (requiresCompareMethod && p.Type.GetFullMetadataName() == "Microsoft.VisualBasic.CompareMethod") { + return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, _visualBasicEqualityComparison.CompareMethodExpression); + } + + if (expandOptionalArgs && p.HasExplicitDefaultValue) { + return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, CommonConversions.Literal(p.ExplicitDefaultValue)); + } + return null; + } + + private ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind refKind) + { + string prefix = $"arg{p.Name}"; + var type = CommonConversions.GetTypeSyntax(p.Type); + ExpressionSyntax initializer; + if (p.HasExplicitDefaultValue) { + initializer = CommonConversions.Literal(p.ExplicitDefaultValue); + } else if (HasOptionalAttribute(p)) { + if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)){ + initializer = CommonConversions.Literal(defaultValue); + } else { + initializer = SyntaxFactory.DefaultExpression(type); + } + } else { + return null; // Should not happen in valid VB code with ByRef optional + } + var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, initializer, type)); + return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, refKind, local.IdentifierName); + + bool HasOptionalAttribute(IParameterSymbol param) + { + var optionalAttribute = CommonConversions.KnownTypes.OptionalAttribute; + if (optionalAttribute == null) return false; + return param.GetAttributes().Any(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, optionalAttribute)); + } + + bool TryGetDefaultParameterValueAttributeValue(IParameterSymbol param, out object defaultValue) + { + defaultValue = null; + var defaultParameterValueAttribute = CommonConversions.KnownTypes.DefaultParameterValueAttribute; + if (defaultParameterValueAttribute == null) return false; + var attributeData = param.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, defaultParameterValueAttribute)); + if (attributeData == null || attributeData.ConstructorArguments.Length == 0) return false; + defaultValue = attributeData.ConstructorArguments.First().Value; + return true; + } + } + + private RefConversion NeedsVariableForArgument(VBasic.Syntax.ArgumentSyntax node, RefKind refKind) + { + if (refKind == RefKind.None) return RefConversion.Inline; + if (!(node is VBSyntax.SimpleArgumentSyntax sas) || sas is { Expression: VBSyntax.ParenthesizedExpressionSyntax }) return RefConversion.PreAssigment; + var expression = sas.Expression; + + return GetRefConversion(expression); + + RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expr) + { + var symbolInfo = GetSymbolInfoInDocument(expr); + if (symbolInfo is IPropertySymbol { ReturnsByRef: false, ReturnsByRefReadonly: false } propertySymbol) { + return propertySymbol.IsReadOnly ? RefConversion.PreAssigment : RefConversion.PreAndPostAssignment; + } + else if (symbolInfo is IFieldSymbol { IsConst: true } or ILocalSymbol { IsConst: true }) { + return RefConversion.PreAssigment; + } else if (symbolInfo is IMethodSymbol { ReturnsByRef: false, ReturnsByRefReadonly: false }) { + return RefConversion.PreAssigment; + } + if (DeclaredInUsing(symbolInfo)) return RefConversion.PreAssigment; + if (expr is VBasic.Syntax.IdentifierNameSyntax || expr is VBSyntax.MemberAccessExpressionSyntax || IsRefArrayAcces(expr)) { + var typeInfo = _semanticModel.GetTypeInfo(expr); + bool isTypeMismatch = typeInfo.Type == null || !typeInfo.Type.Equals(typeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability); + if (isTypeMismatch) return RefConversion.PreAndPostAssignment; + return RefConversion.Inline; + } + return RefConversion.PreAssigment; + } + + bool IsRefArrayAcces(VBSyntax.ExpressionSyntax argumentExpression) + { + if (!(argumentExpression is VBSyntax.InvocationExpressionSyntax ies)) return false; + var op = _semanticModel.GetOperation(ies); + return (op.IsArrayElementAccess() || IsReturnsByRefPropertyElementAccess(op)) + && GetRefConversion(ies.Expression) == RefConversion.Inline; + + static bool IsReturnsByRefPropertyElementAccess(IOperation operation) => + operation.IsPropertyElementAccess() && operation is IPropertyReferenceOperation { Property: { } prop } && (prop.ReturnsByRef || prop.ReturnsByRefReadonly); + } + } + + private static bool DeclaredInUsing(ISymbol symbolInfo) + { + return symbolInfo?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax()?.Parent?.Parent?.IsKind(VBasic.SyntaxKind.UsingStatement) == true; + } + + private enum RefConversion { Inline, PreAssigment, PreAndPostAssignment } + + private ISymbol GetInvocationSymbol(SyntaxNode invocation) + { + return invocation.TypeSwitch( + (VBSyntax.InvocationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch(), + (VBSyntax.ObjectCreationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch(), + (VBSyntax.RaiseEventStatementSyntax e) => _semanticModel.GetSymbolInfo(e.Name).ExtractBestMatch(), + (VBSyntax.MidExpressionSyntax _) => CommonConversions.KnownTypes.VbCompilerStringType?.GetMembers("MidStmtStr").FirstOrDefault(), + _ => throw new NotSupportedException()); + } + + public override async Task VisitParameterList(VBSyntax.ParameterListSyntax node) + { + var parameters = await node.Parameters.SelectAsync(async p => await p.AcceptAsync(TriviaConvertingExpressionVisitor)); + if (node.Parent is VBSyntax.PropertyStatementSyntax && CommonConversions.IsDefaultIndexer(node.Parent)) { + return SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(parameters)); + } + return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)); + } + + public override async Task VisitParameter(VBSyntax.ParameterSyntax node) + { + var id = CommonConversions.ConvertIdentifier(node.Identifier.Identifier); + TypeSyntax paramType = null; + if (node.Parent?.Parent?.IsKind(VBasic.SyntaxKind.FunctionLambdaHeader, VBasic.SyntaxKind.SubLambdaHeader) != true || node.AsClause != null) { + var vbParamSymbol = _semanticModel.GetDeclaredSymbol(node) as IParameterSymbol; + paramType = vbParamSymbol != null ? CommonConversions.GetTypeSyntax(vbParamSymbol.Type) : await SyntaxOnlyConvertParamAsync(node); + } + var attributes = (await node.AttributeLists.SelectManyAsync(CommonConversions.ConvertAttributeAsync)).ToList(); + var modifiers = CommonConversions.ConvertModifiers(node, node.Modifiers, TokenContext.Local); + var vbSymbol = _semanticModel.GetDeclaredSymbol(node) as IParameterSymbol; + var baseParameters = vbSymbol?.ContainingSymbol.OriginalDefinition.GetBaseSymbol().GetParameters(); + var baseParameter = baseParameters?[vbSymbol.Ordinal]; + var csRefKind = CommonConversions.GetCsRefKind(baseParameter ?? vbSymbol, node); + if (csRefKind == RefKind.Out) { + modifiers = SyntaxFactory.TokenList(modifiers.Where(m => !m.IsKind(SyntaxKind.RefKeyword)).Concat(SyntaxFactory.Token(SyntaxKind.OutKeyword).Yield())); + } + EqualsValueClauseSyntax @default = null; + if (node.Default != null) { + var defaultValue = node.Default.Value.SkipIntoParens(); + if (_semanticModel.GetTypeInfo(defaultValue).Type?.SpecialType == SpecialType.System_DateTime) { + var constant = _semanticModel.GetConstantValue(defaultValue); + if (constant.HasValue && constant.Value is DateTime dt) { + var dateTimeAsLongCsLiteral = CommonConversions.Literal(dt.Ticks).WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia($"/* {defaultValue} */")); + var dateTimeArg = CommonConversions.CreateAttributeArgumentList(SyntaxFactory.AttributeArgument(dateTimeAsLongCsLiteral)); + _extraUsingDirectives.Add("System.Runtime.InteropServices"); + _extraUsingDirectives.Add("System.Runtime.CompilerServices"); + var optionalDateTimeAttributes = new[] { SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("Optional")), SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DateTimeConstant"), dateTimeArg) }; + attributes.Insert(0, SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(optionalDateTimeAttributes))); + } + } else if (node.Modifiers.Any(m => m.IsKind(VBasic.SyntaxKind.ByRefKeyword)) || HasRefParametersAfterThisOne(vbSymbol, baseParameters)) { + var defaultExpression = await node.Default.Value.AcceptAsync(TriviaConvertingExpressionVisitor); + var arg = CommonConversions.CreateAttributeArgumentList(SyntaxFactory.AttributeArgument(defaultExpression)); + _extraUsingDirectives.Add("System.Runtime.InteropServices"); + _extraUsingDirectives.Add("System.Runtime.CompilerServices"); + var optionalAttributes = new List { SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("Optional")) }; + if (!node.Default.Value.IsKind(VBasic.SyntaxKind.NothingLiteralExpression)) { + optionalAttributes.Add(SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DefaultParameterValue"), arg)); + } + attributes.Insert(0, SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(optionalAttributes))); + } + else { + @default = SyntaxFactory.EqualsValueClause(await node.Default.Value.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + } + if (node.Parent.Parent is VBSyntax.MethodStatementSyntax mss && mss.AttributeLists.Any(CommonConversions.HasExtensionAttribute) && node.Parent.ChildNodes().First() == node && vbSymbol.ValidCSharpExtensionMethodParameter()) { + modifiers = modifiers.Insert(0, SyntaxFactory.Token(SyntaxKind.ThisKeyword)); + } + return SyntaxFactory.Parameter(SyntaxFactory.List(attributes), modifiers, paramType, id, @default); + } + + private bool HasRefParametersAfterThisOne(IParameterSymbol vbSymbol, ImmutableArray? baseParameters) => + vbSymbol is not null && baseParameters is {} bp && bp.Skip(vbSymbol.Ordinal + 1).Any(x => x.RefKind != RefKind.None); + + private async Task SyntaxOnlyConvertParamAsync(VBSyntax.ParameterSyntax node) + { + var syntaxParamType = await (node.AsClause?.Type).AcceptAsync(TriviaConvertingExpressionVisitor) ?? SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); + var rankSpecifiers = await CommonConversions.ConvertArrayRankSpecifierSyntaxesAsync(node.Identifier.ArrayRankSpecifiers, node.Identifier.ArrayBounds, false); + if (rankSpecifiers.Any()) syntaxParamType = SyntaxFactory.ArrayType(syntaxParamType, rankSpecifiers); + if (!node.Identifier.Nullable.IsKind(SyntaxKind.None)) { + var arrayType = syntaxParamType as ArrayTypeSyntax; + syntaxParamType = arrayType == null ? SyntaxFactory.NullableType(syntaxParamType) : arrayType.WithElementType(SyntaxFactory.NullableType(arrayType.ElementType)); + } + return syntaxParamType; + } + + public override async Task VisitAttribute(VBSyntax.AttributeSyntax node) + { + return SyntaxFactory.AttributeList( + node.Target == null ? null : SyntaxFactory.AttributeTargetSpecifier(node.Target.AttributeModifier.ConvertToken()), + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor), await node.ArgumentList.AcceptAsync(TriviaConvertingExpressionVisitor))) + ); + } + + private async Task ToAttributeArgumentAsync(VBasic.Syntax.ArgumentSyntax arg) + { + if (!(arg is VBasic.Syntax.SimpleArgumentSyntax a)) throw new NotSupportedException(); + var attr = SyntaxFactory.AttributeArgument(await a.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)); + if (a.IsNamed) attr = attr.WithNameEquals(SyntaxFactory.NameEquals(await a.NameColonEquals.Name.AcceptAsync(TriviaConvertingExpressionVisitor))); + return attr; + } + + private ArgumentListSyntax CreateArgList(ISymbol invocationSymbol) + { + return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + GetAdditionalRequiredArgs(Array.Empty(), invocationSymbol)) + ); + } +} diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.Expressions.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.Expressions.cs new file mode 100644 index 00000000..f4f4cde3 --- /dev/null +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.Expressions.cs @@ -0,0 +1,889 @@ +using System.Collections.Immutable; +using System.Data; +using System.Globalization; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using ICSharpCode.CodeConverter.CSharp.Replacements; +using ICSharpCode.CodeConverter.Util.FromRoslyn; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Simplification; +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; +using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; +// Required for Task +using System.Threading.Tasks; +// Required for NotImplementedException +using System; +using System.Linq; // Added for Enumerable.ElementAtOrDefault +using System.Collections.Generic; // Required for Stack, Dictionary, HashSet + +namespace ICSharpCode.CodeConverter.CSharp; + +internal partial class ExpressionNodeVisitor +{ + private readonly IOperatorConverter _operatorConverter; + private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison; + private readonly Stack _withBlockLhs = new(); + private readonly QueryConverter _queryConverter; + private readonly Lazy> _convertMethodsLookupByReturnType; + private readonly VisualBasicNullableExpressionsConverter _visualBasicNullableTypesConverter; + private readonly Dictionary> _tempNameForAnonymousScope = new(); + private readonly HashSet _generatedNames = new(StringComparer.OrdinalIgnoreCase); + + public override async Task VisitLiteralExpression(VBasic.Syntax.LiteralExpressionSyntax node) + { + var typeInfo = _semanticModel.GetTypeInfo(node); + var convertedType = typeInfo.ConvertedType; + if (node.Token.Value == null) { + if (convertedType == null) { + return SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression); + } + + return !convertedType.IsReferenceType ? SyntaxFactory.DefaultExpression(CommonConversions.GetTypeSyntax(convertedType)) : CommonConversions.Literal(null); + } + + if (TypeConversionAnalyzer.ConvertStringToCharLiteral(node, convertedType, out char chr)) { + return CommonConversions.Literal(chr); + } + + + var val = node.Token.Value; + var text = node.Token.Text; + if (_typeContext.Any() && CommonConversions.WinformsConversions.ShouldPrefixAssignedNameWithUnderscore(node.Parent as VBSyntax.AssignmentStatementSyntax) && val is string valStr) { + val = "_" + valStr; + text = "\"_" + valStr + "\""; + } + + return CommonConversions.Literal(val, text, convertedType); + } + + public override async Task VisitInterpolation(VBasic.Syntax.InterpolationSyntax node) + { + return SyntaxFactory.Interpolation(await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor), await node.AlignmentClause.AcceptAsync(TriviaConvertingExpressionVisitor), await node.FormatClause.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitInterpolatedStringExpression(VBasic.Syntax.InterpolatedStringExpressionSyntax node) + { + var useVerbatim = node.DescendantNodes().OfType().Any(c => LiteralConversions.IsWorthBeingAVerbatimString(c.TextToken.Text)); + var startToken = useVerbatim ? + SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedVerbatimStringStartToken, "$@\"", "$@\"", default(SyntaxTriviaList)) + : SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedStringStartToken, "$\"", "$\"", default(SyntaxTriviaList)); + var contents = await node.Contents.SelectAsync(async c => await c.AcceptAsync(TriviaConvertingExpressionVisitor)); + InterpolatedStringExpressionSyntax interpolatedStringExpressionSyntax = SyntaxFactory.InterpolatedStringExpression(startToken, SyntaxFactory.List(contents), SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken)); + return interpolatedStringExpressionSyntax; + } + + public override async Task VisitInterpolatedStringText(VBasic.Syntax.InterpolatedStringTextSyntax node) + { + var useVerbatim = node.Parent.DescendantNodes().OfType().Any(c => LiteralConversions.IsWorthBeingAVerbatimString(c.TextToken.Text)); + var textForUser = LiteralConversions.EscapeQuotes(node.TextToken.Text, node.TextToken.ValueText, useVerbatim); + InterpolatedStringTextSyntax interpolatedStringTextSyntax = SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedStringTextToken, textForUser, node.TextToken.ValueText, default(SyntaxTriviaList))); + return interpolatedStringTextSyntax; + } + + public override async Task VisitInterpolationAlignmentClause(VBasic.Syntax.InterpolationAlignmentClauseSyntax node) + { + return SyntaxFactory.InterpolationAlignmentClause(SyntaxFactory.Token(SyntaxKind.CommaToken), await node.Value.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitInterpolationFormatClause(VBasic.Syntax.InterpolationFormatClauseSyntax node) + { + var textForUser = LiteralConversions.EscapeEscapeChar(node.FormatStringToken.ValueText); + SyntaxToken formatStringToken = SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.InterpolatedStringTextToken, textForUser, node.FormatStringToken.ValueText, SyntaxTriviaList.Empty); + return SyntaxFactory.InterpolationFormatClause(SyntaxFactory.Token(SyntaxKind.ColonToken), formatStringToken); + } + + public override async Task VisitMeExpression(VBasic.Syntax.MeExpressionSyntax node) + { + return SyntaxFactory.ThisExpression(); + } + + public override async Task VisitMyBaseExpression(VBasic.Syntax.MyBaseExpressionSyntax node) + { + return SyntaxFactory.BaseExpression(); + } + + public override async Task VisitParenthesizedExpression(VBasic.Syntax.ParenthesizedExpressionSyntax node) + { + var cSharpSyntaxNode = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + // If structural changes are necessary the expression may have been lifted a statement (e.g. Type inferred lambda) + return cSharpSyntaxNode is ExpressionSyntax expr ? SyntaxFactory.ParenthesizedExpression(expr) : cSharpSyntaxNode; + } + + public override async Task VisitMemberAccessExpression(VBasic.Syntax.MemberAccessExpressionSyntax node) + { + var nodeSymbol = GetSymbolInfoInDocument(node.Name); + + if (!node.IsParentKind(VBasic.SyntaxKind.InvocationExpression) && + SimpleMethodReplacement.TryGet(nodeSymbol, out var methodReplacement) && + methodReplacement.ReplaceIfMatches(nodeSymbol, Array.Empty(), node.IsParentKind(VBasic.SyntaxKind.AddressOfExpression)) is {} replacement) { + return replacement; + } + + var simpleNameSyntax = await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor); + + var isDefaultProperty = nodeSymbol is IPropertySymbol p && VBasic.VisualBasicExtensions.IsDefault(p); + ExpressionSyntax left = null; + if (node.Expression is VBasic.Syntax.MyClassExpressionSyntax && nodeSymbol != null) { + if (nodeSymbol.IsStatic) { + var typeInfo = _semanticModel.GetTypeInfo(node.Expression); + left = CommonConversions.GetTypeSyntax(typeInfo.Type); + } else { + left = SyntaxFactory.ThisExpression(); + if (nodeSymbol.IsVirtual && !nodeSymbol.IsAbstract || + nodeSymbol.IsImplicitlyDeclared && nodeSymbol is IFieldSymbol { AssociatedSymbol: IPropertySymbol { IsVirtual: true, IsAbstract: false } }) { + simpleNameSyntax = + ValidSyntaxFactory.IdentifierName( + $"MyClass{ConvertIdentifier(node.Name.Identifier).ValueText}"); + } + } + } + if (left == null && nodeSymbol?.IsStatic == true) { + var type = nodeSymbol.ContainingType; + if (type != null) { + left = CommonConversions.GetTypeSyntax(type); + } + } + if (left == null) { + left = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + if (left != null && _semanticModel.GetSymbolInfo(node) is {CandidateReason: CandidateReason.LateBound, CandidateSymbols.Length: 0} + && _semanticModel.GetSymbolInfo(node.Expression).Symbol is {Kind: var expressionSymbolKind} + && expressionSymbolKind != SymbolKind.ErrorType + && _semanticModel.GetOperation(node) is IDynamicMemberReferenceOperation) { + left = SyntaxFactory.ParenthesizedExpression(SyntaxFactory.CastExpression(SyntaxFactory.ParseTypeName("dynamic"), left)); + } + } + if (left == null) { + if (IsSubPartOfConditionalAccess(node)) { + return isDefaultProperty ? SyntaxFactory.ElementBindingExpression() + : await AdjustForImplicitInvocationAsync(node, SyntaxFactory.MemberBindingExpression(simpleNameSyntax)); + } else if (node.IsParentKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NamedFieldInitializer)) { + return ValidSyntaxFactory.IdentifierName(_tempNameForAnonymousScope[node.Name.Identifier.Text].Peek().TempName); + } + left = _withBlockLhs.Peek(); + } + + if (node.IsKind(VBasic.SyntaxKind.DictionaryAccessExpression)) { + var args = SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(CommonConversions.Literal(node.Name.Identifier.ValueText))); + var bracketedArgumentListSyntax = SyntaxFactory.BracketedArgumentList(args); + return SyntaxFactory.ElementAccessExpression(left, bracketedArgumentListSyntax); + } + + if (node.Expression.IsKind(VBasic.SyntaxKind.GlobalName)) { + return SyntaxFactory.AliasQualifiedName((IdentifierNameSyntax)left, simpleNameSyntax); + } + + if (isDefaultProperty && left != null) { + return left; + } + + var memberAccessExpressionSyntax = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, left, simpleNameSyntax); + return await AdjustForImplicitInvocationAsync(node, memberAccessExpressionSyntax); + } + + public override async Task VisitConditionalAccessExpression(VBasic.Syntax.ConditionalAccessExpressionSyntax node) + { + var leftExpression = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor) ?? _withBlockLhs.Peek(); + return SyntaxFactory.ConditionalAccessExpression(leftExpression, await node.WhenNotNull.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitNameOfExpression(VBasic.Syntax.NameOfExpressionSyntax node) + { + return SyntaxFactory.InvocationExpression(ValidSyntaxFactory.NameOf(), SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(await node.Argument.AcceptAsync(TriviaConvertingExpressionVisitor))))); + } + + public override async Task VisitAnonymousObjectCreationExpression(VBasic.Syntax.AnonymousObjectCreationExpressionSyntax node) + { + var vbInitializers = node.Initializer.Initializers; + try { + var initializers = await vbInitializers.AcceptSeparatedListAsync(TriviaConvertingExpressionVisitor); + return SyntaxFactory.AnonymousObjectCreationExpression(initializers); + } finally { + var kvpsToPop = _tempNameForAnonymousScope.Where(t => t.Value.Peek().Scope == node).ToArray(); + foreach (var kvp in kvpsToPop) { + if (kvp.Value.Count == 1) _tempNameForAnonymousScope.Remove(kvp.Key); + else kvp.Value.Pop(); + } + } + } + + public override async Task VisitInferredFieldInitializer(VBasic.Syntax.InferredFieldInitializerSyntax node) + { + return SyntaxFactory.AnonymousObjectMemberDeclarator(await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitObjectCreationExpression(VBasic.Syntax.ObjectCreationExpressionSyntax node) + { + var objectCreationExpressionSyntax = SyntaxFactory.ObjectCreationExpression( + await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor), + await ConvertArgumentListOrEmptyAsync(node, node.ArgumentList), + null + ); + async Task ConvertInitializer() => await node.Initializer.AcceptAsync(TriviaConvertingExpressionVisitor); + if (node.Initializer is VBSyntax.ObjectMemberInitializerSyntax objectMemberInitializerSyntax && HasInitializersUsingImpliedLhs(objectMemberInitializerSyntax)) { + var idToUse = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration("init", objectCreationExpressionSyntax, CommonConversions.GetTypeSyntax(_semanticModel.GetTypeInfo(node).Type))).IdentifierName; + _withBlockLhs.Push(idToUse); + try { + var initializer = await ConvertInitializer(); + var originalExpressions = initializer.Expressions.Select(x => x is AssignmentExpressionSyntax e ? e.ReplaceNode(e.Left, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, idToUse, (SimpleNameSyntax) e.Left)) : null).ToArray(); + var expressions = SyntaxFactory.SeparatedList(originalExpressions.Append(idToUse).Select(SyntaxFactory.Argument)); + var tuple = SyntaxFactory.TupleExpression(expressions); + return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, tuple, idToUse); + } finally { + _withBlockLhs.Pop(); + } + } + return objectCreationExpressionSyntax.WithInitializer(await ConvertInitializer()); + } + + private static bool HasInitializersUsingImpliedLhs(VBSyntax.ObjectMemberInitializerSyntax objectMemberInitializerSyntax) + { + return objectMemberInitializerSyntax.Initializers.SelectMany(i => i.ChildNodes().Skip(1), (_, c) => c.DescendantNodesAndSelf()).SelectMany(d => d).OfType().Any(x => x.Expression is null); + } + + public override async Task VisitArrayCreationExpression(VBasic.Syntax.ArrayCreationExpressionSyntax node) + { + var bounds = await CommonConversions.ConvertArrayRankSpecifierSyntaxesAsync(node.RankSpecifiers, node.ArrayBounds); + var allowInitializer = node.ArrayBounds?.Arguments.Any() != true || + node.Initializer.Initializers.Any() && node.ArrayBounds.Arguments.All(b => b.IsOmitted || _semanticModel.GetConstantValue(b.GetExpression()).HasValue); + var initializerToConvert = allowInitializer ? node.Initializer : null; + return SyntaxFactory.ArrayCreationExpression( + SyntaxFactory.ArrayType(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor), bounds), + await initializerToConvert.AcceptAsync(TriviaConvertingExpressionVisitor) + ); + } + + public override async Task VisitCollectionInitializer(VBasic.Syntax.CollectionInitializerSyntax node) + { + var isExplicitCollectionInitializer = node.Parent is VBasic.Syntax.ObjectCollectionInitializerSyntax + || node.Parent is VBasic.Syntax.CollectionInitializerSyntax + || node.Parent is VBasic.Syntax.ArrayCreationExpressionSyntax; + var initializerKind = node.IsParentKind(VBasic.SyntaxKind.ObjectCollectionInitializer) || node.IsParentKind(VBasic.SyntaxKind.ObjectCreationExpression) ? + SyntaxKind.CollectionInitializerExpression : + node.IsParentKind(VBasic.SyntaxKind.CollectionInitializer) && IsComplexInitializer(node) ? SyntaxKind.ComplexElementInitializerExpression : + SyntaxKind.ArrayInitializerExpression; + var initializers = (await node.Initializers.SelectAsync(async i => { + var convertedInitializer = await i.AcceptAsync(TriviaConvertingExpressionVisitor); + return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(i, convertedInitializer, false); + })); + var initializer = SyntaxFactory.InitializerExpression(initializerKind, SyntaxFactory.SeparatedList(initializers)); + if (isExplicitCollectionInitializer) return initializer; + + var convertedType = _semanticModel.GetTypeInfo(node).ConvertedType; + var dimensions = convertedType is IArrayTypeSymbol ats ? ats.Rank : 1; + if (!(convertedType.GetEnumerableElementTypeOrDefault() is {} elementType)) return SyntaxFactory.ImplicitArrayCreationExpression(initializer); + + if (!initializers.Any() && dimensions == 1) { + var arrayTypeArgs = SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(CommonConversions.GetTypeSyntax(elementType))); + var arrayEmpty = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ValidSyntaxFactory.IdentifierName(nameof(Array)), SyntaxFactory.GenericName(nameof(Array.Empty)).WithTypeArgumentList(arrayTypeArgs)); + return SyntaxFactory.InvocationExpression(arrayEmpty); + } + + bool hasExpressionToInferTypeFrom = node.Initializers.SelectMany(n => n.DescendantNodesAndSelf()).Any(n => n is not VBasic.Syntax.CollectionInitializerSyntax); + if (hasExpressionToInferTypeFrom) { + var commas = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), dimensions - 1); + return SyntaxFactory.ImplicitArrayCreationExpression(SyntaxFactory.TokenList(commas), initializer); + } + + var arrayType = (ArrayTypeSyntax)CommonConversions.CsSyntaxGenerator.ArrayTypeExpression(CommonConversions.GetTypeSyntax(elementType)); + var sizes = Enumerable.Repeat(SyntaxFactory.OmittedArraySizeExpression(), dimensions); + var arrayRankSpecifierSyntax = SyntaxFactory.SingletonList(SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(sizes))); + arrayType = arrayType.WithRankSpecifiers(arrayRankSpecifierSyntax); + return SyntaxFactory.ArrayCreationExpression(arrayType, initializer); + } + + private bool IsComplexInitializer(VBSyntax.CollectionInitializerSyntax node) + { + return _semanticModel.GetOperation(node.Parent.Parent) is IObjectOrCollectionInitializerOperation initializer && + initializer.Initializers.OfType().Any(); + } + + public override async Task VisitQueryExpression(VBasic.Syntax.QueryExpressionSyntax node) + { + return await _queryConverter.ConvertClausesAsync(node.Clauses); + } + + public override async Task VisitTypeOfExpression(VBasic.Syntax.TypeOfExpressionSyntax node) + { + var expr = SyntaxFactory.BinaryExpression( + SyntaxKind.IsExpression, + await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor), + await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor) + ); + return node.IsKind(VBasic.SyntaxKind.TypeOfIsNotExpression) ? expr.InvertCondition() : expr; + } + + public override async Task VisitUnaryExpression(VBasic.Syntax.UnaryExpressionSyntax node) + { + var expr = await node.Operand.AcceptAsync(TriviaConvertingExpressionVisitor); + if (node.IsKind(VBasic.SyntaxKind.AddressOfExpression)) { + return ConvertAddressOf(node, expr); + } + var kind = VBasic.VisualBasicExtensions.Kind(node).ConvertToken(); + SyntaxKind csTokenKind = CSharpUtil.GetExpressionOperatorTokenKind(kind); + + if (kind == SyntaxKind.LogicalNotExpression && _semanticModel.GetTypeInfo(node.Operand).ConvertedType is { } t) { + if (t.IsNumericType() || t.IsEnumType()) { + csTokenKind = SyntaxKind.TildeToken; + } else if (await NegateAndSimplifyOrNullAsync(node, expr, t) is { } simpleNegation) { + return simpleNegation; + } + } + + return SyntaxFactory.PrefixUnaryExpression( + kind, + SyntaxFactory.Token(csTokenKind), + expr.AddParens() + ); + } + + private async Task NegateAndSimplifyOrNullAsync(VBSyntax.UnaryExpressionSyntax node, ExpressionSyntax expr, ITypeSymbol operandConvertedType) + { + if (await _operatorConverter.ConvertReferenceOrNothingComparisonOrNullAsync(node.Operand.SkipIntoParens(), TriviaConvertingExpressionVisitor.IsWithinQuery, true) is { } nothingComparison) { + return nothingComparison; + } + if (operandConvertedType.GetNullableUnderlyingType()?.SpecialType == SpecialType.System_Boolean && node.AlwaysHasBooleanTypeInCSharp()) { + return SyntaxFactory.BinaryExpression(SyntaxKind.EqualsExpression, expr, LiteralConversions.GetLiteralExpression(false)); + } + + if (expr is BinaryExpressionSyntax eq && eq.OperatorToken.IsKind(SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken)){ + return eq.WithOperatorToken(SyntaxFactory.Token(eq.OperatorToken.IsKind(SyntaxKind.ExclamationEqualsToken) ? SyntaxKind.EqualsEqualsToken : SyntaxKind.ExclamationEqualsToken)); + } + + return null; + } + + private CSharpSyntaxNode ConvertAddressOf(VBSyntax.UnaryExpressionSyntax node, ExpressionSyntax expr) + { + var typeInfo = _semanticModel.GetTypeInfo(node); + if (_semanticModel.GetSymbolInfo(node.Operand).Symbol is IMethodSymbol ms && typeInfo.Type is INamedTypeSymbol nt && !ms.CompatibleSignatureToDelegate(nt)) { + int count = nt.DelegateInvokeMethod.Parameters.Length; + return CommonConversions.ThrowawayParameters(expr, count); + } + return expr; + } + + public override async Task VisitBinaryExpression(VBasic.Syntax.BinaryExpressionSyntax entryNode) + { + ExpressionSyntax csLhs = null; + int levelsToConvert = 0; + VBSyntax.BinaryExpressionSyntax currentNode = entryNode; + + for (var nextNode = entryNode; nextNode != null; currentNode = nextNode, nextNode = currentNode.Left as VBSyntax.BinaryExpressionSyntax, levelsToConvert++) { + if (await RewriteBinaryOperatorOrNullAsync(nextNode) is { } operatorNode) { + csLhs = operatorNode; + break; + } + } + + for (; levelsToConvert > 0; currentNode = currentNode!.Parent as VBSyntax.BinaryExpressionSyntax, levelsToConvert--) { + csLhs = (ExpressionSyntax)await ConvertBinaryExpressionAsync(currentNode, csLhs); + } + + return csLhs; + } + + private async Task ConvertBinaryExpressionAsync(VBasic.Syntax.BinaryExpressionSyntax node, ExpressionSyntax lhs = null, ExpressionSyntax rhs = null) + { + lhs ??= await node.Left.AcceptAsync(TriviaConvertingExpressionVisitor); + rhs ??= await node.Right.AcceptAsync(TriviaConvertingExpressionVisitor); + + var lhsTypeInfo = _semanticModel.GetTypeInfo(node.Left); + var rhsTypeInfo = _semanticModel.GetTypeInfo(node.Right); + + ITypeSymbol forceLhsTargetType = null; + bool omitRightConversion = false; + bool omitConversion = false; + if (lhsTypeInfo.Type != null && rhsTypeInfo.Type != null) + { + if (node.IsKind(VBasic.SyntaxKind.ConcatenateExpression) && + !lhsTypeInfo.Type.IsEnumType() && !rhsTypeInfo.Type.IsEnumType() && + !lhsTypeInfo.Type.IsDateType() && !rhsTypeInfo.Type.IsDateType()) + { + omitRightConversion = true; + omitConversion = lhsTypeInfo.Type.SpecialType == SpecialType.System_String || + rhsTypeInfo.Type.SpecialType == SpecialType.System_String; + if (lhsTypeInfo.ConvertedType.SpecialType != SpecialType.System_String) { + forceLhsTargetType = CommonConversions.KnownTypes.String; + } + } + } + + var objectEqualityType = _visualBasicEqualityComparison.GetObjectEqualityType(node, lhsTypeInfo, rhsTypeInfo); + + switch (objectEqualityType) { + case VisualBasicEqualityComparison.RequiredType.StringOnly: + if (lhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && + rhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && + _visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, out CSharpSyntaxNode visitBinaryExpression)) { + return visitBinaryExpression; + } + (lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false); + omitConversion = true; + break; + case VisualBasicEqualityComparison.RequiredType.Object: + return _visualBasicEqualityComparison.GetFullExpressionForVbObjectComparison(lhs, rhs, ComparisonKind.Equals, node.IsKind(VBasic.SyntaxKind.NotEqualsExpression)); + } + + var lhsTypeIgnoringNullable = lhsTypeInfo.Type.GetNullableUnderlyingType() ?? lhsTypeInfo.Type; + var rhsTypeIgnoringNullable = rhsTypeInfo.Type.GetNullableUnderlyingType() ?? rhsTypeInfo.Type; + omitConversion |= lhsTypeIgnoringNullable != null && rhsTypeIgnoringNullable != null && + lhsTypeIgnoringNullable.IsEnumType() && SymbolEqualityComparer.Default.Equals(lhsTypeIgnoringNullable, rhsTypeIgnoringNullable) + && !node.IsKind(VBasic.SyntaxKind.AddExpression, VBasic.SyntaxKind.SubtractExpression, VBasic.SyntaxKind.MultiplyExpression, VBasic.SyntaxKind.DivideExpression, VBasic.SyntaxKind.IntegerDivideExpression, VBasic.SyntaxKind.ModuloExpression) + && forceLhsTargetType == null; + lhs = omitConversion ? lhs : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Left, lhs, forceTargetType: forceLhsTargetType); + rhs = omitConversion || omitRightConversion ? rhs : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Right, rhs); + + var kind = VBasic.VisualBasicExtensions.Kind(node).ConvertToken(); + var op = SyntaxFactory.Token(CSharpUtil.GetExpressionOperatorTokenKind(kind)); + + var csBinExp = SyntaxFactory.BinaryExpression(kind, lhs, op, rhs); + var exp = _visualBasicNullableTypesConverter.WithBinaryExpressionLogicForNullableTypes(node, lhsTypeInfo, rhsTypeInfo, csBinExp, lhs, rhs); + return node.Parent.IsKind(VBasic.SyntaxKind.SimpleArgument) ? exp : exp.AddParens(); + } + + private async Task RewriteBinaryOperatorOrNullAsync(VBSyntax.BinaryExpressionSyntax node) => + await _operatorConverter.ConvertRewrittenBinaryOperatorOrNullAsync(node, TriviaConvertingExpressionVisitor.IsWithinQuery); + + public override async Task VisitInvocationExpression( + VBasic.Syntax.InvocationExpressionSyntax node) + { + var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch(); + var methodInvocationSymbol = invocationSymbol as IMethodSymbol; + var withinLocalFunction = methodInvocationSymbol != null && RequiresLocalFunction(node, methodInvocationSymbol); + if (withinLocalFunction) { + _typeContext.PerScopeState.PushScope(); + } + try { + + if (node.Expression is null) { + var convertArgumentListOrEmptyAsync = await ConvertArgumentsAsync(node.ArgumentList); + return SyntaxFactory.ElementBindingExpression(SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(convertArgumentListOrEmptyAsync))); + } + + var convertedInvocation = await ConvertOrReplaceInvocationAsync(node, invocationSymbol); + if (withinLocalFunction) { + return await HoistAndCallLocalFunctionAsync(node, methodInvocationSymbol, (ExpressionSyntax)convertedInvocation); + } + return convertedInvocation; + } finally { + if (withinLocalFunction) { + _typeContext.PerScopeState.PopExpressionScope(); + } + } + } + + private async Task ConvertOrReplaceInvocationAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol invocationSymbol) + { + var expressionSymbol = _semanticModel.GetSymbolInfo(node.Expression).ExtractBestMatch(); + if ((await SubstituteVisualBasicMethodOrNullAsync(node, expressionSymbol) ?? + await WithRemovedRedundantConversionOrNullAsync(node, expressionSymbol)) is { } csEquivalent) { + return csEquivalent; + } + + if (invocationSymbol?.Name is "op_Implicit" or "op_Explicit") { + var vbExpr = node.ArgumentList.Arguments.Single().GetExpression(); + var csExpr = await vbExpr.AcceptAsync(TriviaConvertingExpressionVisitor); + return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(vbExpr, csExpr, true, true, false, forceTargetType: invocationSymbol.GetReturnType()); + } + + return await ConvertInvocationAsync(node, invocationSymbol, expressionSymbol); + } + + private async Task ConvertInvocationAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol invocationSymbol, ISymbol expressionSymbol) + { + var expressionType = _semanticModel.GetTypeInfo(node.Expression).Type; + var expressionReturnType = expressionSymbol?.GetReturnType() ?? expressionType; + var operation = _semanticModel.GetOperation(node); + + var expr = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + if (await TryConvertParameterizedPropertyAsync(operation, node, expr, node.ArgumentList) is { } invocation) + { + return invocation; + } + + (var convertedExpression, bool shouldBeElementAccess) = await ConvertInvocationSubExpressionAsync(node, operation, expressionSymbol, expressionReturnType, expr); + if (shouldBeElementAccess) + { + return await CreateElementAccessAsync(node, convertedExpression); + } + + if (expressionSymbol != null && expressionSymbol.IsKind(SymbolKind.Property) && + invocationSymbol != null && invocationSymbol.GetParameters().Length == 0 && node.ArgumentList.Arguments.Count == 0) + { + return convertedExpression; + } + + var convertedArgumentList = await ConvertArgumentListOrEmptyAsync(node, node.ArgumentList); + + if (IsElementAtOrDefaultInvocation(invocationSymbol, expressionSymbol)) + { + convertedExpression = GetElementAtOrDefaultExpression(expressionType, convertedExpression); + } + + if (invocationSymbol.IsReducedExtension() && invocationSymbol is IMethodSymbol {ReducedFrom: {Parameters: var parameters}} && + !parameters.FirstOrDefault().ValidCSharpExtensionMethodParameter() && + node.Expression is VBSyntax.MemberAccessExpressionSyntax maes) + { + var thisArgExpression = await maes.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + var thisArg = SyntaxFactory.Argument(thisArgExpression).WithRefKindKeyword(GetRefToken(RefKind.Ref)); + convertedArgumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(convertedArgumentList.Arguments.Prepend(thisArg))); + var containingType = (ExpressionSyntax) CommonConversions.CsSyntaxGenerator.TypeExpression(invocationSymbol.ContainingType); + convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, containingType, + ValidSyntaxFactory.IdentifierName((invocationSymbol.Name))); + } + + if (invocationSymbol is IMethodSymbol m && convertedExpression is LambdaExpressionSyntax) { + convertedExpression = SyntaxFactory.ObjectCreationExpression(CommonConversions.GetFuncTypeSyntax(expressionType, m), ExpressionSyntaxExtensions.CreateArgList(convertedExpression), null); + } + return SyntaxFactory.InvocationExpression(convertedExpression, convertedArgumentList); + } + + private async Task<(ExpressionSyntax, bool isElementAccess)> ConvertInvocationSubExpressionAsync(VBSyntax.InvocationExpressionSyntax node, + IOperation operation, ISymbol expressionSymbol, ITypeSymbol expressionReturnType, CSharpSyntaxNode expr) + { + var isElementAccess = operation.IsPropertyElementAccess() + || operation.IsArrayElementAccess() + || ProbablyNotAMethodCall(node, expressionSymbol, expressionReturnType); + + var expressionSyntax = (ExpressionSyntax)expr; + + return (expressionSyntax, isElementAccess); + } + + private async Task CreateElementAccessAsync(VBSyntax.InvocationExpressionSyntax node, ExpressionSyntax expression) + { + var args = + await node.ArgumentList.Arguments.AcceptSeparatedListAsync(TriviaConvertingExpressionVisitor); + var bracketedArgumentListSyntax = SyntaxFactory.BracketedArgumentList(args); + if (expression is ElementBindingExpressionSyntax binding && + !binding.ArgumentList.Arguments.Any()) { + return binding.WithArgumentList(bracketedArgumentListSyntax); + } + + return SyntaxFactory.ElementAccessExpression(expression, bracketedArgumentListSyntax); + } + + private static bool IsElementAtOrDefaultInvocation(ISymbol invocationSymbol, ISymbol expressionSymbol) + { + return (expressionSymbol != null + && (invocationSymbol?.Name == nameof(Enumerable.ElementAtOrDefault) + && !expressionSymbol.Equals(invocationSymbol, SymbolEqualityComparer.IncludeNullability))); + } + + private ExpressionSyntax GetElementAtOrDefaultExpression(ISymbol expressionType, + ExpressionSyntax expression) + { + _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Linq)); + + if (expressionType.Name == nameof(DataTable)) + { + _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Data)); + + expression = SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, expression, + ValidSyntaxFactory.IdentifierName(nameof(DataTableExtensions.AsEnumerable)))); + } + + var newExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + expression, ValidSyntaxFactory.IdentifierName(nameof(Enumerable.ElementAtOrDefault))); + + return newExpression; + } + + private async Task TryConvertParameterizedPropertyAsync(IOperation operation, + SyntaxNode node, CSharpSyntaxNode identifier, + VBSyntax.ArgumentListSyntax optionalArgumentList = null) + { + var (overrideIdentifier, extraArg) = + await CommonConversions.GetParameterizedPropertyAccessMethodAsync(operation); + if (overrideIdentifier != null) + { + var expr = identifier; + var idToken = expr.DescendantTokens().Last(t => t.IsKind(SyntaxKind.IdentifierToken)); + expr = ReplaceRightmostIdentifierText(expr, idToken, overrideIdentifier); + + var args = await ConvertArgumentListOrEmptyAsync(node, optionalArgumentList); + if (extraArg != null) { + var extraArgSyntax = SyntaxFactory.Argument(extraArg); + var propertySymbol = ((IPropertyReferenceOperation)operation).Property; + var forceNamedExtraArg = args.Arguments.Count != propertySymbol.GetParameters().Length || + args.Arguments.Any(t => t.NameColon != null); + + if (forceNamedExtraArg) { + extraArgSyntax = extraArgSyntax.WithNameColon(SyntaxFactory.NameColon("value")); + } + + args = args.WithArguments(args.Arguments.Add(extraArgSyntax)); + } + + return SyntaxFactory.InvocationExpression((ExpressionSyntax)expr, args); + } + + return null; + } + + private async Task HoistAndCallLocalFunctionAsync(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol, ExpressionSyntax csExpression) + { + const string retVariableName = "ret"; + var localFuncName = $"local{invocationSymbol.Name}"; + + var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName, csExpression); + + var statements = await _typeContext.PerScopeState.CreateLocalsAsync(invocation, new[] { callAndStoreResult }, _generatedNames, _semanticModel); + + var block = SyntaxFactory.Block( + statements.Concat(SyntaxFactory.ReturnStatement(ValidSyntaxFactory.IdentifierName(retVariableName)).Yield()) + ); + var returnType = CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType); + + var refParametersOfParent = GetRefParameters(invocation.ArgumentList); + var (args, @params) = CreateArgumentsAndParametersLists(refParametersOfParent); + + var localFunc = _typeContext.PerScopeState.Hoist(new HoistedFunction(localFuncName, returnType, block, SyntaxFactory.ParameterList(@params))); + return SyntaxFactory.InvocationExpression(localFunc.TempIdentifier, SyntaxFactory.ArgumentList(args)); + } + + private bool RequiresLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol) + { + if (invocation.ArgumentList == null) return false; + var definitelyExecutedAfterPrevious = DefinitelyExecutedAfterPreviousStatement(invocation); + var nextStatementDefinitelyExecuted = NextStatementDefinitelyExecutedAfter(invocation); + if (definitelyExecutedAfterPrevious && nextStatementDefinitelyExecuted) return false; + var possibleInline = definitelyExecutedAfterPrevious ? RefConversion.PreAssigment : RefConversion.Inline; + return invocation.ArgumentList.Arguments.Any(a => RequiresLocalFunction(possibleInline, invocation, invocationSymbol, a)); + + bool RequiresLocalFunction(RefConversion possibleInline, VBSyntax.InvocationExpressionSyntax inv, IMethodSymbol sym, VBSyntax.ArgumentSyntax arg) + { + var refConversion = GetRefConversionType(arg, inv.ArgumentList, sym.Parameters, out string _, out _); + if (RefConversion.Inline == refConversion || possibleInline == refConversion) return false; + if (!(arg is VBSyntax.SimpleArgumentSyntax sas)) return false; + var argExpression = sas.Expression.SkipIntoParens(); + if (argExpression is VBSyntax.InstanceExpressionSyntax) return false; + return !_semanticModel.GetConstantValue(argExpression).HasValue; + } + } + + private bool DefinitelyExecutedAfterPreviousStatement(VBSyntax.InvocationExpressionSyntax invocation) + { + SyntaxNode parent = invocation; + while (true) { + parent = parent.Parent; + switch (parent) + { + case VBSyntax.ParenthesizedExpressionSyntax _: + continue; + case VBSyntax.BinaryExpressionSyntax binaryExpression: + if (binaryExpression.Left == invocation) continue; + else return false; + case VBSyntax.ArgumentSyntax argumentSyntax: + if (argumentSyntax.Parent.Parent is VBSyntax.InvocationExpressionSyntax parentInvocation && parentInvocation.ArgumentList.Arguments.First() == argumentSyntax && FirstArgDefinitelyEvaluated(parentInvocation)) continue; + else return false; + case VBSyntax.ElseIfStatementSyntax _: + case VBSyntax.ExpressionSyntax _: + return false; + case VBSyntax.StatementSyntax _: + return true; + } + } + } + + private bool FirstArgDefinitelyEvaluated(VBSyntax.InvocationExpressionSyntax parentInvocation) => + parentInvocation.Expression.SkipIntoParens() switch { + VBSyntax.IdentifierNameSyntax _ => true, + VBSyntax.MemberAccessExpressionSyntax maes => maes.Expression is {} exp && !MayThrow(exp), + _ => true + }; + + private bool MayThrow(VBSyntax.ExpressionSyntax expression) + { + expression = expression.SkipIntoParens(); + if (expression is VBSyntax.InstanceExpressionSyntax) return false; + var symbol = _semanticModel.GetSymbolInfo(expression).Symbol; + return !symbol.IsKind(SymbolKind.Local) && !symbol.IsKind(SymbolKind.Field); + } + + private static bool NextStatementDefinitelyExecutedAfter(VBSyntax.InvocationExpressionSyntax invocation) + { + SyntaxNode parent = invocation; + while (true) { + parent = parent.Parent; + switch (parent) + { + case VBSyntax.ParenthesizedExpressionSyntax _: + continue; + case VBSyntax.BinaryExpressionSyntax binaryExpression: + if (binaryExpression.Right == invocation) continue; + else return false; + case VBSyntax.IfStatementSyntax _: + case VBSyntax.ElseIfStatementSyntax _: + case VBSyntax.SingleLineIfStatementSyntax _: + return false; + case VBSyntax.ExpressionSyntax _: + case VBSyntax.StatementSyntax _: + return true; + } + } + } + public override async Task VisitBinaryConditionalExpression(VBasic.Syntax.BinaryConditionalExpressionSyntax node) + { + var leftSide = await node.FirstExpression.AcceptAsync(TriviaConvertingExpressionVisitor); + var rightSide = await node.SecondExpression.AcceptAsync(TriviaConvertingExpressionVisitor); + var expr = SyntaxFactory.BinaryExpression(SyntaxKind.CoalesceExpression, + node.FirstExpression.ParenthesizeIfPrecedenceCouldChange(leftSide), + node.SecondExpression.ParenthesizeIfPrecedenceCouldChange(rightSide)); + + if (node.Parent.IsKind(VBasic.SyntaxKind.Interpolation) || node.PrecedenceCouldChange()) + return SyntaxFactory.ParenthesizedExpression(expr); + + return expr; + } + + public override async Task VisitTernaryConditionalExpression(VBasic.Syntax.TernaryConditionalExpressionSyntax node) + { + var condition = await node.Condition.AcceptAsync(TriviaConvertingExpressionVisitor); + condition = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Condition, condition, forceTargetType: CommonConversions.KnownTypes.Boolean); + + var whenTrue = await node.WhenTrue.AcceptAsync(TriviaConvertingExpressionVisitor); + whenTrue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.WhenTrue, whenTrue); + + var whenFalse = await node.WhenFalse.AcceptAsync(TriviaConvertingExpressionVisitor); + whenFalse = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.WhenFalse, whenFalse); + + var expr = SyntaxFactory.ConditionalExpression(condition, whenTrue, whenFalse); + + if (node.Parent.IsKind(VBasic.SyntaxKind.Interpolation) || node.PrecedenceCouldChange()) + return SyntaxFactory.ParenthesizedExpression(expr); + + return expr; + } + private async Task WithRemovedRedundantConversionOrNullAsync(VBSyntax.InvocationExpressionSyntax conversionNode, ISymbol invocationSymbol) + { + if (invocationSymbol?.ContainingNamespace.MetadataName != nameof(Microsoft.VisualBasic) || + invocationSymbol.ContainingType.Name != nameof(Conversions) || + !invocationSymbol.Name.StartsWith("To", StringComparison.InvariantCulture) || + conversionNode.ArgumentList.Arguments.Count != 1) { + return null; + } + + var conversionArg = conversionNode.ArgumentList.Arguments.First().GetExpression(); + VBSyntax.ExpressionSyntax coercedConversionNode = conversionNode; + return await WithRemovedRedundantConversionOrNullAsync(coercedConversionNode, conversionArg); + } + + private async Task WithRemovedRedundantConversionOrNullAsync(VBSyntax.ExpressionSyntax conversionNode, VBSyntax.ExpressionSyntax conversionArg) + { + var csharpArg = await conversionArg.AcceptAsync(TriviaConvertingExpressionVisitor); + var typeInfo = _semanticModel.GetTypeInfo(conversionNode); + + var writtenByUser = !conversionNode.HasAnnotation(Simplifier.Annotation); + var forceTargetType = typeInfo.ConvertedType; + return writtenByUser ? null : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(conversionArg, csharpArg, + forceTargetType: forceTargetType, defaultToCast: true); + } + private async Task SubstituteVisualBasicMethodOrNullAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol symbol) + { + ExpressionSyntax cSharpSyntaxNode = null; + if (IsVisualBasicChrMethod(symbol)) { + var vbArg = node.ArgumentList.Arguments.Single().GetExpression(); + var constValue = _semanticModel.GetConstantValue(vbArg); + if (IsCultureInvariant(constValue)) { + var csArg = await vbArg.AcceptAsync(TriviaConvertingExpressionVisitor); + cSharpSyntaxNode = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node, csArg, true, true, true, forceTargetType: _semanticModel.GetTypeInfo(node).Type); + } + } + + if (SimpleMethodReplacement.TryGet(symbol, out var methodReplacement) && + methodReplacement.ReplaceIfMatches(symbol, await ConvertArgumentsAsync(node.ArgumentList), false) is {} csExpression) { + cSharpSyntaxNode = csExpression; + } + + return cSharpSyntaxNode; + } + private static bool IsVisualBasicChrMethod(ISymbol symbol) => + symbol is not null + && symbol.ContainingNamespace.MetadataName == nameof(Microsoft.VisualBasic) + && (symbol.Name == "ChrW" || symbol.Name == "Chr"); + private static bool IsCultureInvariant(Optional constValue) => + constValue.HasValue && Convert.ToUInt64(constValue.Value, CultureInfo.InvariantCulture) <= 127; + + private bool ProbablyNotAMethodCall(VBasic.Syntax.InvocationExpressionSyntax node, ISymbol symbol, ITypeSymbol symbolReturnType) + { + return !node.IsParentKind(VBasic.SyntaxKind.CallStatement) && !(symbol is IMethodSymbol) && + symbolReturnType.IsErrorType() && node.Expression is VBasic.Syntax.IdentifierNameSyntax && + node.ArgumentList?.Arguments.OnlyOrDefault()?.GetExpression() is {} arg && + _semanticModel.GetTypeInfo(arg).Type.IsNumericType(); + } + + private async Task ConvertCastExpressionAsync(VBSyntax.CastExpressionSyntax node, + ExpressionSyntax convertMethodOrNull = null, VBSyntax.TypeSyntax castToOrNull = null) + { + var simplifiedOrNull = await WithRemovedRedundantConversionOrNullAsync(node, node.Expression); + if (simplifiedOrNull != null) return simplifiedOrNull; + var expressionSyntax = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + if (_semanticModel.GetOperation(node) is not IConversionOperation { Conversion.IsIdentity: true }) { + if (convertMethodOrNull != null) { + expressionSyntax = Invoke(convertMethodOrNull, expressionSyntax); + } + + if (castToOrNull != null) { + expressionSyntax = await CastAsync(expressionSyntax, castToOrNull); + expressionSyntax = node.ParenthesizeIfPrecedenceCouldChange(expressionSyntax); + } + } + return expressionSyntax; + } + + private async Task CastAsync(ExpressionSyntax expressionSyntax, VBSyntax.TypeSyntax typeSyntax) + { + return ValidSyntaxFactory.CastExpression(await typeSyntax.AcceptAsync(TriviaConvertingExpressionVisitor), expressionSyntax); + } + + private static InvocationExpressionSyntax Invoke(ExpressionSyntax toInvoke, ExpressionSyntax argExpression) + { + return + SyntaxFactory.InvocationExpression(toInvoke, + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument(argExpression))) + ); + } + private ExpressionSyntax GetConvertMethodForKeywordOrNull(SyntaxNode type) + { + var targetType = _semanticModel.GetTypeInfo(type).Type; + return GetConvertMethodForKeywordOrNull(targetType); + } + + private ExpressionSyntax GetConvertMethodForKeywordOrNull(ITypeSymbol targetType) + { + _extraUsingDirectives.Add(ConvertType.Namespace); + return targetType != null && + _convertMethodsLookupByReturnType.Value.TryGetValue(targetType, out var convertMethodName) + ? SyntaxFactory.ParseExpression(convertMethodName) + : null; + } + private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node) + { + var firstPossiblyConditionalAncestor = node.Parent; + while (firstPossiblyConditionalAncestor != null && + firstPossiblyConditionalAncestor.IsKind(VBasic.SyntaxKind.InvocationExpression, + VBasic.SyntaxKind.SimpleMemberAccessExpression)) { + firstPossiblyConditionalAncestor = firstPossiblyConditionalAncestor.Parent; + } + + return firstPossiblyConditionalAncestor?.IsKind(VBasic.SyntaxKind.ConditionalAccessExpression) == true; + } + private CSharpSyntaxNode AddEmptyArgumentListIfImplicit(SyntaxNode node, ExpressionSyntax id) + { + if (_semanticModel.SyntaxTree != node.SyntaxTree) return id; + return _semanticModel.GetOperation(node) switch { + IInvocationOperation invocation => SyntaxFactory.InvocationExpression(id, CreateArgList(invocation.TargetMethod)), + IPropertyReferenceOperation propReference when propReference.Property.Parameters.Any() => SyntaxFactory.InvocationExpression(id, CreateArgList(propReference.Property)), + _ => id + }; + } +} diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.Lambdas.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.Lambdas.cs new file mode 100644 index 00000000..34c6e37a --- /dev/null +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.Lambdas.cs @@ -0,0 +1,116 @@ +using System.Collections.Immutable; +//using System.Data; // Not directly used by lambda methods +using System.Globalization; +//using System.Linq.Expressions; // Not directly used by lambda methods +//using System.Runtime.CompilerServices; +//using System.Xml.Linq; // Not used +//using ICSharpCode.CodeConverter.CSharp.Replacements; // Not directly used +using ICSharpCode.CodeConverter.Util.FromRoslyn; // For .Yield() +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Operations; // Not directly used +//using Microsoft.CodeAnalysis.Simplification; // Not directly used +//using Microsoft.VisualBasic; // Not directly used +//using Microsoft.VisualBasic.CompilerServices; // Not directly used +//using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; // Not used +using System.Threading.Tasks; +using System; +using System.Linq; +using System.Collections.Generic; +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; + +namespace ICSharpCode.CodeConverter.CSharp; + +internal partial class ExpressionNodeVisitor // Must be partial +{ + private readonly LambdaConverter _lambdaConverter; + + public override async Task VisitSingleLineLambdaExpression(VBasic.Syntax.SingleLineLambdaExpressionSyntax node) + { + var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; + TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); + try { + return await ConvertInnerAsync(); + } finally { + TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; + } + + async Task ConvertInnerAsync() + { + IReadOnlyCollection convertedStatements; + if (node.Body is VBasic.Syntax.StatementSyntax statement) + { + convertedStatements = await ConvertMethodBodyStatementsAsync(statement, statement.Yield().ToArray()); + } + else + { + var csNode = await node.Body.AcceptAsync(TriviaConvertingExpressionVisitor); + convertedStatements = new[] {SyntaxFactory.ExpressionStatement(csNode)}; + } + + var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync(TriviaConvertingExpressionVisitor); + return await _lambdaConverter.ConvertAsync(node, param, convertedStatements); + } + } + + public override async Task VisitMultiLineLambdaExpression(VBasic.Syntax.MultiLineLambdaExpressionSyntax node) + { + var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; + TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); + try { + return await ConvertInnerAsync(); + } finally { + TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; + } + + async Task ConvertInnerAsync() + { + var body = await ConvertMethodBodyStatementsAsync(node, node.Statements); + var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync(TriviaConvertingExpressionVisitor); + return await _lambdaConverter.ConvertAsync(node, param, body.ToList()); + } + } + + public async Task> ConvertMethodBodyStatementsAsync(VBasic.VisualBasicSyntaxNode node, IReadOnlyCollection statements, bool isIterator = false, IdentifierNameSyntax csReturnVariable = null) + { + // _visualBasicEqualityComparison and _withBlockLhs are accessed via `this` from other partial classes + var innerMethodBodyVisitor = await MethodBodyExecutableStatementVisitor.CreateAsync(node, _semanticModel, TriviaConvertingExpressionVisitor, CommonConversions, _visualBasicEqualityComparison, new Stack(), _extraUsingDirectives, _typeContext, isIterator, csReturnVariable); + return await GetWithConvertedGotosOrNull(statements) ?? await ConvertStatements(statements); + + async Task> ConvertStatements(IEnumerable readOnlyCollection) + { + return (await readOnlyCollection.SelectManyAsync(async s => (IEnumerable)await s.Accept(innerMethodBodyVisitor.CommentConvertingVisitor))).ToList(); + } + + async Task> GetWithConvertedGotosOrNull(IReadOnlyCollection stmts) + { + var onlyIdentifierLabel = stmts.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.LabelStatement)); + var onlyOnErrorGotoStatement = stmts.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.OnErrorGoToLabelStatement)); + + // See https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/on-error-statement + if (onlyIdentifierLabel != null && onlyOnErrorGotoStatement != null) { + var statementsList = stmts.ToList(); + var onlyIdentifierLabelIndex = statementsList.IndexOf(onlyIdentifierLabel); + var onlyOnErrorGotoStatementIndex = statementsList.IndexOf(onlyOnErrorGotoStatement); + + if (onlyOnErrorGotoStatementIndex < onlyIdentifierLabelIndex) { + var beforeStatements = await ConvertStatements(stmts.Take(onlyOnErrorGotoStatementIndex)); + var tryBlockStatements = await ConvertStatements(stmts.Take(onlyIdentifierLabelIndex).Skip(onlyOnErrorGotoStatementIndex + 1)); + var tryBlock = SyntaxFactory.Block(tryBlockStatements); + var afterStatements = await ConvertStatements(stmts.Skip(onlyIdentifierLabelIndex + 1)); + + var catchClauseSyntax = SyntaxFactory.CatchClause(); + + if (tryBlockStatements.LastOrDefault().IsKind(SyntaxKind.ReturnStatement)) { + catchClauseSyntax = catchClauseSyntax.WithBlock(SyntaxFactory.Block(afterStatements)); + afterStatements = new List(); + } + + var tryStatement = SyntaxFactory.TryStatement(SyntaxFactory.SingletonList(catchClauseSyntax)).WithBlock(tryBlock); + return beforeStatements.Append(tryStatement).Concat(afterStatements).ToList(); + } + } + return null; + } + } +} diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.Types.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.Types.cs new file mode 100644 index 00000000..7a169c79 --- /dev/null +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.Types.cs @@ -0,0 +1,250 @@ +using System.Collections.Immutable; +using System.Data; +using System.Globalization; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using ICSharpCode.CodeConverter.CSharp.Replacements; +using ICSharpCode.CodeConverter.Util.FromRoslyn; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Simplification; +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; +// Required for Task +using System.Threading.Tasks; +// Required for NotImplementedException +using System; +using System.Linq; +using System.Collections.Generic; +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; + + +namespace ICSharpCode.CodeConverter.CSharp; + +internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor> +{ + public override async Task VisitGetTypeExpression(VBasic.Syntax.GetTypeExpressionSyntax node) + { + return SyntaxFactory.TypeOfExpression(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitGlobalName(VBasic.Syntax.GlobalNameSyntax node) + { + return ValidSyntaxFactory.IdentifierName(SyntaxFactory.Token(SyntaxKind.GlobalKeyword)); + } + + public override async Task VisitTupleType(VBasic.Syntax.TupleTypeSyntax node) + { + var elements = await node.Elements.SelectAsync(async e => await e.AcceptAsync(TriviaConvertingExpressionVisitor)); + return SyntaxFactory.TupleType(SyntaxFactory.SeparatedList(elements)); + } + + public override async Task VisitTypedTupleElement(VBasic.Syntax.TypedTupleElementSyntax node) + { + return SyntaxFactory.TupleElement(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitNamedTupleElement(VBasic.Syntax.NamedTupleElementSyntax node) + { + return SyntaxFactory.TupleElement(await node.AsClause.Type.AcceptAsync(TriviaConvertingExpressionVisitor), CommonConversions.ConvertIdentifier(node.Identifier)); + } + + public override async Task VisitPredefinedType(VBasic.Syntax.PredefinedTypeSyntax node) + { + if (node.Keyword.IsKind(VBasic.SyntaxKind.DateKeyword)) { + return ValidSyntaxFactory.IdentifierName(nameof(DateTime)); + } + return SyntaxFactory.PredefinedType(node.Keyword.ConvertToken()); + } + + public override async Task VisitNullableType(VBasic.Syntax.NullableTypeSyntax node) + { + return SyntaxFactory.NullableType(await node.ElementType.AcceptAsync(TriviaConvertingExpressionVisitor)); + } + + public override async Task VisitArrayType(VBasic.Syntax.ArrayTypeSyntax node) + { + var ranks = await node.RankSpecifiers.SelectAsync(async r => await r.AcceptAsync(TriviaConvertingExpressionVisitor)); + return SyntaxFactory.ArrayType(await node.ElementType.AcceptAsync(TriviaConvertingExpressionVisitor), SyntaxFactory.List(ranks)); + } + + public override async Task VisitArrayRankSpecifier(VBasic.Syntax.ArrayRankSpecifierSyntax node) + { + return SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(Enumerable.Repeat(SyntaxFactory.OmittedArraySizeExpression(), node.Rank))); + } + + public override async Task VisitIdentifierName(VBasic.Syntax.IdentifierNameSyntax node) + { + var identifier = SyntaxFactory.IdentifierName(CommonConversions.ConvertIdentifier(node.Identifier, node.GetAncestor() != null)); + + bool requiresQualification = !node.Parent.IsKind(VBasic.SyntaxKind.SimpleMemberAccessExpression, VBasic.SyntaxKind.QualifiedName, VBasic.SyntaxKind.NameColonEquals, VBasic.SyntaxKind.ImportsStatement, VBasic.SyntaxKind.NamespaceStatement, VBasic.SyntaxKind.NamedFieldInitializer) || + node.Parent is VBSyntax.NamedFieldInitializerSyntax nfs && nfs.Expression == node || + node.Parent is VBasic.Syntax.MemberAccessExpressionSyntax maes && maes.Expression == node; + var qualifiedIdentifier = requiresQualification + ? QualifyNode(node, identifier) : identifier; + + var sym = GetSymbolInfoInDocument(node); // Assumes GetSymbolInfoInDocument is available (kept in main or accessible) + if (sym is ILocalSymbol) { + if (sym.IsStatic && sym.ContainingSymbol is IMethodSymbol m && m.AssociatedSymbol is IPropertySymbol) { + qualifiedIdentifier = qualifiedIdentifier.WithParentPropertyAccessorKind(m.MethodKind); + } + + var vbMethodBlock = node.Ancestors().OfType().FirstOrDefault(); + if (vbMethodBlock != null && + vbMethodBlock.MustReturn() && + !node.Parent.IsKind(VBasic.SyntaxKind.NameOfExpression) && + node.Identifier.ValueText.Equals(CommonConversions.GetMethodBlockBaseIdentifierForImplicitReturn(vbMethodBlock).ValueText, StringComparison.OrdinalIgnoreCase)) { + var retVar = CommonConversions.GetRetVariableNameOrNull(vbMethodBlock); + if (retVar != null) { + return retVar; + } + } + } + + return await AdjustForImplicitInvocationAsync(node, qualifiedIdentifier); + } + + private async Task AdjustForImplicitInvocationAsync(SyntaxNode node, ExpressionSyntax qualifiedIdentifier) + { + bool nonExecutableNode = node.IsParentKind(VBasic.SyntaxKind.QualifiedName); + if (nonExecutableNode || _semanticModel.SyntaxTree != node.SyntaxTree) return qualifiedIdentifier; + + if (await TryConvertParameterizedPropertyAsync(_semanticModel.GetOperation(node), node, qualifiedIdentifier) is {} invocation) // Assumes TryConvertParameterizedPropertyAsync is accessible + { + return invocation; + } + + return AddEmptyArgumentListIfImplicit(node, qualifiedIdentifier); + } + + private CSharpSyntaxNode AddEmptyArgumentListIfImplicit(SyntaxNode node, ExpressionSyntax id) + { + if (_semanticModel.SyntaxTree != node.SyntaxTree) return id; + return _semanticModel.GetOperation(node) switch { + IInvocationOperation invocation => SyntaxFactory.InvocationExpression(id, CreateArgList(invocation.TargetMethod)), // Assumes CreateArgList is accessible + IPropertyReferenceOperation propReference when propReference.Property.Parameters.Any() => SyntaxFactory.InvocationExpression(id, CreateArgList(propReference.Property)), // Assumes CreateArgList is accessible + _ => id + }; + } + + public override async Task VisitQualifiedName(VBasic.Syntax.QualifiedNameSyntax node) + { + var symbol = GetSymbolInfoInDocument(node); // Assumes GetSymbolInfoInDocument is accessible + if (symbol != null) { + return CommonConversions.GetTypeSyntax(symbol.GetSymbolType()); + } + var lhsSyntax = await node.Left.AcceptAsync(TriviaConvertingExpressionVisitor); + var rhsSyntax = await node.Right.AcceptAsync(TriviaConvertingExpressionVisitor); + + VBasic.Syntax.NameSyntax topLevelName = node; + while (topLevelName.Parent is VBasic.Syntax.NameSyntax parentName) { + topLevelName = parentName; + } + var partOfNamespaceDeclaration = topLevelName.Parent.IsKind(VBasic.SyntaxKind.NamespaceStatement); + var leftIsGlobal = node.Left.IsKind(VBasic.SyntaxKind.GlobalName); + ExpressionSyntax qualifiedName; + if (partOfNamespaceDeclaration || !(lhsSyntax is SimpleNameSyntax sns)) { + if (leftIsGlobal) return rhsSyntax; + qualifiedName = lhsSyntax; + } else { + qualifiedName = QualifyNode(node.Left, sns); + } + + return leftIsGlobal ? SyntaxFactory.AliasQualifiedName((IdentifierNameSyntax)lhsSyntax, rhsSyntax) : + SyntaxFactory.QualifiedName((NameSyntax)qualifiedName, rhsSyntax); + } + + public override async Task VisitGenericName(VBasic.Syntax.GenericNameSyntax node) + { + var symbol = GetSymbolInfoInDocument(node); // Assumes GetSymbolInfoInDocument is accessible + var genericNameSyntax = await GenericNameAccountingForReducedParametersAsync(node, symbol); + return await AdjustForImplicitInvocationAsync(node, genericNameSyntax); + } + + private async Task GenericNameAccountingForReducedParametersAsync(VBSyntax.GenericNameSyntax node, ISymbol symbol) + { + SyntaxToken convertedIdentifier = CommonConversions.ConvertIdentifier(node.Identifier); // Assumes CommonConversions is accessible + if (symbol is IMethodSymbol vbMethod && vbMethod.IsReducedTypeParameterMethod()) { + var allTypeArgs = GetOrNullAllTypeArgsIncludingInferred(vbMethod); + if (allTypeArgs != null) { + return (SimpleNameSyntax)CommonConversions.CsSyntaxGenerator.GenericName(convertedIdentifier.Text, allTypeArgs); + } + var commentedText = "/* " + (await ConvertTypeArgumentListAsync(node)).ToFullString() + " */"; + var error = SyntaxFactory.ParseLeadingTrivia($"#error Conversion error: Could not convert all type parameters, so they've been commented out. Inferred type may be different{Environment.NewLine}"); + var partialConversion = SyntaxFactory.Comment(commentedText); + return ValidSyntaxFactory.IdentifierName(convertedIdentifier).WithPrependedLeadingTrivia(error).WithTrailingTrivia(partialConversion); + } + + return SyntaxFactory.GenericName(convertedIdentifier, await ConvertTypeArgumentListAsync(node)); + } + + private ITypeSymbol[] GetOrNullAllTypeArgsIncludingInferred(IMethodSymbol vbMethod) + { + if (!(CommonConversions.GetCsOriginalSymbolOrNull(vbMethod) is IMethodSymbol csSymbolWithInferredTypeParametersSet)) return null; // Assumes CommonConversions is accessible + var argSubstitutions = vbMethod.TypeParameters + .Zip(vbMethod.TypeArguments, (parameter, arg) => (parameter, arg)) + .ToDictionary(x => x.parameter.Name, x => x.arg); + var allTypeArgs = csSymbolWithInferredTypeParametersSet.GetTypeArguments() + .Select(a => a.Kind == SymbolKind.TypeParameter && argSubstitutions.TryGetValue(a.Name, out var t) ? t : a) + .ToArray(); + return allTypeArgs; + } + + private async Task ConvertTypeArgumentListAsync(VBSyntax.GenericNameSyntax node) + { + return await node.TypeArgumentList.AcceptAsync(TriviaConvertingExpressionVisitor); + } + + public override async Task VisitTypeArgumentList(VBasic.Syntax.TypeArgumentListSyntax node) + { + var args = await node.Arguments.SelectAsync(async a => await a.AcceptAsync(TriviaConvertingExpressionVisitor)); + return SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(args)); + } + + private ExpressionSyntax QualifyNode(SyntaxNode node, SimpleNameSyntax left) + { + var nodeSymbolInfo = GetSymbolInfoInDocument(node); // Assumes GetSymbolInfoInDocument is accessible + if (left != null && + nodeSymbolInfo != null && + nodeSymbolInfo.MatchesKind(SymbolKind.TypeParameter) == false && + nodeSymbolInfo.ContainingSymbol is INamespaceOrTypeSymbol containingSymbol && + !ContextImplicitlyQualfiesSymbol(node, containingSymbol)) { + + if (containingSymbol is ITypeSymbol containingTypeSymbol && + !nodeSymbolInfo.IsConstructor()) { + var qualification = CommonConversions.GetTypeSyntax(containingTypeSymbol); // Assumes CommonConversions is accessible + return Qualify(qualification.ToString(), left); + } + + if (nodeSymbolInfo.IsNamespace()) { + var qualification = containingSymbol.ToCSharpDisplayString(); + return Qualify(qualification, left); + } + } + return left; + } + + private bool ContextImplicitlyQualfiesSymbol(SyntaxNode syntaxNodeContext, INamespaceOrTypeSymbol symbolToCheck) + { + return symbolToCheck is INamespaceSymbol ns && ns.IsGlobalNamespace || + EnclosingTypeImplicitlyQualifiesSymbol(syntaxNodeContext, symbolToCheck); + } + + private bool EnclosingTypeImplicitlyQualifiesSymbol(SyntaxNode syntaxNodeContext, INamespaceOrTypeSymbol symbolToCheck) + { + ISymbol typeContext = syntaxNodeContext.GetEnclosingDeclaredTypeSymbol(_semanticModel); // Assumes _semanticModel is accessible + var implicitCsQualifications = ((ITypeSymbol)typeContext).GetBaseTypesAndThis() + .Concat(typeContext.FollowProperty(n => n.ContainingSymbol)) + .ToList(); + return implicitCsQualifications.Contains(symbolToCheck); + } + + private static QualifiedNameSyntax Qualify(string qualification, ExpressionSyntax toBeQualified) + { + return SyntaxFactory.QualifiedName( + SyntaxFactory.ParseName(qualification), + (SimpleNameSyntax)toBeQualified); + } +} diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.Xml.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.Xml.cs new file mode 100644 index 00000000..978c95be --- /dev/null +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.Xml.cs @@ -0,0 +1,176 @@ +using System.Collections.Immutable; +using System.Data; +using System.Globalization; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using ICSharpCode.CodeConverter.CSharp.Replacements; +using ICSharpCode.CodeConverter.Util.FromRoslyn; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Simplification; +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; +using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; + +namespace ICSharpCode.CodeConverter.CSharp; + +internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor> +{ + private readonly HashSet _extraUsingDirectives; + private readonly XmlImportContext _xmlImportContext; + + public override async Task VisitXmlEmbeddedExpression(VBSyntax.XmlEmbeddedExpressionSyntax node) => + await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); + + public override async Task VisitXmlDocument(VBasic.Syntax.XmlDocumentSyntax node) + { + _extraUsingDirectives.Add("System.Xml.Linq"); + var arguments = SyntaxFactory.SeparatedList( + (await node.PrecedingMisc.SelectAsync(async misc => SyntaxFactory.Argument(await misc.AcceptAsync(TriviaConvertingExpressionVisitor)))) + .Concat(SyntaxFactory.Argument(await node.Root.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield()) + .Concat(await node.FollowingMisc.SelectAsync(async misc => SyntaxFactory.Argument(await misc.AcceptAsync(TriviaConvertingExpressionVisitor)))) + ); + return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XDocument")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); + } + + public override async Task VisitXmlElement(VBasic.Syntax.XmlElementSyntax node) + { + _extraUsingDirectives.Add("System.Xml.Linq"); + var arguments = SyntaxFactory.SeparatedList( + SyntaxFactory.Argument(await node.StartTag.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() + .Concat(await node.StartTag.Attributes.SelectAsync(async attribute => SyntaxFactory.Argument(await attribute.AcceptAsync(TriviaConvertingExpressionVisitor)))) + .Concat(await node.Content.SelectAsync(async content => SyntaxFactory.Argument(await content.AcceptAsync(TriviaConvertingExpressionVisitor)))) + ); + return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XElement")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); + } + + public override async Task VisitXmlEmptyElement(VBSyntax.XmlEmptyElementSyntax node) + { + _extraUsingDirectives.Add("System.Xml.Linq"); + var arguments = SyntaxFactory.SeparatedList( + SyntaxFactory.Argument(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() + .Concat(await node.Attributes.SelectAsync(async attribute => SyntaxFactory.Argument(await attribute.AcceptAsync(TriviaConvertingExpressionVisitor)))) + ); + return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XElement")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); + } + + private CSharpSyntaxNode ApplyXmlImportsIfNecessary(VBSyntax.XmlNodeSyntax vbNode, ObjectCreationExpressionSyntax creation) + { + if (!_xmlImportContext.HasImports || vbNode.Parent is VBSyntax.XmlNodeSyntax) return creation; + return SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, XmlImportContext.HelperClassShortIdentifierName, ValidSyntaxFactory.IdentifierName("Apply")), + SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(creation)))); + } + + public override async Task VisitXmlAttribute(VBasic.Syntax.XmlAttributeSyntax node) + { + var arguments = SyntaxFactory.SeparatedList( + SyntaxFactory.Argument(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() + .Concat(SyntaxFactory.Argument(await node.Value.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield()) + ); + return SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XAttribute")).WithArgumentList(SyntaxFactory.ArgumentList(arguments)); + } + + public override async Task VisitXmlString(VBasic.Syntax.XmlStringSyntax node) => + CommonConversions.Literal(string.Join("", node.TextTokens.Select(b => b.Text))); + + public override async Task VisitXmlText(VBSyntax.XmlTextSyntax node) => + CommonConversions.Literal(string.Join("", node.TextTokens.Select(b => b.Text))); + + public override async Task VisitXmlCDataSection(VBSyntax.XmlCDataSectionSyntax node) + { + var xcDataTypeSyntax = SyntaxFactory.ParseTypeName(nameof(XCData)); + var argumentListSyntax = CommonConversions.Literal(string.Join("", node.TextTokens.Select( b=> b.Text))).Yield().CreateCsArgList(); + return SyntaxFactory.ObjectCreationExpression(xcDataTypeSyntax).WithArgumentList(argumentListSyntax); + } + + /// + /// https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/xml/accessing-xml + /// + public override async Task VisitXmlMemberAccessExpression( + VBasic.Syntax.XmlMemberAccessExpressionSyntax node) + { + _extraUsingDirectives.Add("System.Xml.Linq"); + + var xElementMethodName = GetXElementMethodName(node); + + ExpressionSyntax elements = node.Base != null ? SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + await node.Base.AcceptAsync(TriviaConvertingExpressionVisitor), + ValidSyntaxFactory.IdentifierName(xElementMethodName) + ) : SyntaxFactory.MemberBindingExpression( + ValidSyntaxFactory.IdentifierName(xElementMethodName) + ); + + return SyntaxFactory.InvocationExpression(elements, + ExpressionSyntaxExtensions.CreateArgList( + await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)) + ); + } + + private static string GetXElementMethodName(VBSyntax.XmlMemberAccessExpressionSyntax node) + { + if (node.Token2 == default(SyntaxToken)) { + return "Elements"; + } + + if (node.Token2.Text == "@") { + return "Attributes"; + } + + if (node.Token2.Text == ".") { + return "Descendants"; + } + throw new NotImplementedException($"Xml member access operator: '{node.Token1}{node.Token2}{node.Token3}'"); + } + + public override Task VisitXmlBracketedName(VBSyntax.XmlBracketedNameSyntax node) + { + return node.Name.AcceptAsync(TriviaConvertingExpressionVisitor); + } + + public override async Task VisitXmlName(VBSyntax.XmlNameSyntax node) + { + if (node.Prefix != null) { + switch (node.Prefix.Name.ValueText) { + case "xml": + case "xmlns": + return SyntaxFactory.BinaryExpression( + SyntaxKind.AddExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ValidSyntaxFactory.IdentifierName("XNamespace"), + ValidSyntaxFactory.IdentifierName(node.Prefix.Name.ValueText.ToPascalCase()) + ), + SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) + ); + default: + return SyntaxFactory.BinaryExpression( + SyntaxKind.AddExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + XmlImportContext.HelperClassShortIdentifierName, + ValidSyntaxFactory.IdentifierName(node.Prefix.Name.ValueText) + ), + SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) + ); + } + } + + if (_xmlImportContext.HasDefaultImport && node.Parent is not VBSyntax.XmlAttributeSyntax) { + return SyntaxFactory.BinaryExpression( + SyntaxKind.AddExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + XmlImportContext.HelperClassShortIdentifierName, + XmlImportContext.DefaultIdentifierName + ), + SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) + ); + } + + return SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)); + } +} diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.cs index dc1eb31e..80fd8252 100644 --- a/CodeConverter/CSharp/ExpressionNodeVisitor.cs +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.cs @@ -13,6 +13,13 @@ using Microsoft.VisualBasic; using Microsoft.VisualBasic.CompilerServices; using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; +// Required for Task +using System.Threading.Tasks; +// Required for NotImplementedException +using System; +using System.Linq; +using System.Collections.Generic; +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace ICSharpCode.CodeConverter.CSharp; @@ -22,23 +29,23 @@ namespace ICSharpCode.CodeConverter.CSharp; /// http://source.roslyn.codeplex.com/#Microsoft.CodeAnalysis.CSharp/Binder/Binder_Expressions.cs,365 /// http://source.roslyn.codeplex.com/#Microsoft.CodeAnalysis.VisualBasic/Binding/Binder_Expressions.vb,43 /// -internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor> +internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor> { private static readonly Type ConvertType = typeof(Conversions); public CommentConvertingVisitorWrapper TriviaConvertingExpressionVisitor { get; } private readonly SemanticModel _semanticModel; private readonly HashSet _extraUsingDirectives; private readonly XmlImportContext _xmlImportContext; + private readonly ITypeContext _typeContext; + + // Fields defined in other partial classes, but initialized by the constructor here + private readonly LambdaConverter _lambdaConverter; private readonly IOperatorConverter _operatorConverter; private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison; - private readonly Stack _withBlockLhs = new(); - private readonly ITypeContext _typeContext; private readonly QueryConverter _queryConverter; private readonly Lazy> _convertMethodsLookupByReturnType; - private readonly LambdaConverter _lambdaConverter; private readonly VisualBasicNullableExpressionsConverter _visualBasicNullableTypesConverter; - private readonly Dictionary> _tempNameForAnonymousScope = new(); - private readonly HashSet _generatedNames = new(StringComparer.OrdinalIgnoreCase); + public ExpressionNodeVisitor(SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison, ITypeContext typeContext, CommonConversions commonConversions, @@ -55,7 +62,6 @@ public ExpressionNodeVisitor(SemanticModel semanticModel, _xmlImportContext = xmlImportContext; _visualBasicNullableTypesConverter = visualBasicNullableTypesConverter; _operatorConverter = VbOperatorConversion.Create(TriviaConvertingExpressionVisitor, semanticModel, visualBasicEqualityComparison, commonConversions.TypeConversionAnalyzer); - // If this isn't needed, the assembly with Conversions may not be referenced, so this must be done lazily _convertMethodsLookupByReturnType = new Lazy>(() => CreateConvertMethodsLookupByReturnType(semanticModel)); } @@ -63,7 +69,6 @@ public ExpressionNodeVisitor(SemanticModel semanticModel, private static IReadOnlyDictionary CreateConvertMethodsLookupByReturnType( SemanticModel semanticModel) { - // In some projects there's a source declaration as well as the referenced one, which causes the first of these methods to fail var symbolsWithName = semanticModel.Compilation .GetSymbolsWithName(n => n.Equals(ConvertType.Name, StringComparison.Ordinal), SymbolFilter.Type).ToList(); @@ -77,7 +82,7 @@ private static IReadOnlyDictionary CreateConvertMethodsLook var convertMethods = convertType.GetMembers().Where(m => m.Name.StartsWith("To", StringComparison.Ordinal) && m.GetParameters().Length == 1); -#pragma warning disable RS1024 // Compare symbols correctly - GroupBy and ToDictionary use the same logic to dedupe as to lookup, so it doesn't matter which equality is used +#pragma warning disable RS1024 // Compare symbols correctly var methodsByType = convertMethods .GroupBy(m => new { ReturnType = m.GetReturnType(), Name = $"{ConvertType.FullName}.{m.Name}" }) .ToDictionary(m => m.Key.ReturnType, m => m.Key.Name); @@ -95,195 +100,35 @@ public override async Task DefaultVisit(SyntaxNode node) .WithNodeInformation(node); } - public override async Task VisitXmlEmbeddedExpression(VBSyntax.XmlEmbeddedExpressionSyntax node) => - await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - - public override async Task VisitXmlDocument(VBasic.Syntax.XmlDocumentSyntax node) - { - _extraUsingDirectives.Add("System.Xml.Linq"); - var arguments = SyntaxFactory.SeparatedList( - (await node.PrecedingMisc.SelectAsync(async misc => SyntaxFactory.Argument(await misc.AcceptAsync(TriviaConvertingExpressionVisitor)))) - .Concat(SyntaxFactory.Argument(await node.Root.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield()) - .Concat(await node.FollowingMisc.SelectAsync(async misc => SyntaxFactory.Argument(await misc.AcceptAsync(TriviaConvertingExpressionVisitor)))) - ); - return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XDocument")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); - } - - public override async Task VisitXmlElement(VBasic.Syntax.XmlElementSyntax node) - { - _extraUsingDirectives.Add("System.Xml.Linq"); - var arguments = SyntaxFactory.SeparatedList( - SyntaxFactory.Argument(await node.StartTag.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() - .Concat(await node.StartTag.Attributes.SelectAsync(async attribute => SyntaxFactory.Argument(await attribute.AcceptAsync(TriviaConvertingExpressionVisitor)))) - .Concat(await node.Content.SelectAsync(async content => SyntaxFactory.Argument(await content.AcceptAsync(TriviaConvertingExpressionVisitor)))) - ); - return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XElement")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); - } - - public override async Task VisitXmlEmptyElement(VBSyntax.XmlEmptyElementSyntax node) - { - _extraUsingDirectives.Add("System.Xml.Linq"); - var arguments = SyntaxFactory.SeparatedList( - SyntaxFactory.Argument(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() - .Concat(await node.Attributes.SelectAsync(async attribute => SyntaxFactory.Argument(await attribute.AcceptAsync(TriviaConvertingExpressionVisitor)))) - ); - return ApplyXmlImportsIfNecessary(node, SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XElement")).WithArgumentList(SyntaxFactory.ArgumentList(arguments))); - } - - private CSharpSyntaxNode ApplyXmlImportsIfNecessary(VBSyntax.XmlNodeSyntax vbNode, ObjectCreationExpressionSyntax creation) - { - if (!_xmlImportContext.HasImports || vbNode.Parent is VBSyntax.XmlNodeSyntax) return creation; - return SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, XmlImportContext.HelperClassShortIdentifierName, ValidSyntaxFactory.IdentifierName("Apply")), - SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(creation)))); - } - - public override async Task VisitXmlAttribute(VBasic.Syntax.XmlAttributeSyntax node) - { - var arguments = SyntaxFactory.SeparatedList( - SyntaxFactory.Argument(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield() - .Concat(SyntaxFactory.Argument(await node.Value.AcceptAsync(TriviaConvertingExpressionVisitor)).Yield()) - ); - return SyntaxFactory.ObjectCreationExpression(ValidSyntaxFactory.IdentifierName("XAttribute")).WithArgumentList(SyntaxFactory.ArgumentList(arguments)); - } - - public override async Task VisitXmlString(VBasic.Syntax.XmlStringSyntax node) => - CommonConversions.Literal(string.Join("", node.TextTokens.Select(b => b.Text))); - - public override async Task VisitXmlText(VBSyntax.XmlTextSyntax node) => - CommonConversions.Literal(string.Join("", node.TextTokens.Select(b => b.Text))); - - public override async Task VisitXmlCDataSection(VBSyntax.XmlCDataSectionSyntax node) - { - var xcDataTypeSyntax = SyntaxFactory.ParseTypeName(nameof(XCData)); - var argumentListSyntax = CommonConversions.Literal(string.Join("", node.TextTokens.Select( b=> b.Text))).Yield().CreateCsArgList(); - return SyntaxFactory.ObjectCreationExpression(xcDataTypeSyntax).WithArgumentList(argumentListSyntax); - } - - /// - /// https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/xml/accessing-xml - /// - public override async Task VisitXmlMemberAccessExpression( - VBasic.Syntax.XmlMemberAccessExpressionSyntax node) - { - _extraUsingDirectives.Add("System.Xml.Linq"); - - var xElementMethodName = GetXElementMethodName(node); - - ExpressionSyntax elements = node.Base != null ? SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - await node.Base.AcceptAsync(TriviaConvertingExpressionVisitor), - ValidSyntaxFactory.IdentifierName(xElementMethodName) - ) : SyntaxFactory.MemberBindingExpression( - ValidSyntaxFactory.IdentifierName(xElementMethodName) - ); - - return SyntaxFactory.InvocationExpression(elements, - ExpressionSyntaxExtensions.CreateArgList( - await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor)) - ); - } - - private static string GetXElementMethodName(VBSyntax.XmlMemberAccessExpressionSyntax node) - { - if (node.Token2 == default(SyntaxToken)) { - return "Elements"; - } - - if (node.Token2.Text == "@") { - return "Attributes"; - } - - if (node.Token2.Text == ".") { - return "Descendants"; - } - throw new NotImplementedException($"Xml member access operator: '{node.Token1}{node.Token2}{node.Token3}'"); - } - - public override Task VisitXmlBracketedName(VBSyntax.XmlBracketedNameSyntax node) - { - return node.Name.AcceptAsync(TriviaConvertingExpressionVisitor); - } - - public override async Task VisitXmlName(VBSyntax.XmlNameSyntax node) - { - if (node.Prefix != null) { - switch (node.Prefix.Name.ValueText) { - case "xml": - case "xmlns": - return SyntaxFactory.BinaryExpression( - SyntaxKind.AddExpression, - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - ValidSyntaxFactory.IdentifierName("XNamespace"), - ValidSyntaxFactory.IdentifierName(node.Prefix.Name.ValueText.ToPascalCase()) - ), - SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) - ); - default: - return SyntaxFactory.BinaryExpression( - SyntaxKind.AddExpression, - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - XmlImportContext.HelperClassShortIdentifierName, - ValidSyntaxFactory.IdentifierName(node.Prefix.Name.ValueText) - ), - SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) - ); - } - } - - if (_xmlImportContext.HasDefaultImport && node.Parent is not VBSyntax.XmlAttributeSyntax) { - return SyntaxFactory.BinaryExpression( - SyntaxKind.AddExpression, - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - XmlImportContext.HelperClassShortIdentifierName, - XmlImportContext.DefaultIdentifierName - ), - SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)) - ); - } - - return SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(node.LocalName.Text)); - } - - public override async Task VisitGetTypeExpression(VBasic.Syntax.GetTypeExpressionSyntax node) - { - return SyntaxFactory.TypeOfExpression(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitGlobalName(VBasic.Syntax.GlobalNameSyntax node) - { - return ValidSyntaxFactory.IdentifierName(SyntaxFactory.Token(SyntaxKind.GlobalKeyword)); - } - public override async Task VisitAwaitExpression(VBasic.Syntax.AwaitExpressionSyntax node) { return SyntaxFactory.AwaitExpression(await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)); } + // Cast-related methods remain here as they call helpers now in Expressions.cs public override async Task VisitCTypeExpression(VBasic.Syntax.CTypeExpressionSyntax node) { var csharpArg = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); var typeInfo = _semanticModel.GetTypeInfo(node.Type); var forceTargetType = typeInfo.ConvertedType; + // Relies on ConvertCastExpressionAsync in Expressions.cs - this call should resolve via partial class return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, csharpArg, forceTargetType: forceTargetType, defaultToCast: true).AddParens(); } public override async Task VisitDirectCastExpression(VBasic.Syntax.DirectCastExpressionSyntax node) { + // Relies on ConvertCastExpressionAsync in Expressions.cs return await ConvertCastExpressionAsync(node, castToOrNull: node.Type); } public override async Task VisitPredefinedCastExpression(VBasic.Syntax.PredefinedCastExpressionSyntax node) { + // Relies on WithRemovedRedundantConversionOrNullAsync in Expressions.cs var simplifiedOrNull = await WithRemovedRedundantConversionOrNullAsync(node, node.Expression); if (simplifiedOrNull != null) return simplifiedOrNull; var expressionSyntax = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); if (node.Keyword.IsKind(VBasic.SyntaxKind.CDateKeyword)) { - _extraUsingDirectives.Add("Microsoft.VisualBasic.CompilerServices"); return SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression("Conversions.ToDate"), SyntaxFactory.ArgumentList( SyntaxFactory.SingletonSeparatedList( @@ -291,7 +136,7 @@ public override async Task VisitPredefinedCastExpression(VBasi } var withConversion = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, expressionSyntax, false, true, forceTargetType: _semanticModel.GetTypeInfo(node).Type); - return node.ParenthesizeIfPrecedenceCouldChange(withConversion); // Use context of outer node, rather than just its exprssion, as the above method call would do if allowed to add parenthesis + return node.ParenthesizeIfPrecedenceCouldChange(withConversion); } public override async Task VisitTryCastExpression(VBasic.Syntax.TryCastExpressionSyntax node) @@ -302,1862 +147,10 @@ await node.Expression.AcceptAsync(TriviaConvertingExpressionVi await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor) )); } + + // General Helpers + private SyntaxToken ConvertIdentifier(SyntaxToken identifierIdentifier, bool isAttribute = false) => CommonConversions.ConvertIdentifier(identifierIdentifier, isAttribute); + private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier) => expr.ReplaceToken(idToken, SyntaxFactory.Identifier(overrideIdentifier).WithTriviaFrom(idToken).WithAdditionalAnnotations(idToken.GetAnnotations())); - public override async Task VisitLiteralExpression(VBasic.Syntax.LiteralExpressionSyntax node) - { - var typeInfo = _semanticModel.GetTypeInfo(node); - var convertedType = typeInfo.ConvertedType; - if (node.Token.Value == null) { - if (convertedType == null) { - return SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression); - } - - return !convertedType.IsReferenceType ? SyntaxFactory.DefaultExpression(CommonConversions.GetTypeSyntax(convertedType)) : CommonConversions.Literal(null); - } - - if (TypeConversionAnalyzer.ConvertStringToCharLiteral(node, convertedType, out char chr)) { - return CommonConversions.Literal(chr); - } - - - var val = node.Token.Value; - var text = node.Token.Text; - if (_typeContext.Any() && CommonConversions.WinformsConversions.ShouldPrefixAssignedNameWithUnderscore(node.Parent as VBSyntax.AssignmentStatementSyntax) && val is string valStr) { - val = "_" + valStr; - text = "\"_" + valStr + "\""; - } - - return CommonConversions.Literal(val, text, convertedType); - } - - public override async Task VisitInterpolation(VBasic.Syntax.InterpolationSyntax node) - { - return SyntaxFactory.Interpolation(await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor), await node.AlignmentClause.AcceptAsync(TriviaConvertingExpressionVisitor), await node.FormatClause.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitInterpolatedStringExpression(VBasic.Syntax.InterpolatedStringExpressionSyntax node) - { - var useVerbatim = node.DescendantNodes().OfType().Any(c => LiteralConversions.IsWorthBeingAVerbatimString(c.TextToken.Text)); - var startToken = useVerbatim ? - SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedVerbatimStringStartToken, "$@\"", "$@\"", default(SyntaxTriviaList)) - : SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedStringStartToken, "$\"", "$\"", default(SyntaxTriviaList)); - var contents = await node.Contents.SelectAsync(async c => await c.AcceptAsync(TriviaConvertingExpressionVisitor)); - InterpolatedStringExpressionSyntax interpolatedStringExpressionSyntax = SyntaxFactory.InterpolatedStringExpression(startToken, SyntaxFactory.List(contents), SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken)); - return interpolatedStringExpressionSyntax; - } - - public override async Task VisitInterpolatedStringText(VBasic.Syntax.InterpolatedStringTextSyntax node) - { - var useVerbatim = node.Parent.DescendantNodes().OfType().Any(c => LiteralConversions.IsWorthBeingAVerbatimString(c.TextToken.Text)); - var textForUser = LiteralConversions.EscapeQuotes(node.TextToken.Text, node.TextToken.ValueText, useVerbatim); - InterpolatedStringTextSyntax interpolatedStringTextSyntax = SyntaxFactory.InterpolatedStringText(SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.InterpolatedStringTextToken, textForUser, node.TextToken.ValueText, default(SyntaxTriviaList))); - return interpolatedStringTextSyntax; - } - - public override async Task VisitInterpolationAlignmentClause(VBasic.Syntax.InterpolationAlignmentClauseSyntax node) - { - return SyntaxFactory.InterpolationAlignmentClause(SyntaxFactory.Token(SyntaxKind.CommaToken), await node.Value.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitInterpolationFormatClause(VBasic.Syntax.InterpolationFormatClauseSyntax node) - { - var textForUser = LiteralConversions.EscapeEscapeChar(node.FormatStringToken.ValueText); - SyntaxToken formatStringToken = SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.InterpolatedStringTextToken, textForUser, node.FormatStringToken.ValueText, SyntaxTriviaList.Empty); - return SyntaxFactory.InterpolationFormatClause(SyntaxFactory.Token(SyntaxKind.ColonToken), formatStringToken); - } - - public override async Task VisitMeExpression(VBasic.Syntax.MeExpressionSyntax node) - { - return SyntaxFactory.ThisExpression(); - } - - public override async Task VisitMyBaseExpression(VBasic.Syntax.MyBaseExpressionSyntax node) - { - return SyntaxFactory.BaseExpression(); - } - - public override async Task VisitParenthesizedExpression(VBasic.Syntax.ParenthesizedExpressionSyntax node) - { - var cSharpSyntaxNode = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - // If structural changes are necessary the expression may have been lifted a statement (e.g. Type inferred lambda) - return cSharpSyntaxNode is ExpressionSyntax expr ? SyntaxFactory.ParenthesizedExpression(expr) : cSharpSyntaxNode; - } - - public override async Task VisitMemberAccessExpression(VBasic.Syntax.MemberAccessExpressionSyntax node) - { - var nodeSymbol = GetSymbolInfoInDocument(node.Name); - - if (!node.IsParentKind(VBasic.SyntaxKind.InvocationExpression) && - SimpleMethodReplacement.TryGet(nodeSymbol, out var methodReplacement) && - methodReplacement.ReplaceIfMatches(nodeSymbol, Array.Empty(), node.IsParentKind(VBasic.SyntaxKind.AddressOfExpression)) is {} replacement) { - return replacement; - } - - var simpleNameSyntax = await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor); - - var isDefaultProperty = nodeSymbol is IPropertySymbol p && VBasic.VisualBasicExtensions.IsDefault(p); - ExpressionSyntax left = null; - if (node.Expression is VBasic.Syntax.MyClassExpressionSyntax && nodeSymbol != null) { - if (nodeSymbol.IsStatic) { - var typeInfo = _semanticModel.GetTypeInfo(node.Expression); - left = CommonConversions.GetTypeSyntax(typeInfo.Type); - } else { - left = SyntaxFactory.ThisExpression(); - if (nodeSymbol.IsVirtual && !nodeSymbol.IsAbstract || - nodeSymbol.IsImplicitlyDeclared && nodeSymbol is IFieldSymbol { AssociatedSymbol: IPropertySymbol { IsVirtual: true, IsAbstract: false } }) { - simpleNameSyntax = - ValidSyntaxFactory.IdentifierName( - $"MyClass{ConvertIdentifier(node.Name.Identifier).ValueText}"); - } - } - } - if (left == null && nodeSymbol?.IsStatic == true) { - var type = nodeSymbol.ContainingType; - if (type != null) { - left = CommonConversions.GetTypeSyntax(type); - } - } - if (left == null) { - left = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - if (left != null && _semanticModel.GetSymbolInfo(node) is {CandidateReason: CandidateReason.LateBound, CandidateSymbols.Length: 0} - && _semanticModel.GetSymbolInfo(node.Expression).Symbol is {Kind: var expressionSymbolKind} - && expressionSymbolKind != SymbolKind.ErrorType - && _semanticModel.GetOperation(node) is IDynamicMemberReferenceOperation) { - left = SyntaxFactory.ParenthesizedExpression(SyntaxFactory.CastExpression(SyntaxFactory.ParseTypeName("dynamic"), left)); - } - } - if (left == null) { - if (IsSubPartOfConditionalAccess(node)) { - return isDefaultProperty ? SyntaxFactory.ElementBindingExpression() - : await AdjustForImplicitInvocationAsync(node, SyntaxFactory.MemberBindingExpression(simpleNameSyntax)); - } else if (node.IsParentKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NamedFieldInitializer)) { - return ValidSyntaxFactory.IdentifierName(_tempNameForAnonymousScope[node.Name.Identifier.Text].Peek().TempName); - } - left = _withBlockLhs.Peek(); - } - - if (node.IsKind(VBasic.SyntaxKind.DictionaryAccessExpression)) { - var args = SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(CommonConversions.Literal(node.Name.Identifier.ValueText))); - var bracketedArgumentListSyntax = SyntaxFactory.BracketedArgumentList(args); - return SyntaxFactory.ElementAccessExpression(left, bracketedArgumentListSyntax); - } - - if (node.Expression.IsKind(VBasic.SyntaxKind.GlobalName)) { - return SyntaxFactory.AliasQualifiedName((IdentifierNameSyntax)left, simpleNameSyntax); - } - - if (isDefaultProperty && left != null) { - return left; - } - - var memberAccessExpressionSyntax = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, left, simpleNameSyntax); - return await AdjustForImplicitInvocationAsync(node, memberAccessExpressionSyntax); - } - - public override async Task VisitConditionalAccessExpression(VBasic.Syntax.ConditionalAccessExpressionSyntax node) - { - var leftExpression = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor) ?? _withBlockLhs.Peek(); - return SyntaxFactory.ConditionalAccessExpression(leftExpression, await node.WhenNotNull.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitArgumentList(VBasic.Syntax.ArgumentListSyntax node) - { - if (node.Parent.IsKind(VBasic.SyntaxKind.Attribute)) { - return CommonConversions.CreateAttributeArgumentList(await node.Arguments.SelectAsync(ToAttributeArgumentAsync)); - } - var argumentSyntaxes = await ConvertArgumentsAsync(node); - return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(argumentSyntaxes)); - } - - public override async Task VisitSimpleArgument(VBasic.Syntax.SimpleArgumentSyntax node) - { - var argList = (VBasic.Syntax.ArgumentListSyntax)node.Parent; - var invocation = argList.Parent; - if (invocation is VBasic.Syntax.ArrayCreationExpressionSyntax) - return await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - var symbol = GetInvocationSymbol(invocation); - SyntaxToken token = default(SyntaxToken); - var convertedArgExpression = (await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)).SkipIntoParens(); - var typeConversionAnalyzer = CommonConversions.TypeConversionAnalyzer; - var baseSymbol = symbol?.OriginalDefinition.GetBaseSymbol(); - var possibleParameters = (CommonConversions.GetCsOriginalSymbolOrNull(baseSymbol) ?? symbol)?.GetParameters(); - if (possibleParameters.HasValue) { - var refType = GetRefConversionType(node, argList, possibleParameters.Value, out var argName, out var refKind); - token = GetRefToken(refKind); - if (refType != RefConversion.Inline) { - convertedArgExpression = HoistByRefDeclaration(node, convertedArgExpression, refType, argName, refKind); - } else { - convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression, defaultToCast: refKind != RefKind.None); - } - } else { - convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression); - } - - var nameColon = node.IsNamed ? SyntaxFactory.NameColon(await node.NameColonEquals.Name.AcceptAsync(TriviaConvertingExpressionVisitor)) : null; - return SyntaxFactory.Argument(nameColon, token, convertedArgExpression); - } - - private ExpressionSyntax HoistByRefDeclaration(VBSyntax.SimpleArgumentSyntax node, ExpressionSyntax refLValue, RefConversion refType, string argName, RefKind refKind) - { - string prefix = $"arg{argName}"; - var expressionTypeInfo = _semanticModel.GetTypeInfo(node.Expression); - bool useVar = expressionTypeInfo.Type?.Equals(expressionTypeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability) == true && !CommonConversions.ShouldPreferExplicitType(node.Expression, expressionTypeInfo.ConvertedType, out var _); - var typeSyntax = CommonConversions.GetTypeSyntax(expressionTypeInfo.ConvertedType, useVar); - - if (refLValue is ElementAccessExpressionSyntax eae) { - //Hoist out the container so we can assign back to the same one after (like VB does) - var tmpContainer = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration("tmp", eae.Expression, ValidSyntaxFactory.VarType)); - refLValue = eae.WithExpression(tmpContainer.IdentifierName); - } - - var withCast = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, refLValue, defaultToCast: refKind != RefKind.None); - - var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, withCast, typeSyntax)); - - if (refType == RefConversion.PreAndPostAssignment) { - var convertedLocalIdentifier = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, local.IdentifierName, forceSourceType: expressionTypeInfo.ConvertedType, forceTargetType: expressionTypeInfo.Type); - _typeContext.PerScopeState.Hoist(new AdditionalAssignment(refLValue, convertedLocalIdentifier)); - } - - return local.IdentifierName; - } - - private static SyntaxToken GetRefToken(RefKind refKind) - { - SyntaxToken token; - switch (refKind) { - case RefKind.None: - token = default(SyntaxToken); - break; - case RefKind.Ref: - token = SyntaxFactory.Token(SyntaxKind.RefKeyword); - break; - case RefKind.Out: - token = SyntaxFactory.Token(SyntaxKind.OutKeyword); - break; - default: - throw new ArgumentOutOfRangeException(nameof(refKind), refKind, null); - } - - return token; - } - - private RefConversion GetRefConversionType(VBSyntax.ArgumentSyntax node, VBSyntax.ArgumentListSyntax argList, ImmutableArray parameters, out string argName, out RefKind refKind) - { - var parameter = node.IsNamed && node is VBSyntax.SimpleArgumentSyntax sas - ? parameters.FirstOrDefault(p => p.Name.Equals(sas.NameColonEquals.Name.Identifier.Text, StringComparison.OrdinalIgnoreCase)) - : parameters.ElementAtOrDefault(argList.Arguments.IndexOf(node)); - if (parameter != null) { - refKind = parameter.RefKind; - argName = parameter.Name; - } else { - refKind = RefKind.None; - argName = null; - } - return NeedsVariableForArgument(node, refKind); - } - - public override async Task VisitNameOfExpression(VBasic.Syntax.NameOfExpressionSyntax node) - { - return SyntaxFactory.InvocationExpression(ValidSyntaxFactory.NameOf(), SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(await node.Argument.AcceptAsync(TriviaConvertingExpressionVisitor))))); - } - - public override async Task VisitEqualsValue(VBasic.Syntax.EqualsValueSyntax node) - { - return SyntaxFactory.EqualsValueClause(await node.Value.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitAnonymousObjectCreationExpression(VBasic.Syntax.AnonymousObjectCreationExpressionSyntax node) - { - var vbInitializers = node.Initializer.Initializers; - try { - var initializers = await vbInitializers.AcceptSeparatedListAsync(TriviaConvertingExpressionVisitor); - return SyntaxFactory.AnonymousObjectCreationExpression(initializers); - } finally { - var kvpsToPop = _tempNameForAnonymousScope.Where(t => t.Value.Peek().Scope == node).ToArray(); - foreach (var kvp in kvpsToPop) { - if (kvp.Value.Count == 1) _tempNameForAnonymousScope.Remove(kvp.Key); - else kvp.Value.Pop(); - } - } - - } - - public override async Task VisitInferredFieldInitializer(VBasic.Syntax.InferredFieldInitializerSyntax node) - { - return SyntaxFactory.AnonymousObjectMemberDeclarator(await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitObjectCreationExpression(VBasic.Syntax.ObjectCreationExpressionSyntax node) - { - - var objectCreationExpressionSyntax = SyntaxFactory.ObjectCreationExpression( - await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor), - // VB can omit empty arg lists: - await ConvertArgumentListOrEmptyAsync(node, node.ArgumentList), - null - ); - async Task ConvertInitializer() => await node.Initializer.AcceptAsync(TriviaConvertingExpressionVisitor); - if (node.Initializer is VBSyntax.ObjectMemberInitializerSyntax objectMemberInitializerSyntax && HasInitializersUsingImpliedLhs(objectMemberInitializerSyntax)) { - - var idToUse = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration("init", objectCreationExpressionSyntax, CommonConversions.GetTypeSyntax(_semanticModel.GetTypeInfo(node).Type))).IdentifierName; - _withBlockLhs.Push(idToUse); - try { - var initializer = await ConvertInitializer(); - var originalExpressions = initializer.Expressions.Select(x => x is AssignmentExpressionSyntax e ? e.ReplaceNode(e.Left, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, idToUse, (SimpleNameSyntax) e.Left)) : null).ToArray(); - var expressions = SyntaxFactory.SeparatedList(originalExpressions.Append(idToUse).Select(SyntaxFactory.Argument)); - var tuple = SyntaxFactory.TupleExpression(expressions); - return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, tuple, idToUse); - } finally { - _withBlockLhs.Pop(); - } - } - return objectCreationExpressionSyntax.WithInitializer(await ConvertInitializer()); - } - - private static bool HasInitializersUsingImpliedLhs(VBSyntax.ObjectMemberInitializerSyntax objectMemberInitializerSyntax) - { - return objectMemberInitializerSyntax.Initializers.SelectMany(i => i.ChildNodes().Skip(1), (_, c) => c.DescendantNodesAndSelf()).SelectMany(d => d).OfType().Any(x => x.Expression is null); - } - - public override async Task VisitArrayCreationExpression(VBasic.Syntax.ArrayCreationExpressionSyntax node) - { - var bounds = await CommonConversions.ConvertArrayRankSpecifierSyntaxesAsync(node.RankSpecifiers, node.ArrayBounds); - - var allowInitializer = node.ArrayBounds?.Arguments.Any() != true || - node.Initializer.Initializers.Any() && node.ArrayBounds.Arguments.All(b => b.IsOmitted || _semanticModel.GetConstantValue(b.GetExpression()).HasValue); - - var initializerToConvert = allowInitializer ? node.Initializer : null; - return SyntaxFactory.ArrayCreationExpression( - SyntaxFactory.ArrayType(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor), bounds), - await initializerToConvert.AcceptAsync(TriviaConvertingExpressionVisitor) - ); - } - - /// Collection initialization has many variants in both VB and C#. Please add especially many test cases when touching this. - public override async Task VisitCollectionInitializer(VBasic.Syntax.CollectionInitializerSyntax node) - { - var isExplicitCollectionInitializer = node.Parent is VBasic.Syntax.ObjectCollectionInitializerSyntax - || node.Parent is VBasic.Syntax.CollectionInitializerSyntax - || node.Parent is VBasic.Syntax.ArrayCreationExpressionSyntax; - var initializerKind = node.IsParentKind(VBasic.SyntaxKind.ObjectCollectionInitializer) || node.IsParentKind(VBasic.SyntaxKind.ObjectCreationExpression) ? - SyntaxKind.CollectionInitializerExpression : - node.IsParentKind(VBasic.SyntaxKind.CollectionInitializer) && IsComplexInitializer(node) ? SyntaxKind.ComplexElementInitializerExpression : - SyntaxKind.ArrayInitializerExpression; - var initializers = (await node.Initializers.SelectAsync(async i => { - var convertedInitializer = await i.AcceptAsync(TriviaConvertingExpressionVisitor); - return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(i, convertedInitializer, false); - })); - var initializer = SyntaxFactory.InitializerExpression(initializerKind, SyntaxFactory.SeparatedList(initializers)); - if (isExplicitCollectionInitializer) return initializer; - - var convertedType = _semanticModel.GetTypeInfo(node).ConvertedType; - var dimensions = convertedType is IArrayTypeSymbol ats ? ats.Rank : 1; // For multidimensional array [,] note these are different from nested arrays [][] - if (!(convertedType.GetEnumerableElementTypeOrDefault() is {} elementType)) return SyntaxFactory.ImplicitArrayCreationExpression(initializer); - - if (!initializers.Any() && dimensions == 1) { - var arrayTypeArgs = SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(CommonConversions.GetTypeSyntax(elementType))); - var arrayEmpty = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - ValidSyntaxFactory.IdentifierName(nameof(Array)), SyntaxFactory.GenericName(nameof(Array.Empty)).WithTypeArgumentList(arrayTypeArgs)); - return SyntaxFactory.InvocationExpression(arrayEmpty); - } - - bool hasExpressionToInferTypeFrom = node.Initializers.SelectMany(n => n.DescendantNodesAndSelf()).Any(n => n is not VBasic.Syntax.CollectionInitializerSyntax); - if (hasExpressionToInferTypeFrom) { - var commas = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), dimensions - 1); - return SyntaxFactory.ImplicitArrayCreationExpression(SyntaxFactory.TokenList(commas), initializer); - } - - var arrayType = (ArrayTypeSyntax)CommonConversions.CsSyntaxGenerator.ArrayTypeExpression(CommonConversions.GetTypeSyntax(elementType)); - var sizes = Enumerable.Repeat(SyntaxFactory.OmittedArraySizeExpression(), dimensions); - var arrayRankSpecifierSyntax = SyntaxFactory.SingletonList(SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(sizes))); - arrayType = arrayType.WithRankSpecifiers(arrayRankSpecifierSyntax); - return SyntaxFactory.ArrayCreationExpression(arrayType, initializer); - } - - private bool IsComplexInitializer(VBSyntax.CollectionInitializerSyntax node) - { - return _semanticModel.GetOperation(node.Parent.Parent) is IObjectOrCollectionInitializerOperation initializer && - initializer.Initializers.OfType().Any(); - } - - public override async Task VisitQueryExpression(VBasic.Syntax.QueryExpressionSyntax node) - { - return await _queryConverter.ConvertClausesAsync(node.Clauses); - } - - public override async Task VisitOrdering(VBasic.Syntax.OrderingSyntax node) - { - var convertToken = node.Kind().ConvertToken(); - var expressionSyntax = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - var ascendingOrDescendingKeyword = node.AscendingOrDescendingKeyword.ConvertToken(); - return SyntaxFactory.Ordering(convertToken, expressionSyntax, ascendingOrDescendingKeyword); - } - - public override async Task VisitObjectMemberInitializer(VBasic.Syntax.ObjectMemberInitializerSyntax node) - { - var initializers = await node.Initializers.AcceptSeparatedListAsync(TriviaConvertingExpressionVisitor); - return SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializers); - } - - public override async Task VisitNamedFieldInitializer(VBasic.Syntax.NamedFieldInitializerSyntax node) - { - var csExpressionSyntax = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - csExpressionSyntax = - CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, csExpressionSyntax); - if (node.Parent?.Parent is VBasic.Syntax.AnonymousObjectCreationExpressionSyntax {Initializer: {Initializers: var initializers}} anonymousObjectCreationExpression) { - string nameIdentifierText = node.Name.Identifier.Text; - var isAnonymouslyReused = initializers.OfType() - .Select(i => i.Expression).OfType() - .Any(maes => maes.Expression is null && maes.Name.Identifier.Text.Equals(nameIdentifierText, StringComparison.OrdinalIgnoreCase)); - if (isAnonymouslyReused) { - string tempNameForAnonymousSelfReference = GenerateUniqueVariableName(node.Name, "temp" + ((VBSyntax.SimpleNameSyntax) node.Name).Identifier.Text.UppercaseFirstLetter()); - csExpressionSyntax = DeclareVariableInline(csExpressionSyntax, tempNameForAnonymousSelfReference); - if (!_tempNameForAnonymousScope.TryGetValue(nameIdentifierText, out var stack)) { - stack = _tempNameForAnonymousScope[nameIdentifierText] = new Stack<(SyntaxNode Scope, string TempName)>(); - } - stack.Push((anonymousObjectCreationExpression, tempNameForAnonymousSelfReference)); - } - - var anonymousObjectMemberDeclaratorSyntax = SyntaxFactory.AnonymousObjectMemberDeclarator( - SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(ConvertIdentifier(node.Name.Identifier))), - csExpressionSyntax); - return anonymousObjectMemberDeclaratorSyntax; - } - - return SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor), - csExpressionSyntax - ); - } - - private string GenerateUniqueVariableName(VisualBasicSyntaxNode existingNode, string varNameBase) => NameGenerator.CS.GetUniqueVariableNameInScope(_semanticModel, _generatedNames, existingNode, varNameBase); - - private static ExpressionSyntax DeclareVariableInline(ExpressionSyntax csExpressionSyntax, string temporaryName) - { - var temporaryNameId = SyntaxFactory.Identifier(temporaryName); - var temporaryNameExpression = ValidSyntaxFactory.IdentifierName(temporaryNameId); - csExpressionSyntax = SyntaxFactory.ConditionalExpression( - SyntaxFactory.IsPatternExpression( - csExpressionSyntax, - SyntaxFactory.VarPattern( - SyntaxFactory.SingleVariableDesignation(temporaryNameId))), - temporaryNameExpression, - SyntaxFactory.LiteralExpression( - SyntaxKind.DefaultLiteralExpression, - SyntaxFactory.Token(SyntaxKind.DefaultKeyword))); - return csExpressionSyntax; - } - - public override async Task VisitVariableNameEquals(VBSyntax.VariableNameEqualsSyntax node) => - SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(ConvertIdentifier(node.Identifier.Identifier))); - - public override async Task VisitObjectCollectionInitializer(VBasic.Syntax.ObjectCollectionInitializerSyntax node) - { - return await node.Initializer.AcceptAsync(TriviaConvertingExpressionVisitor); //Dictionary initializer comes through here despite the FROM keyword not being in the source code - } - - public override async Task VisitBinaryConditionalExpression(VBasic.Syntax.BinaryConditionalExpressionSyntax node) - { - var leftSide = await node.FirstExpression.AcceptAsync(TriviaConvertingExpressionVisitor); - var rightSide = await node.SecondExpression.AcceptAsync(TriviaConvertingExpressionVisitor); - var expr = SyntaxFactory.BinaryExpression(SyntaxKind.CoalesceExpression, - node.FirstExpression.ParenthesizeIfPrecedenceCouldChange(leftSide), - node.SecondExpression.ParenthesizeIfPrecedenceCouldChange(rightSide)); - - if (node.Parent.IsKind(VBasic.SyntaxKind.Interpolation) || node.PrecedenceCouldChange()) - return SyntaxFactory.ParenthesizedExpression(expr); - - return expr; - } - - public override async Task VisitTernaryConditionalExpression(VBasic.Syntax.TernaryConditionalExpressionSyntax node) - { - var condition = await node.Condition.AcceptAsync(TriviaConvertingExpressionVisitor); - condition = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Condition, condition, forceTargetType: CommonConversions.KnownTypes.Boolean); - - var whenTrue = await node.WhenTrue.AcceptAsync(TriviaConvertingExpressionVisitor); - whenTrue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.WhenTrue, whenTrue); - - var whenFalse = await node.WhenFalse.AcceptAsync(TriviaConvertingExpressionVisitor); - whenFalse = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.WhenFalse, whenFalse); - - var expr = SyntaxFactory.ConditionalExpression(condition, whenTrue, whenFalse); - - - if (node.Parent.IsKind(VBasic.SyntaxKind.Interpolation) || node.PrecedenceCouldChange()) - return SyntaxFactory.ParenthesizedExpression(expr); - - return expr; - } - - public override async Task VisitTypeOfExpression(VBasic.Syntax.TypeOfExpressionSyntax node) - { - var expr = SyntaxFactory.BinaryExpression( - SyntaxKind.IsExpression, - await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor), - await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor) - ); - return node.IsKind(VBasic.SyntaxKind.TypeOfIsNotExpression) ? expr.InvertCondition() : expr; - } - - public override async Task VisitUnaryExpression(VBasic.Syntax.UnaryExpressionSyntax node) - { - var expr = await node.Operand.AcceptAsync(TriviaConvertingExpressionVisitor); - if (node.IsKind(VBasic.SyntaxKind.AddressOfExpression)) { - return ConvertAddressOf(node, expr); - } - var kind = VBasic.VisualBasicExtensions.Kind(node).ConvertToken(); - SyntaxKind csTokenKind = CSharpUtil.GetExpressionOperatorTokenKind(kind); - - if (kind == SyntaxKind.LogicalNotExpression && _semanticModel.GetTypeInfo(node.Operand).ConvertedType is { } t) { - if (t.IsNumericType() || t.IsEnumType()) { - csTokenKind = SyntaxKind.TildeToken; - } else if (await NegateAndSimplifyOrNullAsync(node, expr, t) is { } simpleNegation) { - return simpleNegation; - } - } - - return SyntaxFactory.PrefixUnaryExpression( - kind, - SyntaxFactory.Token(csTokenKind), - expr.AddParens() - ); - } - - private async Task NegateAndSimplifyOrNullAsync(VBSyntax.UnaryExpressionSyntax node, ExpressionSyntax expr, ITypeSymbol operandConvertedType) - { - if (await _operatorConverter.ConvertReferenceOrNothingComparisonOrNullAsync(node.Operand.SkipIntoParens(), TriviaConvertingExpressionVisitor.IsWithinQuery, true) is { } nothingComparison) { - return nothingComparison; - } - if (operandConvertedType.GetNullableUnderlyingType()?.SpecialType == SpecialType.System_Boolean && node.AlwaysHasBooleanTypeInCSharp()) { - return SyntaxFactory.BinaryExpression(SyntaxKind.EqualsExpression, expr, LiteralConversions.GetLiteralExpression(false)); - } - - if (expr is BinaryExpressionSyntax eq && eq.OperatorToken.IsKind(SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken)){ - return eq.WithOperatorToken(SyntaxFactory.Token(eq.OperatorToken.IsKind(SyntaxKind.ExclamationEqualsToken) ? SyntaxKind.EqualsEqualsToken : SyntaxKind.ExclamationEqualsToken)); - } - - return null; - } - - private CSharpSyntaxNode ConvertAddressOf(VBSyntax.UnaryExpressionSyntax node, ExpressionSyntax expr) - { - var typeInfo = _semanticModel.GetTypeInfo(node); - if (_semanticModel.GetSymbolInfo(node.Operand).Symbol is IMethodSymbol ms && typeInfo.Type is INamedTypeSymbol nt && !ms.CompatibleSignatureToDelegate(nt)) { - int count = nt.DelegateInvokeMethod.Parameters.Length; - return CommonConversions.ThrowawayParameters(expr, count); - } - return expr; - } - - public override async Task VisitBinaryExpression(VBasic.Syntax.BinaryExpressionSyntax entryNode) - { - // Walk down the syntax tree for deeply nested binary expressions to avoid stack overflow - // e.g. 3 + 4 + 5 + ... - // Test "DeeplyNestedBinaryExpressionShouldNotStackOverflowAsync()" skipped because it's too slow - - ExpressionSyntax csLhs = null; - int levelsToConvert = 0; - VBSyntax.BinaryExpressionSyntax currentNode = entryNode; - - // Walk down the nested levels to count them - for (var nextNode = entryNode; nextNode != null; currentNode = nextNode, nextNode = currentNode.Left as VBSyntax.BinaryExpressionSyntax, levelsToConvert++) { - // Don't go beyond a rewritten operator because that code has many paths that can call VisitBinaryExpression. Passing csLhs through all of that would harm the code quality more than it's worth to help that edge case. - if (await RewriteBinaryOperatorOrNullAsync(nextNode) is { } operatorNode) { - csLhs = operatorNode; - break; - } - } - - // Walk back up the same levels converting as we go. - for (; levelsToConvert > 0; currentNode = currentNode!.Parent as VBSyntax.BinaryExpressionSyntax, levelsToConvert--) { - csLhs = (ExpressionSyntax)await ConvertBinaryExpressionAsync(currentNode, csLhs); - } - - return csLhs; - } - - private async Task ConvertBinaryExpressionAsync(VBasic.Syntax.BinaryExpressionSyntax node, ExpressionSyntax lhs = null, ExpressionSyntax rhs = null) - { - lhs ??= await node.Left.AcceptAsync(TriviaConvertingExpressionVisitor); - rhs ??= await node.Right.AcceptAsync(TriviaConvertingExpressionVisitor); - - var lhsTypeInfo = _semanticModel.GetTypeInfo(node.Left); - var rhsTypeInfo = _semanticModel.GetTypeInfo(node.Right); - - ITypeSymbol forceLhsTargetType = null; - bool omitRightConversion = false; - bool omitConversion = false; - if (lhsTypeInfo.Type != null && rhsTypeInfo.Type != null) - { - if (node.IsKind(VBasic.SyntaxKind.ConcatenateExpression) && - !lhsTypeInfo.Type.IsEnumType() && !rhsTypeInfo.Type.IsEnumType() && - !lhsTypeInfo.Type.IsDateType() && !rhsTypeInfo.Type.IsDateType()) - { - omitRightConversion = true; - omitConversion = lhsTypeInfo.Type.SpecialType == SpecialType.System_String || - rhsTypeInfo.Type.SpecialType == SpecialType.System_String; - if (lhsTypeInfo.ConvertedType.SpecialType != SpecialType.System_String) { - forceLhsTargetType = CommonConversions.KnownTypes.String; - } - } - } - - var objectEqualityType = _visualBasicEqualityComparison.GetObjectEqualityType(node, lhsTypeInfo, rhsTypeInfo); - - switch (objectEqualityType) { - case VisualBasicEqualityComparison.RequiredType.StringOnly: - if (lhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && - rhsTypeInfo.ConvertedType?.SpecialType == SpecialType.System_String && - _visualBasicEqualityComparison.TryConvertToNullOrEmptyCheck(node, lhs, rhs, out CSharpSyntaxNode visitBinaryExpression)) { - return visitBinaryExpression; - } - (lhs, rhs) = _visualBasicEqualityComparison.AdjustForVbStringComparison(node.Left, lhs, lhsTypeInfo, false, node.Right, rhs, rhsTypeInfo, false); - omitConversion = true; // Already handled within for the appropriate types (rhs can become int in comparison) - break; - case VisualBasicEqualityComparison.RequiredType.Object: - return _visualBasicEqualityComparison.GetFullExpressionForVbObjectComparison(lhs, rhs, ComparisonKind.Equals, node.IsKind(VBasic.SyntaxKind.NotEqualsExpression)); - } - - var lhsTypeIgnoringNullable = lhsTypeInfo.Type.GetNullableUnderlyingType() ?? lhsTypeInfo.Type; - var rhsTypeIgnoringNullable = rhsTypeInfo.Type.GetNullableUnderlyingType() ?? rhsTypeInfo.Type; - omitConversion |= lhsTypeIgnoringNullable != null && rhsTypeIgnoringNullable != null && - lhsTypeIgnoringNullable.IsEnumType() && SymbolEqualityComparer.Default.Equals(lhsTypeIgnoringNullable, rhsTypeIgnoringNullable) - && !node.IsKind(VBasic.SyntaxKind.AddExpression, VBasic.SyntaxKind.SubtractExpression, VBasic.SyntaxKind.MultiplyExpression, VBasic.SyntaxKind.DivideExpression, VBasic.SyntaxKind.IntegerDivideExpression, VBasic.SyntaxKind.ModuloExpression) - && forceLhsTargetType == null; - lhs = omitConversion ? lhs : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Left, lhs, forceTargetType: forceLhsTargetType); - rhs = omitConversion || omitRightConversion ? rhs : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Right, rhs); - - var kind = VBasic.VisualBasicExtensions.Kind(node).ConvertToken(); - var op = SyntaxFactory.Token(CSharpUtil.GetExpressionOperatorTokenKind(kind)); - - var csBinExp = SyntaxFactory.BinaryExpression(kind, lhs, op, rhs); - var exp = _visualBasicNullableTypesConverter.WithBinaryExpressionLogicForNullableTypes(node, lhsTypeInfo, rhsTypeInfo, csBinExp, lhs, rhs); - return node.Parent.IsKind(VBasic.SyntaxKind.SimpleArgument) ? exp : exp.AddParens(); - } - - - - private async Task RewriteBinaryOperatorOrNullAsync(VBSyntax.BinaryExpressionSyntax node) => - await _operatorConverter.ConvertRewrittenBinaryOperatorOrNullAsync(node, TriviaConvertingExpressionVisitor.IsWithinQuery); - - private async Task WithRemovedRedundantConversionOrNullAsync(VBSyntax.InvocationExpressionSyntax conversionNode, ISymbol invocationSymbol) - { - if (invocationSymbol?.ContainingNamespace.MetadataName != nameof(Microsoft.VisualBasic) || - invocationSymbol.ContainingType.Name != nameof(Conversions) || - !invocationSymbol.Name.StartsWith("To", StringComparison.InvariantCulture) || - conversionNode.ArgumentList.Arguments.Count != 1) { - return null; - } - - var conversionArg = conversionNode.ArgumentList.Arguments.First().GetExpression(); - VBSyntax.ExpressionSyntax coercedConversionNode = conversionNode; - return await WithRemovedRedundantConversionOrNullAsync(coercedConversionNode, conversionArg); - } - - private async Task WithRemovedRedundantConversionOrNullAsync(VBSyntax.ExpressionSyntax conversionNode, VBSyntax.ExpressionSyntax conversionArg) - { - var csharpArg = await conversionArg.AcceptAsync(TriviaConvertingExpressionVisitor); - var typeInfo = _semanticModel.GetTypeInfo(conversionNode); - - // If written by the user (i.e. not generated during expand phase), maintain intended semantics which could throw sometimes e.g. object o = (int) (object) long.MaxValue; - var writtenByUser = !conversionNode.HasAnnotation(Simplifier.Annotation); - var forceTargetType = typeInfo.ConvertedType; - // TypeConversionAnalyzer can't figure out which type is required for operator/method overloads, inferred func returns or inferred variable declarations - // (currently overapproximates for numeric and gets it wrong in non-numeric cases). - // Future: Avoid more redundant conversions by still calling AddExplicitConversion when writtenByUser avoiding the above and forcing typeInfo.Type - return writtenByUser ? null : CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(conversionArg, csharpArg, - forceTargetType: forceTargetType, defaultToCast: true); - } - - - public override async Task VisitInvocationExpression( - VBasic.Syntax.InvocationExpressionSyntax node) - { - var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch(); - var methodInvocationSymbol = invocationSymbol as IMethodSymbol; - var withinLocalFunction = methodInvocationSymbol != null && RequiresLocalFunction(node, methodInvocationSymbol); - if (withinLocalFunction) { - _typeContext.PerScopeState.PushScope(); - } - try { - - if (node.Expression is null) { - var convertArgumentListOrEmptyAsync = await ConvertArgumentsAsync(node.ArgumentList); - return SyntaxFactory.ElementBindingExpression(SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(convertArgumentListOrEmptyAsync))); - } - - var convertedInvocation = await ConvertOrReplaceInvocationAsync(node, invocationSymbol); - if (withinLocalFunction) { - return await HoistAndCallLocalFunctionAsync(node, methodInvocationSymbol, (ExpressionSyntax)convertedInvocation); - } - return convertedInvocation; - } finally { - if (withinLocalFunction) { - _typeContext.PerScopeState.PopExpressionScope(); - } - } - } - - private async Task ConvertOrReplaceInvocationAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol invocationSymbol) - { - var expressionSymbol = _semanticModel.GetSymbolInfo(node.Expression).ExtractBestMatch(); - if ((await SubstituteVisualBasicMethodOrNullAsync(node, expressionSymbol) ?? - await WithRemovedRedundantConversionOrNullAsync(node, expressionSymbol)) is { } csEquivalent) { - return csEquivalent; - } - - if (invocationSymbol?.Name is "op_Implicit" or "op_Explicit") { - var vbExpr = node.ArgumentList.Arguments.Single().GetExpression(); - var csExpr = await vbExpr.AcceptAsync(TriviaConvertingExpressionVisitor); - return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(vbExpr, csExpr, true, true, false, forceTargetType: invocationSymbol.GetReturnType()); - } - - return await ConvertInvocationAsync(node, invocationSymbol, expressionSymbol); - } - - private async Task ConvertInvocationAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol invocationSymbol, ISymbol expressionSymbol) - { - var expressionType = _semanticModel.GetTypeInfo(node.Expression).Type; - var expressionReturnType = expressionSymbol?.GetReturnType() ?? expressionType; - var operation = _semanticModel.GetOperation(node); - - var expr = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - if (await TryConvertParameterizedPropertyAsync(operation, node, expr, node.ArgumentList) is { } invocation) - { - return invocation; - } - //TODO: Decide if the above override should be subject to the rest of this method's adjustments (probably) - - - // VB doesn't have a specialized node for element access because the syntax is ambiguous. Instead, it just uses an invocation expression or dictionary access expression, then figures out using the semantic model which one is most likely intended. - // https://github.com/dotnet/roslyn/blob/master/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb#L768 - (var convertedExpression, bool shouldBeElementAccess) = await ConvertInvocationSubExpressionAsync(node, operation, expressionSymbol, expressionReturnType, expr); - if (shouldBeElementAccess) - { - return await CreateElementAccessAsync(node, convertedExpression); - } - - if (expressionSymbol != null && expressionSymbol.IsKind(SymbolKind.Property) && - invocationSymbol != null && invocationSymbol.GetParameters().Length == 0 && node.ArgumentList.Arguments.Count == 0) - { - return convertedExpression; //Parameterless property access - } - - var convertedArgumentList = await ConvertArgumentListOrEmptyAsync(node, node.ArgumentList); - - if (IsElementAtOrDefaultInvocation(invocationSymbol, expressionSymbol)) - { - convertedExpression = GetElementAtOrDefaultExpression(expressionType, convertedExpression); - } - - if (invocationSymbol.IsReducedExtension() && invocationSymbol is IMethodSymbol {ReducedFrom: {Parameters: var parameters}} && - !parameters.FirstOrDefault().ValidCSharpExtensionMethodParameter() && - node.Expression is VBSyntax.MemberAccessExpressionSyntax maes) - { - var thisArgExpression = await maes.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - var thisArg = SyntaxFactory.Argument(thisArgExpression).WithRefKindKeyword(GetRefToken(RefKind.Ref)); - convertedArgumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(convertedArgumentList.Arguments.Prepend(thisArg))); - var containingType = (ExpressionSyntax) CommonConversions.CsSyntaxGenerator.TypeExpression(invocationSymbol.ContainingType); - convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, containingType, - ValidSyntaxFactory.IdentifierName((invocationSymbol.Name))); - } - - if (invocationSymbol is IMethodSymbol m && convertedExpression is LambdaExpressionSyntax) { - convertedExpression = SyntaxFactory.ObjectCreationExpression(CommonConversions.GetFuncTypeSyntax(expressionType, m), ExpressionSyntaxExtensions.CreateArgList(convertedExpression), null); - } - return SyntaxFactory.InvocationExpression(convertedExpression, convertedArgumentList); - } - - private async Task<(ExpressionSyntax, bool isElementAccess)> ConvertInvocationSubExpressionAsync(VBSyntax.InvocationExpressionSyntax node, - IOperation operation, ISymbol expressionSymbol, ITypeSymbol expressionReturnType, CSharpSyntaxNode expr) - { - var isElementAccess = operation.IsPropertyElementAccess() - || operation.IsArrayElementAccess() - || ProbablyNotAMethodCall(node, expressionSymbol, expressionReturnType); - - var expressionSyntax = (ExpressionSyntax)expr; - - return (expressionSyntax, isElementAccess); - } - - private async Task CreateElementAccessAsync(VBSyntax.InvocationExpressionSyntax node, ExpressionSyntax expression) - { - var args = - await node.ArgumentList.Arguments.AcceptSeparatedListAsync(TriviaConvertingExpressionVisitor); - var bracketedArgumentListSyntax = SyntaxFactory.BracketedArgumentList(args); - if (expression is ElementBindingExpressionSyntax binding && - !binding.ArgumentList.Arguments.Any()) { - // Special case where structure changes due to conditional access (See VisitMemberAccessExpression) - return binding.WithArgumentList(bracketedArgumentListSyntax); - } - - return SyntaxFactory.ElementAccessExpression(expression, bracketedArgumentListSyntax); - } - - private static bool IsElementAtOrDefaultInvocation(ISymbol invocationSymbol, ISymbol expressionSymbol) - { - return (expressionSymbol != null - && (invocationSymbol?.Name == nameof(Enumerable.ElementAtOrDefault) - && !expressionSymbol.Equals(invocationSymbol, SymbolEqualityComparer.IncludeNullability))); - } - - private ExpressionSyntax GetElementAtOrDefaultExpression(ISymbol expressionType, - ExpressionSyntax expression) - { - _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Linq)); - - // The Vb compiler interprets Datatable indexing as a AsEnumerable().ElementAtOrDefault() operation. - if (expressionType.Name == nameof(DataTable)) - { - _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Data)); - - expression = SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, expression, - ValidSyntaxFactory.IdentifierName(nameof(DataTableExtensions.AsEnumerable)))); - } - - var newExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - expression, ValidSyntaxFactory.IdentifierName(nameof(Enumerable.ElementAtOrDefault))); - - return newExpression; - } - - private async Task TryConvertParameterizedPropertyAsync(IOperation operation, - SyntaxNode node, CSharpSyntaxNode identifier, - VBSyntax.ArgumentListSyntax optionalArgumentList = null) - { - var (overrideIdentifier, extraArg) = - await CommonConversions.GetParameterizedPropertyAccessMethodAsync(operation); - if (overrideIdentifier != null) - { - var expr = identifier; - var idToken = expr.DescendantTokens().Last(t => t.IsKind(SyntaxKind.IdentifierToken)); - expr = ReplaceRightmostIdentifierText(expr, idToken, overrideIdentifier); - - var args = await ConvertArgumentListOrEmptyAsync(node, optionalArgumentList); - if (extraArg != null) { - var extraArgSyntax = SyntaxFactory.Argument(extraArg); - var propertySymbol = ((IPropertyReferenceOperation)operation).Property; - var forceNamedExtraArg = args.Arguments.Count != propertySymbol.GetParameters().Length || - args.Arguments.Any(t => t.NameColon != null); - - if (forceNamedExtraArg) { - extraArgSyntax = extraArgSyntax.WithNameColon(SyntaxFactory.NameColon("value")); - } - - args = args.WithArguments(args.Arguments.Add(extraArgSyntax)); - } - - return SyntaxFactory.InvocationExpression((ExpressionSyntax)expr, args); - } - - return null; - } - - - /// - /// The VB compiler actually just hoists the conditions within the same method, but that leads to the original logic looking very different. - /// This should be equivalent but keep closer to the look of the original source code. - /// See https://github.com/icsharpcode/CodeConverter/issues/310 and https://github.com/icsharpcode/CodeConverter/issues/324 - /// - private async Task HoistAndCallLocalFunctionAsync(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol, ExpressionSyntax csExpression) - { - const string retVariableName = "ret"; - var localFuncName = $"local{invocationSymbol.Name}"; - - var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName, csExpression); - - var statements = await _typeContext.PerScopeState.CreateLocalsAsync(invocation, new[] { callAndStoreResult }, _generatedNames, _semanticModel); - - var block = SyntaxFactory.Block( - statements.Concat(SyntaxFactory.ReturnStatement(ValidSyntaxFactory.IdentifierName(retVariableName)).Yield()) - ); - var returnType = CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType); - - //any argument that's a ByRef parameter of the parent method needs to be passed as a ref parameter to the local function (to avoid error CS1628) - var refParametersOfParent = GetRefParameters(invocation.ArgumentList); - var (args, @params) = CreateArgumentsAndParametersLists(refParametersOfParent); - - var localFunc = _typeContext.PerScopeState.Hoist(new HoistedFunction(localFuncName, returnType, block, SyntaxFactory.ParameterList(@params))); - return SyntaxFactory.InvocationExpression(localFunc.TempIdentifier, SyntaxFactory.ArgumentList(args)); - - List GetRefParameters(VBSyntax.ArgumentListSyntax argumentList) - { - var result = new List(); - if (argumentList is null) return result; - - foreach (var arg in argumentList.Arguments) { - if (_semanticModel.GetSymbolInfo(arg.GetExpression()).Symbol is not IParameterSymbol p) continue; - if (p.RefKind != RefKind.None) { - result.Add(p); - } - } - - return result; - } - - (SeparatedSyntaxList, SeparatedSyntaxList) CreateArgumentsAndParametersLists(List parameterSymbols) - { - var arguments = new List(); - var parameters = new List(); - foreach (var p in parameterSymbols) { - var arg = (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.RefKind, SyntaxFactory.IdentifierName(p.Name)); - arguments.Add(arg); - var par = (ParameterSyntax)CommonConversions.CsSyntaxGenerator.ParameterDeclaration(p); - parameters.Add(par); - } - return (SyntaxFactory.SeparatedList(arguments), SyntaxFactory.SeparatedList(parameters)); - } - } - - private bool RequiresLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol) - { - if (invocation.ArgumentList == null) return false; - var definitelyExecutedAfterPrevious = DefinitelyExecutedAfterPreviousStatement(invocation); - var nextStatementDefinitelyExecuted = NextStatementDefinitelyExecutedAfter(invocation); - if (definitelyExecutedAfterPrevious && nextStatementDefinitelyExecuted) return false; - var possibleInline = definitelyExecutedAfterPrevious ? RefConversion.PreAssigment : RefConversion.Inline; - return invocation.ArgumentList.Arguments.Any(a => RequiresLocalFunction(possibleInline, invocation, invocationSymbol, a)); - - bool RequiresLocalFunction(RefConversion possibleInline, VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol, VBSyntax.ArgumentSyntax a) - { - var refConversion = GetRefConversionType(a, invocation.ArgumentList, invocationSymbol.Parameters, out string _, out _); - if (RefConversion.Inline == refConversion || possibleInline == refConversion) return false; - if (!(a is VBSyntax.SimpleArgumentSyntax sas)) return false; - var argExpression = sas.Expression.SkipIntoParens(); - if (argExpression is VBSyntax.InstanceExpressionSyntax) return false; - return !_semanticModel.GetConstantValue(argExpression).HasValue; - } - } - - /// - /// Conservative version of _semanticModel.AnalyzeControlFlow(invocation).ExitPoints to account for exceptions - /// - private bool DefinitelyExecutedAfterPreviousStatement(VBSyntax.InvocationExpressionSyntax invocation) - { - SyntaxNode parent = invocation; - while (true) { - parent = parent.Parent; - switch (parent) - { - case VBSyntax.ParenthesizedExpressionSyntax _: - continue; - case VBSyntax.BinaryExpressionSyntax binaryExpression: - if (binaryExpression.Left == invocation) continue; - else return false; - case VBSyntax.ArgumentSyntax argumentSyntax: - // Being the leftmost invocation of an unqualified method call ensures no other code is executed. Could add other cases here, such as a method call on a local variable name, or "this.". A method call on a property is not acceptable. - if (argumentSyntax.Parent.Parent is VBSyntax.InvocationExpressionSyntax parentInvocation && parentInvocation.ArgumentList.Arguments.First() == argumentSyntax && FirstArgDefinitelyEvaluated(parentInvocation)) continue; - else return false; - case VBSyntax.ElseIfStatementSyntax _: - case VBSyntax.ExpressionSyntax _: - return false; - case VBSyntax.StatementSyntax _: - return true; - } - } - } - - private bool FirstArgDefinitelyEvaluated(VBSyntax.InvocationExpressionSyntax parentInvocation) => - parentInvocation.Expression.SkipIntoParens() switch { - VBSyntax.IdentifierNameSyntax _ => true, - VBSyntax.MemberAccessExpressionSyntax maes => maes.Expression is {} exp && !MayThrow(exp), - _ => true - }; - - /// - /// Safe overapproximation of whether an expression may throw. - /// - private bool MayThrow(VBSyntax.ExpressionSyntax expression) - { - expression = expression.SkipIntoParens(); - if (expression is VBSyntax.InstanceExpressionSyntax) return false; - var symbol = _semanticModel.GetSymbolInfo(expression).Symbol; - return !symbol.IsKind(SymbolKind.Local) && !symbol.IsKind(SymbolKind.Field); - } - - /// - /// Conservative version of _semanticModel.AnalyzeControlFlow(invocation).ExitPoints to account for exceptions - /// - private static bool NextStatementDefinitelyExecutedAfter(VBSyntax.InvocationExpressionSyntax invocation) - { - SyntaxNode parent = invocation; - while (true) { - parent = parent.Parent; - switch (parent) - { - case VBSyntax.ParenthesizedExpressionSyntax _: - continue; - case VBSyntax.BinaryExpressionSyntax binaryExpression: - if (binaryExpression.Right == invocation) continue; - else return false; - case VBSyntax.IfStatementSyntax _: - case VBSyntax.ElseIfStatementSyntax _: - case VBSyntax.SingleLineIfStatementSyntax _: - return false; - case VBSyntax.ExpressionSyntax _: - case VBSyntax.StatementSyntax _: - return true; - } - } - } - - public override async Task VisitSingleLineLambdaExpression(VBasic.Syntax.SingleLineLambdaExpressionSyntax node) - { - var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; - TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); - try { - return await ConvertInnerAsync(); - } finally { - TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; - } - - async Task ConvertInnerAsync() - { - IReadOnlyCollection convertedStatements; - if (node.Body is VBasic.Syntax.StatementSyntax statement) - { - convertedStatements = await ConvertMethodBodyStatementsAsync(statement, statement.Yield().ToArray()); - } - else - { - var csNode = await node.Body.AcceptAsync(TriviaConvertingExpressionVisitor); - convertedStatements = new[] {SyntaxFactory.ExpressionStatement(csNode)}; - } - - var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync(TriviaConvertingExpressionVisitor); - return await _lambdaConverter.ConvertAsync(node, param, convertedStatements); - } - } - - public override async Task VisitMultiLineLambdaExpression(VBasic.Syntax.MultiLineLambdaExpressionSyntax node) - { - var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; - TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); - try { - return await ConvertInnerAsync(); - } finally { - TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; - } - - async Task ConvertInnerAsync() - { - var body = await ConvertMethodBodyStatementsAsync(node, node.Statements); - var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync(TriviaConvertingExpressionVisitor); - return await _lambdaConverter.ConvertAsync(node, param, body.ToList()); - } - } - - public async Task> ConvertMethodBodyStatementsAsync(VBasic.VisualBasicSyntaxNode node, IReadOnlyCollection statements, bool isIterator = false, IdentifierNameSyntax csReturnVariable = null) - { - - var innerMethodBodyVisitor = await MethodBodyExecutableStatementVisitor.CreateAsync(node, _semanticModel, TriviaConvertingExpressionVisitor, CommonConversions, _visualBasicEqualityComparison, _withBlockLhs, _extraUsingDirectives, _typeContext, isIterator, csReturnVariable); - return await GetWithConvertedGotosOrNull(statements) ?? await ConvertStatements(statements); - - async Task> ConvertStatements(IEnumerable readOnlyCollection) - { - return (await readOnlyCollection.SelectManyAsync(async s => (IEnumerable)await s.Accept(innerMethodBodyVisitor.CommentConvertingVisitor))).ToList(); - } - - async Task> GetWithConvertedGotosOrNull(IReadOnlyCollection statements) - { - var onlyIdentifierLabel = statements.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.LabelStatement)); - var onlyOnErrorGotoStatement = statements.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.OnErrorGoToLabelStatement)); - - // See https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/on-error-statement - if (onlyIdentifierLabel != null && onlyOnErrorGotoStatement != null) { - var statementsList = statements.ToList(); - var onlyIdentifierLabelIndex = statementsList.IndexOf(onlyIdentifierLabel); - var onlyOnErrorGotoStatementIndex = statementsList.IndexOf(onlyOnErrorGotoStatement); - - // Even this very simple case can generate compile errors if the error handling uses statements declared in the scope of the try block - // For now, the user will have to fix these manually, in future it'd be possible to hoist any used declarations out of the try block - if (onlyOnErrorGotoStatementIndex < onlyIdentifierLabelIndex) { - var beforeStatements = await ConvertStatements(statements.Take(onlyOnErrorGotoStatementIndex)); - var tryBlockStatements = await ConvertStatements(statements.Take(onlyIdentifierLabelIndex).Skip(onlyOnErrorGotoStatementIndex + 1)); - var tryBlock = SyntaxFactory.Block(tryBlockStatements); - var afterStatements = await ConvertStatements(statements.Skip(onlyIdentifierLabelIndex + 1)); - - var catchClauseSyntax = SyntaxFactory.CatchClause(); - - // Default to putting the statements after the catch block in case logic falls through, but if the last statement is a return, put them inside the catch block for neatness. - if (tryBlockStatements.LastOrDefault().IsKind(SyntaxKind.ReturnStatement)) { - catchClauseSyntax = catchClauseSyntax.WithBlock(SyntaxFactory.Block(afterStatements)); - afterStatements = new List(); - } - - var tryStatement = SyntaxFactory.TryStatement(SyntaxFactory.SingletonList(catchClauseSyntax)).WithBlock(tryBlock); - return beforeStatements.Append(tryStatement).Concat(afterStatements).ToList(); - } - } - - return null; - } - } - - public override async Task VisitParameterList(VBSyntax.ParameterListSyntax node) - { - var parameters = await node.Parameters.SelectAsync(async p => await p.AcceptAsync(TriviaConvertingExpressionVisitor)); - if (node.Parent is VBSyntax.PropertyStatementSyntax && CommonConversions.IsDefaultIndexer(node.Parent)) { - return SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(parameters)); - } - return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)); - } - - public override async Task VisitParameter(VBSyntax.ParameterSyntax node) - { - var id = CommonConversions.ConvertIdentifier(node.Identifier.Identifier); - - TypeSyntax paramType = null; - if (node.Parent?.Parent?.IsKind(VBasic.SyntaxKind.FunctionLambdaHeader, - VBasic.SyntaxKind.SubLambdaHeader) != true || node.AsClause != null) { - var vbParamSymbol = _semanticModel.GetDeclaredSymbol(node) as IParameterSymbol; - paramType = vbParamSymbol != null ? CommonConversions.GetTypeSyntax(vbParamSymbol.Type) - : await SyntaxOnlyConvertParamAsync(node); - } - - var attributes = (await node.AttributeLists.SelectManyAsync(CommonConversions.ConvertAttributeAsync)).ToList(); - var modifiers = CommonConversions.ConvertModifiers(node, node.Modifiers, TokenContext.Local); - var vbSymbol = _semanticModel.GetDeclaredSymbol(node) as IParameterSymbol; - var baseParameters = vbSymbol?.ContainingSymbol.OriginalDefinition.GetBaseSymbol().GetParameters(); - var baseParameter = baseParameters?[vbSymbol.Ordinal]; - - var csRefKind = CommonConversions.GetCsRefKind(baseParameter ?? vbSymbol, node); - if (csRefKind == RefKind.Out) { - modifiers = SyntaxFactory.TokenList(modifiers - .Where(m => !m.IsKind(SyntaxKind.RefKeyword)) - .Concat(SyntaxFactory.Token(SyntaxKind.OutKeyword).Yield()) - ); - } - - EqualsValueClauseSyntax @default = null; - // Parameterized properties get compiled/converted to a method with non-optional parameters - if (node.Default != null) { - var defaultValue = node.Default.Value.SkipIntoParens(); - if (_semanticModel.GetTypeInfo(defaultValue).Type?.SpecialType == SpecialType.System_DateTime) { - var constant = _semanticModel.GetConstantValue(defaultValue); - if (constant.HasValue && constant.Value is DateTime dt) { - var dateTimeAsLongCsLiteral = CommonConversions.Literal(dt.Ticks) - .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia($"/* {defaultValue} */")); - var dateTimeArg = CommonConversions.CreateAttributeArgumentList(SyntaxFactory.AttributeArgument(dateTimeAsLongCsLiteral)); - _extraUsingDirectives.Add("System.Runtime.InteropServices"); - _extraUsingDirectives.Add("System.Runtime.CompilerServices"); - var optionalDateTimeAttributes = new[] { - SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("Optional")), - SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DateTimeConstant"), dateTimeArg) - }; - attributes.Insert(0, - SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(optionalDateTimeAttributes))); - } - } else if (node.Modifiers.Any(m => m.IsKind(VBasic.SyntaxKind.ByRefKeyword)) || HasRefParametersAfterThisOne()) { - var defaultExpression = await node.Default.Value.AcceptAsync(TriviaConvertingExpressionVisitor); - var arg = CommonConversions.CreateAttributeArgumentList(SyntaxFactory.AttributeArgument(defaultExpression)); - _extraUsingDirectives.Add("System.Runtime.InteropServices"); - _extraUsingDirectives.Add("System.Runtime.CompilerServices"); - var optionalAttributes = new List { - SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("Optional")), - }; - if (!node.Default.Value.IsKind(VBasic.SyntaxKind.NothingLiteralExpression)) { - optionalAttributes.Add(SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DefaultParameterValue"), arg)); - } - attributes.Insert(0, - SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(optionalAttributes))); - } else { - @default = SyntaxFactory.EqualsValueClause( - await node.Default.Value.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - } - - if (node.Parent.Parent is VBSyntax.MethodStatementSyntax mss - && mss.AttributeLists.Any(CommonConversions.HasExtensionAttribute) && node.Parent.ChildNodes().First() == node && - vbSymbol.ValidCSharpExtensionMethodParameter()) { - modifiers = modifiers.Insert(0, SyntaxFactory.Token(SyntaxKind.ThisKeyword)); - } - return SyntaxFactory.Parameter( - SyntaxFactory.List(attributes), - modifiers, - paramType, - id, - @default - ); - - bool HasRefParametersAfterThisOne() => vbSymbol is not null && baseParameters is {} bp && bp.Skip(vbSymbol.Ordinal + 1).Any(x => x.RefKind != RefKind.None); - } - - private async Task SyntaxOnlyConvertParamAsync(VBSyntax.ParameterSyntax node) - { - var syntaxParamType = await (node.AsClause?.Type).AcceptAsync(TriviaConvertingExpressionVisitor) - ?? SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); - - var rankSpecifiers = await CommonConversions.ConvertArrayRankSpecifierSyntaxesAsync(node.Identifier.ArrayRankSpecifiers, node.Identifier.ArrayBounds, false); - if (rankSpecifiers.Any()) { - syntaxParamType = SyntaxFactory.ArrayType(syntaxParamType, rankSpecifiers); - } - - if (!node.Identifier.Nullable.IsKind(SyntaxKind.None)) { - var arrayType = syntaxParamType as ArrayTypeSyntax; - if (arrayType == null) { - syntaxParamType = SyntaxFactory.NullableType(syntaxParamType); - } else { - syntaxParamType = arrayType.WithElementType(SyntaxFactory.NullableType(arrayType.ElementType)); - } - } - return syntaxParamType; - } - - public override async Task VisitAttribute(VBSyntax.AttributeSyntax node) - { - return SyntaxFactory.AttributeList( - node.Target == null ? null : SyntaxFactory.AttributeTargetSpecifier(node.Target.AttributeModifier.ConvertToken()), - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(await node.Name.AcceptAsync(TriviaConvertingExpressionVisitor), await node.ArgumentList.AcceptAsync(TriviaConvertingExpressionVisitor))) - ); - } - - public override async Task VisitTupleType(VBasic.Syntax.TupleTypeSyntax node) - { - var elements = await node.Elements.SelectAsync(async e => await e.AcceptAsync(TriviaConvertingExpressionVisitor)); - return SyntaxFactory.TupleType(SyntaxFactory.SeparatedList(elements)); - } - - public override async Task VisitTypedTupleElement(VBasic.Syntax.TypedTupleElementSyntax node) - { - return SyntaxFactory.TupleElement(await node.Type.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitNamedTupleElement(VBasic.Syntax.NamedTupleElementSyntax node) - { - return SyntaxFactory.TupleElement(await node.AsClause.Type.AcceptAsync(TriviaConvertingExpressionVisitor), CommonConversions.ConvertIdentifier(node.Identifier)); - } - - public override async Task VisitTupleExpression(VBasic.Syntax.TupleExpressionSyntax node) - { - var args = await node.Arguments.SelectAsync(async a => { - var expr = await a.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - return SyntaxFactory.Argument(expr); - }); - return SyntaxFactory.TupleExpression(SyntaxFactory.SeparatedList(args)); - } - - public override async Task VisitPredefinedType(VBasic.Syntax.PredefinedTypeSyntax node) - { - if (node.Keyword.IsKind(VBasic.SyntaxKind.DateKeyword)) { - return ValidSyntaxFactory.IdentifierName(nameof(DateTime)); - } - return SyntaxFactory.PredefinedType(node.Keyword.ConvertToken()); - } - - public override async Task VisitNullableType(VBasic.Syntax.NullableTypeSyntax node) - { - return SyntaxFactory.NullableType(await node.ElementType.AcceptAsync(TriviaConvertingExpressionVisitor)); - } - - public override async Task VisitArrayType(VBasic.Syntax.ArrayTypeSyntax node) - { - var ranks = await node.RankSpecifiers.SelectAsync(async r => await r.AcceptAsync(TriviaConvertingExpressionVisitor)); - return SyntaxFactory.ArrayType(await node.ElementType.AcceptAsync(TriviaConvertingExpressionVisitor), SyntaxFactory.List(ranks)); - } - - public override async Task VisitArrayRankSpecifier(VBasic.Syntax.ArrayRankSpecifierSyntax node) - { - return SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(Enumerable.Repeat(SyntaxFactory.OmittedArraySizeExpression(), node.Rank))); - } - - /// PERF: This is a hot code path, try to avoid using things like GetOperation except where needed. - public override async Task VisitIdentifierName(VBasic.Syntax.IdentifierNameSyntax node) - { - var identifier = SyntaxFactory.IdentifierName(ConvertIdentifier(node.Identifier, node.GetAncestor() != null)); - - bool requiresQualification = !node.Parent.IsKind(VBasic.SyntaxKind.SimpleMemberAccessExpression, VBasic.SyntaxKind.QualifiedName, VBasic.SyntaxKind.NameColonEquals, VBasic.SyntaxKind.ImportsStatement, VBasic.SyntaxKind.NamespaceStatement, VBasic.SyntaxKind.NamedFieldInitializer) || - node.Parent is VBSyntax.NamedFieldInitializerSyntax nfs && nfs.Expression == node || - node.Parent is VBasic.Syntax.MemberAccessExpressionSyntax maes && maes.Expression == node; - var qualifiedIdentifier = requiresQualification - ? QualifyNode(node, identifier) : identifier; - - var sym = GetSymbolInfoInDocument(node); - if (sym is ILocalSymbol) { - if (sym.IsStatic && sym.ContainingSymbol is IMethodSymbol m && m.AssociatedSymbol is IPropertySymbol) { - qualifiedIdentifier = qualifiedIdentifier.WithParentPropertyAccessorKind(m.MethodKind); - } - - var vbMethodBlock = node.Ancestors().OfType().FirstOrDefault(); - if (vbMethodBlock != null && - vbMethodBlock.MustReturn() && - !node.Parent.IsKind(VBasic.SyntaxKind.NameOfExpression) && - node.Identifier.ValueText.Equals(CommonConversions.GetMethodBlockBaseIdentifierForImplicitReturn(vbMethodBlock).ValueText, StringComparison.OrdinalIgnoreCase)) { - var retVar = CommonConversions.GetRetVariableNameOrNull(vbMethodBlock); - if (retVar != null) { - return retVar; - } - } - } - - return await AdjustForImplicitInvocationAsync(node, qualifiedIdentifier); - } - - private async Task AdjustForImplicitInvocationAsync(SyntaxNode node, ExpressionSyntax qualifiedIdentifier) - { - //PERF: Avoid calling expensive GetOperation when it's easy - bool nonExecutableNode = node.IsParentKind(VBasic.SyntaxKind.QualifiedName); - if (nonExecutableNode || _semanticModel.SyntaxTree != node.SyntaxTree) return qualifiedIdentifier; - - if (await TryConvertParameterizedPropertyAsync(_semanticModel.GetOperation(node), node, qualifiedIdentifier) is {} - invocation) - { - return invocation; - } - - return AddEmptyArgumentListIfImplicit(node, qualifiedIdentifier); - } - - public override async Task VisitQualifiedName(VBasic.Syntax.QualifiedNameSyntax node) - { - var symbol = GetSymbolInfoInDocument(node); - if (symbol != null) { - return CommonConversions.GetTypeSyntax(symbol.GetSymbolType()); - } - var lhsSyntax = await node.Left.AcceptAsync(TriviaConvertingExpressionVisitor); - var rhsSyntax = await node.Right.AcceptAsync(TriviaConvertingExpressionVisitor); - - VBasic.Syntax.NameSyntax topLevelName = node; - while (topLevelName.Parent is VBasic.Syntax.NameSyntax parentName) { - topLevelName = parentName; - } - var partOfNamespaceDeclaration = topLevelName.Parent.IsKind(VBasic.SyntaxKind.NamespaceStatement); - var leftIsGlobal = node.Left.IsKind(VBasic.SyntaxKind.GlobalName); - ExpressionSyntax qualifiedName; - if (partOfNamespaceDeclaration || !(lhsSyntax is SimpleNameSyntax sns)) { - if (leftIsGlobal) return rhsSyntax; - qualifiedName = lhsSyntax; - } else { - qualifiedName = QualifyNode(node.Left, sns); - } - - return leftIsGlobal ? SyntaxFactory.AliasQualifiedName((IdentifierNameSyntax)lhsSyntax, rhsSyntax) : - SyntaxFactory.QualifiedName((NameSyntax)qualifiedName, rhsSyntax); - } - - public override async Task VisitGenericName(VBasic.Syntax.GenericNameSyntax node) - { - var symbol = GetSymbolInfoInDocument(node); - var genericNameSyntax = await GenericNameAccountingForReducedParametersAsync(node, symbol); - return await AdjustForImplicitInvocationAsync(node, genericNameSyntax); - } - - /// - /// Adjusts for Visual Basic's omission of type arguments that can be inferred in reduced generic method invocations - /// The upfront WithExpandedRootAsync pass should ensure this only happens on broken syntax trees. - /// In those cases, just comment the errant information. It would only cause a compiling change in behaviour if it can be inferred, was not set to the inferred value, and was reflected upon within the method body - /// - private async Task GenericNameAccountingForReducedParametersAsync(VBSyntax.GenericNameSyntax node, ISymbol symbol) - { - SyntaxToken convertedIdentifier = ConvertIdentifier(node.Identifier); - if (symbol is IMethodSymbol vbMethod && vbMethod.IsReducedTypeParameterMethod()) { - var allTypeArgs = GetOrNullAllTypeArgsIncludingInferred(vbMethod); - if (allTypeArgs != null) { - return (SimpleNameSyntax)CommonConversions.CsSyntaxGenerator.GenericName(convertedIdentifier.Text, allTypeArgs); - } - var commentedText = "/* " + (await ConvertTypeArgumentListAsync(node)).ToFullString() + " */"; - var error = SyntaxFactory.ParseLeadingTrivia($"#error Conversion error: Could not convert all type parameters, so they've been commented out. Inferred type may be different{Environment.NewLine}"); - var partialConversion = SyntaxFactory.Comment(commentedText); - return ValidSyntaxFactory.IdentifierName(convertedIdentifier).WithPrependedLeadingTrivia(error).WithTrailingTrivia(partialConversion); - } - - return SyntaxFactory.GenericName(convertedIdentifier, await ConvertTypeArgumentListAsync(node)); - } - - /// TODO: Would be more robust to use - private ITypeSymbol[] GetOrNullAllTypeArgsIncludingInferred(IMethodSymbol vbMethod) - { - if (!(CommonConversions.GetCsOriginalSymbolOrNull(vbMethod) is IMethodSymbol - csSymbolWithInferredTypeParametersSet)) return null; - var argSubstitutions = vbMethod.TypeParameters - .Zip(vbMethod.TypeArguments, (parameter, arg) => (parameter, arg)) - .ToDictionary(x => x.parameter.Name, x => x.arg); - var allTypeArgs = csSymbolWithInferredTypeParametersSet.GetTypeArguments() - .Select(a => a.Kind == SymbolKind.TypeParameter && argSubstitutions.TryGetValue(a.Name, out var t) ? t : a) - .ToArray(); - return allTypeArgs; - } - - private async Task ConvertTypeArgumentListAsync(VBSyntax.GenericNameSyntax node) - { - return await node.TypeArgumentList.AcceptAsync(TriviaConvertingExpressionVisitor); - } - - public override async Task VisitTypeArgumentList(VBasic.Syntax.TypeArgumentListSyntax node) - { - var args = await node.Arguments.SelectAsync(async a => await a.AcceptAsync(TriviaConvertingExpressionVisitor)); - return SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(args)); - } - - private async Task ConvertCastExpressionAsync(VBSyntax.CastExpressionSyntax node, - ExpressionSyntax convertMethodOrNull = null, VBSyntax.TypeSyntax castToOrNull = null) - { - var simplifiedOrNull = await WithRemovedRedundantConversionOrNullAsync(node, node.Expression); - if (simplifiedOrNull != null) return simplifiedOrNull; - var expressionSyntax = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor); - if (_semanticModel.GetOperation(node) is not IConversionOperation { Conversion.IsIdentity: true }) { - if (convertMethodOrNull != null) { - expressionSyntax = Invoke(convertMethodOrNull, expressionSyntax); - } - - if (castToOrNull != null) { - expressionSyntax = await CastAsync(expressionSyntax, castToOrNull); - expressionSyntax = node.ParenthesizeIfPrecedenceCouldChange(expressionSyntax); - } - } - - return expressionSyntax; - } - - private async Task CastAsync(ExpressionSyntax expressionSyntax, VBSyntax.TypeSyntax typeSyntax) - { - return ValidSyntaxFactory.CastExpression(await typeSyntax.AcceptAsync(TriviaConvertingExpressionVisitor), expressionSyntax); - } - - private static InvocationExpressionSyntax Invoke(ExpressionSyntax toInvoke, ExpressionSyntax argExpression) - { - return - SyntaxFactory.InvocationExpression(toInvoke, - SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Argument(argExpression))) - ); - } - - private ExpressionSyntax GetConvertMethodForKeywordOrNull(SyntaxNode type) - { - var targetType = _semanticModel.GetTypeInfo(type).Type; - return GetConvertMethodForKeywordOrNull(targetType); - } - - private ExpressionSyntax GetConvertMethodForKeywordOrNull(ITypeSymbol targetType) - { - _extraUsingDirectives.Add(ConvertType.Namespace); - return targetType != null && - _convertMethodsLookupByReturnType.Value.TryGetValue(targetType, out var convertMethodName) - ? SyntaxFactory.ParseExpression(convertMethodName) - : null; - } - - private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node) - { - var firstPossiblyConditionalAncestor = node.Parent; - while (firstPossiblyConditionalAncestor != null && - firstPossiblyConditionalAncestor.IsKind(VBasic.SyntaxKind.InvocationExpression, - VBasic.SyntaxKind.SimpleMemberAccessExpression)) { - firstPossiblyConditionalAncestor = firstPossiblyConditionalAncestor.Parent; - } - - return firstPossiblyConditionalAncestor?.IsKind(VBasic.SyntaxKind.ConditionalAccessExpression) == true; - } - - private async Task> ConvertArgumentsAsync(VBasic.Syntax.ArgumentListSyntax node) - { - ISymbol invocationSymbol = GetInvocationSymbol(node.Parent); - var forceNamedParameters = false; - var invocationHasOverloads = invocationSymbol.HasOverloads(); - - var processedParameters = new HashSet(StringComparer.OrdinalIgnoreCase); - var argumentSyntaxs = (await node.Arguments.SelectAsync(ConvertArg)).Where(a => a != null); - return argumentSyntaxs.Concat(GetAdditionalRequiredArgs(node.Arguments, processedParameters, invocationSymbol, invocationHasOverloads)); - - async Task ConvertArg(VBSyntax.ArgumentSyntax arg, int argIndex) - { - var argName = arg is VBSyntax.SimpleArgumentSyntax { IsNamed: true } namedArg ? namedArg.NameColonEquals.Name.Identifier.Text : null; - var parameterSymbol = invocationSymbol?.GetParameters().GetArgument(argName, argIndex); - var convertedArg = await ConvertArgForParameter(arg, parameterSymbol); - - if (convertedArg is not null && parameterSymbol is not null) { - processedParameters.Add(parameterSymbol.Name); - } - - return convertedArg; - } - - async Task ConvertArgForParameter(VBSyntax.ArgumentSyntax arg, IParameterSymbol parameterSymbol) - { - if (arg.IsOmitted) { - if (invocationSymbol != null && !invocationHasOverloads) { - forceNamedParameters = true; - return null; //Prefer to skip omitted and use named parameters when the symbol has only one overload - } - return ConvertOmittedArgument(parameterSymbol); - } - - var argSyntax = await arg.AcceptAsync(TriviaConvertingExpressionVisitor); - if (forceNamedParameters && !arg.IsNamed && parameterSymbol != null) { - return argSyntax.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(CommonConversions.CsEscapedIdentifier(parameterSymbol.Name)))); - } - - return argSyntax; - } - - ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter) - { - if (parameter == null) { - return SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression)); - } - - var csRefKind = CommonConversions.GetCsRefKind(parameter); - return csRefKind != RefKind.None - ? CreateOptionalRefArg(parameter, csRefKind) - : SyntaxFactory.Argument(CommonConversions.Literal(parameter.ExplicitDefaultValue)); - } - } - - private IEnumerable GetAdditionalRequiredArgs( - IEnumerable arguments, - ISymbol invocationSymbol) - { - var invocationHasOverloads = invocationSymbol.HasOverloads(); - return GetAdditionalRequiredArgs(arguments, processedParametersNames: null, invocationSymbol, invocationHasOverloads); - } - - private IEnumerable GetAdditionalRequiredArgs( - IEnumerable arguments, - ICollection processedParametersNames, - ISymbol invocationSymbol, - bool invocationHasOverloads) - { - if (invocationSymbol is null) { - yield break; - } - - var invocationHasOmittedArgs = arguments.Any(t => t.IsOmitted); - var expandOptionalArgs = invocationHasOmittedArgs && invocationHasOverloads; - var missingArgs = invocationSymbol.GetParameters().Where(t => processedParametersNames is null || !processedParametersNames.Contains(t.Name)); - var requiresCompareMethod = _visualBasicEqualityComparison.OptionCompareTextCaseInsensitive && RequiresStringCompareMethodToBeAppended(invocationSymbol); - - foreach (var parameterSymbol in missingArgs) { - var extraArg = CreateExtraArgOrNull(parameterSymbol, requiresCompareMethod, expandOptionalArgs); - if (extraArg != null) { - yield return extraArg; - } - } - } - - private ArgumentSyntax CreateExtraArgOrNull(IParameterSymbol p, bool requiresCompareMethod, bool expandOptionalArgs) - { - var csRefKind = CommonConversions.GetCsRefKind(p); - if (csRefKind != RefKind.None) { - return CreateOptionalRefArg(p, csRefKind); - } - - if (requiresCompareMethod && p.Type.GetFullMetadataName() == "Microsoft.VisualBasic.CompareMethod") { - return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, _visualBasicEqualityComparison.CompareMethodExpression); - } - - if (expandOptionalArgs && p.HasExplicitDefaultValue) { - return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, CommonConversions.Literal(p.ExplicitDefaultValue)); - } - - return null; - } - - private ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind refKind) - { - string prefix = $"arg{p.Name}"; - var type = CommonConversions.GetTypeSyntax(p.Type); - ExpressionSyntax initializer; - if (p.HasExplicitDefaultValue) { - initializer = CommonConversions.Literal(p.ExplicitDefaultValue); - } else if (HasOptionalAttribute(p)) { - if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)){ - initializer = CommonConversions.Literal(defaultValue); - } else { - initializer = SyntaxFactory.DefaultExpression(type); - } - } else { - //invalid VB.NET code - return null; - } - var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, initializer, type)); - return (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, refKind, local.IdentifierName); - - bool HasOptionalAttribute(IParameterSymbol p) - { - var optionalAttribute = CommonConversions.KnownTypes.OptionalAttribute; - if (optionalAttribute == null) { - return false; - } - - return p.GetAttributes().Any(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, optionalAttribute)); - } - - bool TryGetDefaultParameterValueAttributeValue(IParameterSymbol p, out object defaultValue) - { - defaultValue = null; - - var defaultParameterValueAttribute = CommonConversions.KnownTypes.DefaultParameterValueAttribute; - if (defaultParameterValueAttribute == null) { - return false; - } - - var attributeData = p.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, defaultParameterValueAttribute)); - if (attributeData == null) { - return false; - } - - if (attributeData.ConstructorArguments.Length == 0) { - return false; - } - - defaultValue = attributeData.ConstructorArguments.First().Value; - return true; - } - } - - private RefConversion NeedsVariableForArgument(VBasic.Syntax.ArgumentSyntax node, RefKind refKind) - { - if (refKind == RefKind.None) return RefConversion.Inline; - if (!(node is VBSyntax.SimpleArgumentSyntax sas) || sas is { Expression: VBSyntax.ParenthesizedExpressionSyntax }) return RefConversion.PreAssigment; - var expression = sas.Expression; - - return GetRefConversion(expression); - - RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression) - { - var symbolInfo = GetSymbolInfoInDocument(expression); - if (symbolInfo is IPropertySymbol { ReturnsByRef: false, ReturnsByRefReadonly: false } propertySymbol) { - // a property in VB.NET code can be ReturnsByRef if it's defined in a C# assembly the VB.NET code references - return propertySymbol.IsReadOnly ? RefConversion.PreAssigment : RefConversion.PreAndPostAssignment; - } - else if (symbolInfo is IFieldSymbol { IsConst: true } or ILocalSymbol { IsConst: true }) { - return RefConversion.PreAssigment; - } else if (symbolInfo is IMethodSymbol { ReturnsByRef: false, ReturnsByRefReadonly: false }) { - // a method in VB.NET code can be ReturnsByRef if it's defined in a C# assembly the VB.NET code references - return RefConversion.PreAssigment; - } - - if (DeclaredInUsing(symbolInfo)) return RefConversion.PreAssigment; - - if (expression is VBasic.Syntax.IdentifierNameSyntax || expression is VBSyntax.MemberAccessExpressionSyntax || - IsRefArrayAcces(expression)) { - - var typeInfo = _semanticModel.GetTypeInfo(expression); - bool isTypeMismatch = typeInfo.Type == null || !typeInfo.Type.Equals(typeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability); - - if (isTypeMismatch) { - return RefConversion.PreAndPostAssignment; - } - - return RefConversion.Inline; - } - - return RefConversion.PreAssigment; - } - - bool IsRefArrayAcces(VBSyntax.ExpressionSyntax expression) - { - if (!(expression is VBSyntax.InvocationExpressionSyntax ies)) return false; - var op = _semanticModel.GetOperation(ies); - return (op.IsArrayElementAccess() || IsReturnsByRefPropertyElementAccess(op)) - && GetRefConversion(ies.Expression) == RefConversion.Inline; - - static bool IsReturnsByRefPropertyElementAccess(IOperation op) - { - return op.IsPropertyElementAccess() - && op is IPropertyReferenceOperation { Property: { } prop } - && (prop.ReturnsByRef || prop.ReturnsByRefReadonly); - } - } - } - - private static bool DeclaredInUsing(ISymbol symbolInfo) - { - return symbolInfo?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax()?.Parent?.Parent?.IsKind(VBasic.SyntaxKind.UsingStatement) == true; - } - - /// - /// https://github.com/icsharpcode/CodeConverter/issues/324 - /// https://github.com/icsharpcode/CodeConverter/issues/310 - /// - private enum RefConversion - { - /// - /// e.g. Normal field, parameter or local - /// - Inline, - /// - /// Needs assignment before and/or after - /// e.g. Method/Property result - /// - PreAssigment, - /// - /// Needs assignment before and/or after - /// i.e. Property - /// - PreAndPostAssignment - } - - private ISymbol GetInvocationSymbol(SyntaxNode invocation) - { - var symbol = invocation.TypeSwitch( - (VBSyntax.InvocationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch(), - (VBSyntax.ObjectCreationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch(), - (VBSyntax.RaiseEventStatementSyntax e) => _semanticModel.GetSymbolInfo(e.Name).ExtractBestMatch(), - (VBSyntax.MidExpressionSyntax _) => CommonConversions.KnownTypes.VbCompilerStringType?.GetMembers("MidStmtStr").FirstOrDefault(), - _ => throw new NotSupportedException()); - return symbol; - } - - private async Task ToAttributeArgumentAsync(VBasic.Syntax.ArgumentSyntax arg) - { - if (!(arg is VBasic.Syntax.SimpleArgumentSyntax)) - throw new NotSupportedException(); - var a = (VBasic.Syntax.SimpleArgumentSyntax)arg; - var attr = SyntaxFactory.AttributeArgument(await a.Expression.AcceptAsync(TriviaConvertingExpressionVisitor)); - if (a.IsNamed) { - attr = attr.WithNameEquals(SyntaxFactory.NameEquals(await a.NameColonEquals.Name.AcceptAsync(TriviaConvertingExpressionVisitor))); - } - return attr; - } - - private SyntaxToken ConvertIdentifier(SyntaxToken identifierIdentifier, bool isAttribute = false) - { - return CommonConversions.ConvertIdentifier(identifierIdentifier, isAttribute); - } - - private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier) - { - return expr.ReplaceToken(idToken, SyntaxFactory.Identifier(overrideIdentifier).WithTriviaFrom(idToken).WithAdditionalAnnotations(idToken.GetAnnotations())); - } - - /// - /// If there's a single numeric arg, let's assume it's an indexer (probably an array). - /// Otherwise, err on the side of a method call. - /// - private bool ProbablyNotAMethodCall(VBasic.Syntax.InvocationExpressionSyntax node, ISymbol symbol, ITypeSymbol symbolReturnType) - { - return !node.IsParentKind(VBasic.SyntaxKind.CallStatement) && !(symbol is IMethodSymbol) && - symbolReturnType.IsErrorType() && node.Expression is VBasic.Syntax.IdentifierNameSyntax && - node.ArgumentList?.Arguments.OnlyOrDefault()?.GetExpression() is {} arg && - _semanticModel.GetTypeInfo(arg).Type.IsNumericType(); - } - - private async Task ConvertArgumentListOrEmptyAsync(SyntaxNode node, VBSyntax.ArgumentListSyntax argumentList) - { - return await argumentList.AcceptAsync(TriviaConvertingExpressionVisitor) ?? CreateArgList(_semanticModel.GetSymbolInfo(node).Symbol); - } - - private ArgumentListSyntax CreateArgList(ISymbol invocationSymbol) - { - return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( - GetAdditionalRequiredArgs(Array.Empty(), invocationSymbol)) - ); - } - - private async Task SubstituteVisualBasicMethodOrNullAsync(VBSyntax.InvocationExpressionSyntax node, ISymbol symbol) - { - ExpressionSyntax cSharpSyntaxNode = null; - if (IsVisualBasicChrMethod(symbol)) { - var vbArg = node.ArgumentList.Arguments.Single().GetExpression(); - var constValue = _semanticModel.GetConstantValue(vbArg); - if (IsCultureInvariant(constValue)) { - var csArg = await vbArg.AcceptAsync(TriviaConvertingExpressionVisitor); - cSharpSyntaxNode = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node, csArg, true, true, true, forceTargetType: _semanticModel.GetTypeInfo(node).Type); - } - } - - if (SimpleMethodReplacement.TryGet(symbol, out var methodReplacement) && - methodReplacement.ReplaceIfMatches(symbol, await ConvertArgumentsAsync(node.ArgumentList), false) is {} csExpression) { - cSharpSyntaxNode = csExpression; - } - - return cSharpSyntaxNode; - } - - - private static bool RequiresStringCompareMethodToBeAppended(ISymbol symbol) => - symbol?.ContainingType.Name == nameof(Strings) && - symbol.ContainingType.ContainingNamespace.Name == nameof(Microsoft.VisualBasic) && - symbol.ContainingType.ContainingNamespace.ContainingNamespace.Name == nameof(Microsoft) && - symbol.Name is "InStr" or "InStrRev" or "Replace" or "Split" or "StrComp"; - - private static bool IsVisualBasicChrMethod(ISymbol symbol) => - symbol is not null - && symbol.ContainingNamespace.MetadataName == nameof(Microsoft.VisualBasic) - && (symbol.Name == "ChrW" || symbol.Name == "Chr"); - - /// - /// https://github.com/icsharpcode/CodeConverter/issues/745 - /// - private static bool IsCultureInvariant(Optional constValue) => - constValue.HasValue && Convert.ToUInt64(constValue.Value, CultureInfo.InvariantCulture) <= 127; - - private CSharpSyntaxNode AddEmptyArgumentListIfImplicit(SyntaxNode node, ExpressionSyntax id) - { - if (_semanticModel.SyntaxTree != node.SyntaxTree) return id; - return _semanticModel.GetOperation(node) switch { - IInvocationOperation invocation => SyntaxFactory.InvocationExpression(id, CreateArgList(invocation.TargetMethod)), - IPropertyReferenceOperation propReference when propReference.Property.Parameters.Any() => SyntaxFactory.InvocationExpression(id, CreateArgList(propReference.Property)), - _ => id - }; - } - - /// - /// The pre-expansion phase should handle this for compiling nodes. - /// This is mainly targeted at dealing with missing semantic info. - /// - /// - private ExpressionSyntax QualifyNode(SyntaxNode node, SimpleNameSyntax left) - { - var nodeSymbolInfo = GetSymbolInfoInDocument(node); - if (left != null && - nodeSymbolInfo != null && - nodeSymbolInfo.MatchesKind(SymbolKind.TypeParameter) == false && - nodeSymbolInfo.ContainingSymbol is INamespaceOrTypeSymbol containingSymbol && - !ContextImplicitlyQualfiesSymbol(node, containingSymbol)) { - - if (containingSymbol is ITypeSymbol containingTypeSymbol && - !nodeSymbolInfo.IsConstructor() /* Constructors are implicitly qualified with their type */) { - // Qualify with a type to handle VB's type promotion https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/declared-elements/type-promotion - var qualification = - CommonConversions.GetTypeSyntax(containingTypeSymbol); - return Qualify(qualification.ToString(), left); - } - - if (nodeSymbolInfo.IsNamespace()) { - // Turn partial namespace qualification into full namespace qualification - var qualification = - containingSymbol.ToCSharpDisplayString(); - return Qualify(qualification, left); - } - } - - return left; - } - - private bool ContextImplicitlyQualfiesSymbol(SyntaxNode syntaxNodeContext, INamespaceOrTypeSymbol symbolToCheck) - { - return symbolToCheck is INamespaceSymbol ns && ns.IsGlobalNamespace || - EnclosingTypeImplicitlyQualifiesSymbol(syntaxNodeContext, symbolToCheck); - } - - private bool EnclosingTypeImplicitlyQualifiesSymbol(SyntaxNode syntaxNodeContext, INamespaceOrTypeSymbol symbolToCheck) - { - ISymbol typeContext = syntaxNodeContext.GetEnclosingDeclaredTypeSymbol(_semanticModel); - var implicitCsQualifications = ((ITypeSymbol)typeContext).GetBaseTypesAndThis() - .Concat(typeContext.FollowProperty(n => n.ContainingSymbol)) - .ToList(); - - return implicitCsQualifications.Contains(symbolToCheck); - } - - private static QualifiedNameSyntax Qualify(string qualification, ExpressionSyntax toBeQualified) - { - return SyntaxFactory.QualifiedName( - SyntaxFactory.ParseName(qualification), - (SimpleNameSyntax)toBeQualified); - } - - /// The ISymbol if available in this document, otherwise null - /// It's possible to use _semanticModel.GetSpeculativeSymbolInfo(...) if you know (or can approximate) the position where the symbol would have been in the original document. - private TSymbol GetSymbolInfoInDocument(SyntaxNode node) where TSymbol: class, ISymbol - { - return _semanticModel.SyntaxTree == node.SyntaxTree ? _semanticModel.GetSymbolInfo(node).ExtractBestMatch(): null; - } -} \ No newline at end of file + private TSymbol GetSymbolInfoInDocument(SyntaxNode node) where TSymbol: class, ISymbol => _semanticModel.SyntaxTree == node.SyntaxTree ? _semanticModel.GetSymbolInfo(node).ExtractBestMatch(): null; +} diff --git a/Tests/CSharp/MemberTests/MemberTests.cs b/Tests/CSharp/MemberTests/MemberTests.cs index 0ac91a1a..62bcbacf 100644 --- a/Tests/CSharp/MemberTests/MemberTests.cs +++ b/Tests/CSharp/MemberTests/MemberTests.cs @@ -6,84 +6,6 @@ namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests; public class MemberTests : ConverterTestBase { - [Fact] - public async Task TestFieldAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Const answer As Integer = 42 - Private value As Integer = 10 - ReadOnly v As Integer = 15 -End Class", @" -internal partial class TestClass -{ - private const int answer = 42; - private int value = 10; - private readonly int v = 15; -}"); - } - - [Fact] - public async Task TestMultiArrayFieldAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Dim Parts(), Taxes(), Deposits()(), Prepaid()(), FromDate, ToDate As String -End Class", @" -internal partial class TestClass -{ - private string[] Parts, Taxes; - private string[][] Deposits, Prepaid; - private string FromDate, ToDate; -}"); - } - - [Fact] - public async Task TestConstantFieldInModuleAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module TestModule - Const answer As Integer = 42 -End Module", @" -internal static partial class TestModule -{ - private const int answer = 42; -}"); - } - - [Fact] - public async Task TestConstructorVisibilityAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class Class1 - Sub New(x As Boolean) - End Sub -End Class", @" -internal partial class Class1 -{ - public Class1(bool x) - { - } -}"); - } - - [Fact] - public async Task TestModuleConstructorAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module Module1 - Sub New() - Dim someValue As Integer = 0 - End Sub -End Module", @" -internal static partial class Module1 -{ - static Module1() - { - int someValue = 0; - } -}"); - } - [Fact] public async Task TestDeclareMethodVisibilityInModuleAsync() { @@ -112,59 +34,6 @@ internal partial class Class1 }"); } - [Fact] - public async Task TestTypeInferredConstAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Const someConstField = 42 - Sub TestMethod() - Const someConst = System.DateTimeKind.Local - End Sub -End Class", @"using System; - -internal partial class TestClass -{ - private const int someConstField = 42; - public void TestMethod() - { - const DateTimeKind someConst = DateTimeKind.Local; - } -}"); - } - - [Fact] - public async Task TestTypeInferredVarAsync() - { - // VB doesn't infer the type of EnumVariable like you'd think, it just uses object - // VB compiler uses Conversions rather than any plainer casting - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Public Enum TestEnum As Integer - Test1 - End Enum - - Dim EnumVariable = TestEnum.Test1 - Public Sub AMethod() - Dim t1 As Integer = EnumVariable - End Sub -End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic - -internal partial class TestClass -{ - public enum TestEnum : int - { - Test1 - } - - private object EnumVariable = TestEnum.Test1; - public void AMethod() - { - int t1 = Conversions.ToInteger(EnumVariable); - } -}"); - } - [Fact] public async Task TestMethodAsync() { @@ -527,3348 +396,511 @@ public static void TestMethod(out T argument, ref T2 argument2, T3 ar } [Fact] - public async Task TestAbstractMethodAndPropertyAsync() + public async Task TestSealedMethodAsync() { await TestConversionVisualBasicToCSharpAsync( - @"MustInherit Class TestClass - Public MustOverride Sub TestMethod() - Public MustOverride ReadOnly Property AbstractProperty As String + @"Class TestClass + Public NotOverridable Sub TestMethod(Of T As {Class, New}, T2 As Structure, T3)( ByRef argument As T, ByRef argument2 As T2, ByVal argument3 As T3) + argument = Nothing + argument2 = Nothing + argument3 = Nothing + End Sub End Class", @" -internal abstract partial class TestClass +internal partial class TestClass { - public abstract void TestMethod(); - public abstract string AbstractProperty { get; } -}"); + public sealed void TestMethod(out T argument, ref T2 argument2, T3 argument3) + where T : class, new() + where T2 : struct + { + argument = null; + argument2 = default; + argument3 = default; + } +} +1 source compilation errors: +BC31088: 'NotOverridable' cannot be specified for methods that do not override another method. +1 target compilation errors: +CS0238: 'TestClass.TestMethod(out T, ref T2, T3)' cannot be sealed because it is not an override"); } [Fact] - public async Task TestAbstractReadOnlyAndWriteOnlyPropertyAsync() + public async Task TestShadowedMethodAsync() { await TestConversionVisualBasicToCSharpAsync( - @"MustInherit Class TestClass - Public MustOverride ReadOnly Property ReadOnlyProp As String - Public MustOverride WriteOnly Property WriteOnlyProp As String + @"Class TestClass + Public Sub TestMethod() + End Sub + + Public Sub TestMethod(i as Integer) + End Sub End Class -Class ChildClass +Class TestSubclass Inherits TestClass - Public Overrides ReadOnly Property ReadOnlyProp As String - Public Overrides WriteOnly Property WriteOnlyProp As String - Set - End Set - End Property -End Class -", @" -internal abstract partial class TestClass + Public Shadows Sub TestMethod() + ' Not possible: TestMethod(3) + System.Console.WriteLine(""New implementation"") + End Sub +End Class", @"using System; + +internal partial class TestClass { - public abstract string ReadOnlyProp { get; } - public abstract string WriteOnlyProp { set; } + public void TestMethod() + { + } + + public void TestMethod(int i) + { + } } -internal partial class ChildClass : TestClass +internal partial class TestSubclass : TestClass { - public override string ReadOnlyProp { get; } - public override string WriteOnlyProp + public new void TestMethod() { - set - { - } + // Not possible: TestMethod(3) + Console.WriteLine(""New implementation""); } }"); } [Fact] - public async Task TestReadOnlyOrWriteOnlyPropertyImplementedByNormalPropertyAsync() + public async Task TestDestructorAsync() { await TestConversionVisualBasicToCSharpAsync( - @" -Interface IClass - ReadOnly Property ReadOnlyPropParam(i as Integer) As Integer - ReadOnly Property ReadOnlyProp As Integer - - WriteOnly Property WriteOnlyPropParam(i as Integer) As Integer - WriteOnly Property WriteOnlyProp As Integer -End Interface - -Class ChildClass - Implements IClass - - Public Overridable Property RenamedPropertyParam(i As Integer) As Integer Implements IClass.ReadOnlyPropParam - Get - Return 1 - End Get - Set - End Set - End Property + @"Class TestClass + Protected Overrides Sub Finalize() + End Sub +End Class", @" +internal partial class TestClass +{ + ~TestClass() + { + } +}"); + } - Public Overridable Property RenamedReadOnlyProperty As Integer Implements IClass.ReadOnlyProp ' Comment moves because this line gets split - Get - Return 2 - End Get - Set - End Set - End Property + [Fact] + public async Task Issue681_OverloadsOverridesPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Class C + Inherits B - Public Overridable Property RenamedWriteOnlyPropParam(i As Integer) As Integer Implements IClass.WriteOnlyPropParam + Public ReadOnly Overloads Overrides Property X() Get - Return 1 + Return Nothing End Get - Set - End Set End Property +End Class - Public Overridable Property RenamedWriteOnlyProperty As Integer Implements IClass.WriteOnlyProp ' Comment moves because this line gets split +Public Class B + Public ReadOnly Overridable Property X() Get - Return 2 + Return Nothing End Get - Set - End Set End Property -End Class -", @" -internal partial interface IClass -{ - int get_ReadOnlyPropParam(int i); - int ReadOnlyProp { get; } - - void set_WriteOnlyPropParam(int i, int value); - int WriteOnlyProp { set; } -} - -internal partial class ChildClass : IClass +End Class", @"public partial class C : B { - public virtual int get_RenamedPropertyParam(int i) - { - return 1; - } - public virtual void set_RenamedPropertyParam(int i, int value) + public override object X { + get + { + return null; + } } - int IClass.get_ReadOnlyPropParam(int i) => get_RenamedPropertyParam(i); +} - public virtual int RenamedReadOnlyProperty +public partial class B +{ + public virtual object X { get { - return 2; - } - set - { + return null; } } +}"); + } + + [Fact] + public async Task PartialFriendClassWithOverloadsAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Partial Friend MustInherit Class TestClass1 + Public Shared Sub CreateStatic() + End Sub + + Public Overloads Sub CreateInstance() + End Sub + + Public Overloads Sub CreateInstance(o As Object) + End Sub + + Public MustOverride Sub CreateAbstractInstance() + + Public Overridable Sub CreateVirtualInstance() + End Sub +End Class + +Friend Class TestClass2 + Inherits TestClass1 + Public Overloads Shared Sub CreateStatic() + End Sub - int IClass.ReadOnlyProp { get => RenamedReadOnlyProperty; } // Comment moves because this line gets split + Public Overloads Sub CreateInstance() + End Sub + + Public Overrides Sub CreateAbstractInstance() + End Sub - public virtual int get_RenamedWriteOnlyPropParam(int i) + Public Overloads Sub CreateVirtualInstance(o As Object) + End Sub +End Class", + @" +internal abstract partial class TestClass1 +{ + public static void CreateStatic() { - return 1; } - public virtual void set_RenamedWriteOnlyPropParam(int i, int value) + + public void CreateInstance() { } - void IClass.set_WriteOnlyPropParam(int i, int value) => set_RenamedWriteOnlyPropParam(i, value); - public virtual int RenamedWriteOnlyProperty + public void CreateInstance(object o) { - get - { - return 2; - } - set - { - } } - int IClass.WriteOnlyProp { set => RenamedWriteOnlyProperty = value; } // Comment moves because this line gets split -}"); - } + public abstract void CreateAbstractInstance(); - [Fact] - public async Task SetterProperty1053Async() + public virtual void CreateVirtualInstance() { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Property Prop(ByVal i As Integer) As String - Get - Static bGet As Boolean - bGet = False - End Get + } +} - Set(ByVal s As String) - Static bSet As Boolean - bSet = False - End Set -End Property -", @" -internal partial class SurroundingClass +internal partial class TestClass2 : TestClass1 { - private bool _Prop_bGet; - private bool _Prop_bSet; + public new static void CreateStatic() + { + } - public string get_Prop(int i) + public new void CreateInstance() { - _Prop_bGet = false; - return default; } - public void set_Prop(int i, string value) + public override void CreateAbstractInstance() { - _Prop_bSet = false; } + public void CreateVirtualInstance(object o) + { + } }"); } [Fact] - public async Task StaticLocalsInPropertyGetterAndSetterAsync() + public async Task ClassWithGloballyQualifiedAttributeAsync() { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Property Prop As String - Get - Static b As Boolean - b = True - End Get - - Set(ByVal s As String) - Static b As Boolean - b = False - End Set -End Property -", @" -internal partial class SurroundingClass -{ - private bool _Prop_b; - private bool _Prop_b1; - - public string Prop - { - get - { - _Prop_b = true; - return default; - } - - set - { - _Prop_b1 = false; - } - } - -}"); - } - - [Fact] - public async Task TestReadOnlyAndWriteOnlyParametrizedPropertyAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Interface IClass - ReadOnly Property ReadOnlyProp(i as Integer) As String - WriteOnly Property WriteOnlyProp(i as Integer) As String -End Interface - -Class ChildClass - Implements IClass - - Public Overridable ReadOnly Property ReadOnlyProp(i As Integer) As String Implements IClass.ReadOnlyProp - Get - Throw New NotImplementedException - End Get - End Property - - Public Overridable WriteOnly Property WriteOnlyProp(i As Integer) As String Implements IClass.WriteOnlyProp - Set - Throw New NotImplementedException - End Set - End Property -End Class -", @"using System; - -internal partial interface IClass -{ - string get_ReadOnlyProp(int i); - void set_WriteOnlyProp(int i, string value); -} - -internal partial class ChildClass : IClass -{ - - public virtual string get_ReadOnlyProp(int i) - { - throw new NotImplementedException(); - } - - public virtual void set_WriteOnlyProp(int i, string value) - { - throw new NotImplementedException(); - } -}"); - } - - [Fact] - public async Task TestExplicitInterfaceOfParametrizedPropertyAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Interface IClass - ReadOnly Property ReadOnlyPropToRename(i as Integer) As String - WriteOnly Property WriteOnlyPropToRename(i as Integer) As String - Property PropToRename(i as Integer) As String - - ReadOnly Property ReadOnlyPropNonPublic(i as Integer) As String - WriteOnly Property WriteOnlyPropNonPublic(i as Integer) As String - Property PropNonPublic(i as Integer) As String - - ReadOnly Property ReadOnlyPropToRenameNonPublic(i as Integer) As String - WriteOnly Property WriteOnlyPropToRenameNonPublic(i as Integer) As String - Property PropToRenameNonPublic(i as Integer) As String - -End Interface - -Class ChildClass - Implements IClass - - Public ReadOnly Property ReadOnlyPropRenamed(i As Integer) As String Implements IClass.ReadOnlyPropToRename - Get - Throw New NotImplementedException - End Get - End Property - - Public Overridable WriteOnly Property WriteOnlyPropRenamed(i As Integer) As String Implements IClass.WriteOnlyPropToRename - Set - Throw New NotImplementedException - End Set - End Property - - Public Overridable Property PropRenamed(i As Integer) As String Implements IClass.PropToRename - Get - Throw New NotImplementedException - End Get - Set - Throw New NotImplementedException - End Set - End Property - - Private ReadOnly Property ReadOnlyPropNonPublic(i As Integer) As String Implements IClass.ReadOnlyPropNonPublic - Get - Throw New NotImplementedException - End Get - End Property - - Protected Friend Overridable WriteOnly Property WriteOnlyPropNonPublic(i As Integer) As String Implements IClass.WriteOnlyPropNonPublic - Set - Throw New NotImplementedException - End Set - End Property - - Friend Overridable Property PropNonPublic(i As Integer) As String Implements IClass.PropNonPublic - Get - Throw New NotImplementedException - End Get - Set - Throw New NotImplementedException - End Set - End Property - - Protected Friend Overridable ReadOnly Property ReadOnlyPropRenamedNonPublic(i As Integer) As String Implements IClass.ReadOnlyPropToRenameNonPublic - Get - Throw New NotImplementedException - End Get - End Property - - Private WriteOnly Property WriteOnlyPropRenamedNonPublic(i As Integer) As String Implements IClass.WriteOnlyPropToRenameNonPublic - Set - Throw New NotImplementedException - End Set - End Property - - Friend Overridable Property PropToRenameNonPublic(i As Integer) As String Implements IClass.PropToRenameNonPublic - Get - Throw New NotImplementedException - End Get - Set - Throw New NotImplementedException - End Set - End Property -End Class -", @"using System; - -internal partial interface IClass -{ - string get_ReadOnlyPropToRename(int i); - void set_WriteOnlyPropToRename(int i, string value); - string get_PropToRename(int i); - void set_PropToRename(int i, string value); - - string get_ReadOnlyPropNonPublic(int i); - void set_WriteOnlyPropNonPublic(int i, string value); - string get_PropNonPublic(int i); - void set_PropNonPublic(int i, string value); - - string get_ReadOnlyPropToRenameNonPublic(int i); - void set_WriteOnlyPropToRenameNonPublic(int i, string value); - string get_PropToRenameNonPublic(int i); - void set_PropToRenameNonPublic(int i, string value); - -} - -internal partial class ChildClass : IClass -{ - - public string get_ReadOnlyPropRenamed(int i) - { - throw new NotImplementedException(); - } - string IClass.get_ReadOnlyPropToRename(int i) => get_ReadOnlyPropRenamed(i); - - public virtual void set_WriteOnlyPropRenamed(int i, string value) - { - throw new NotImplementedException(); - } - void IClass.set_WriteOnlyPropToRename(int i, string value) => set_WriteOnlyPropRenamed(i, value); - - public virtual string get_PropRenamed(int i) - { - throw new NotImplementedException(); - } - public virtual void set_PropRenamed(int i, string value) - { - throw new NotImplementedException(); - } - - string IClass.get_PropToRename(int i) => get_PropRenamed(i); - void IClass.set_PropToRename(int i, string value) => set_PropRenamed(i, value); - - private string get_ReadOnlyPropNonPublic(int i) - { - throw new NotImplementedException(); - } - string IClass.get_ReadOnlyPropNonPublic(int i) => get_ReadOnlyPropNonPublic(i); - - protected internal virtual void set_WriteOnlyPropNonPublic(int i, string value) - { - throw new NotImplementedException(); - } - void IClass.set_WriteOnlyPropNonPublic(int i, string value) => set_WriteOnlyPropNonPublic(i, value); - - internal virtual string get_PropNonPublic(int i) - { - throw new NotImplementedException(); - } - internal virtual void set_PropNonPublic(int i, string value) - { - throw new NotImplementedException(); - } - - string IClass.get_PropNonPublic(int i) => get_PropNonPublic(i); - void IClass.set_PropNonPublic(int i, string value) => set_PropNonPublic(i, value); - - protected internal virtual string get_ReadOnlyPropRenamedNonPublic(int i) - { - throw new NotImplementedException(); - } - string IClass.get_ReadOnlyPropToRenameNonPublic(int i) => get_ReadOnlyPropRenamedNonPublic(i); - - private void set_WriteOnlyPropRenamedNonPublic(int i, string value) - { - throw new NotImplementedException(); - } - void IClass.set_WriteOnlyPropToRenameNonPublic(int i, string value) => set_WriteOnlyPropRenamedNonPublic(i, value); - - internal virtual string get_PropToRenameNonPublic(int i) - { - throw new NotImplementedException(); - } - internal virtual void set_PropToRenameNonPublic(int i, string value) - { - throw new NotImplementedException(); - } - - string IClass.get_PropToRenameNonPublic(int i) => get_PropToRenameNonPublic(i); - void IClass.set_PropToRenameNonPublic(int i, string value) => set_PropToRenameNonPublic(i, value); -}"); - } - - [Fact] - public async Task TestSealedMethodAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Public NotOverridable Sub TestMethod(Of T As {Class, New}, T2 As Structure, T3)( ByRef argument As T, ByRef argument2 As T2, ByVal argument3 As T3) - argument = Nothing - argument2 = Nothing - argument3 = Nothing - End Sub -End Class", @" -internal partial class TestClass -{ - public sealed void TestMethod(out T argument, ref T2 argument2, T3 argument3) - where T : class, new() - where T2 : struct - { - argument = null; - argument2 = default; - argument3 = default; - } -} -1 source compilation errors: -BC31088: 'NotOverridable' cannot be specified for methods that do not override another method. -1 target compilation errors: -CS0238: 'TestClass.TestMethod(out T, ref T2, T3)' cannot be sealed because it is not an override"); - } - - [Fact] - public async Task TestShadowedMethodAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Public Sub TestMethod() - End Sub - - Public Sub TestMethod(i as Integer) - End Sub -End Class - -Class TestSubclass - Inherits TestClass - - Public Shadows Sub TestMethod() - ' Not possible: TestMethod(3) - System.Console.WriteLine(""New implementation"") - End Sub -End Class", @"using System; - -internal partial class TestClass -{ - public void TestMethod() - { - } - - public void TestMethod(int i) - { - } -} - -internal partial class TestSubclass : TestClass -{ - - public new void TestMethod() - { - // Not possible: TestMethod(3) - Console.WriteLine(""New implementation""); - } -}"); - } - - [Fact] - public async Task TestExtensionMethodAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module TestClass - - Sub TestMethod(ByVal str As String) - End Sub - - - Sub TestMethod2Parameters(ByVal str As String, other As String) - End Sub -End Module", @" -internal static partial class TestClass -{ - public static void TestMethod(this string str) - { - } - - public static void TestMethod2Parameters(this string str, string other) - { - } -}"); - } - - [Fact] - public async Task TestExtensionMethodWithExistingImportAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Imports System.Runtime.CompilerServices ' Removed by simplifier - -Module TestClass - - Sub TestMethod(ByVal str As String) - End Sub -End Module", @" -internal static partial class TestClass -{ - public static void TestMethod(this string str) - { - } -}"); - } - - [Fact] - public async Task TestRefExtensionMethodAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Imports System -Imports System.Runtime.CompilerServices ' Removed since the extension attribute is removed - -Public Module MyExtensions - - Public Sub Add(Of T)(ByRef arr As T(), item As T) - Array.Resize(arr, arr.Length + 1) - arr(arr.Length - 1) = item - End Sub -End Module - -Public Module UsagePoint - Public Sub Main() - Dim arr = New Integer() {1, 2, 3} - arr.Add(4) - System.Console.WriteLine(arr(3)) - End Sub -End Module", @"using System; - -public static partial class MyExtensions -{ - public static void Add(ref T[] arr, T item) - { - Array.Resize(ref arr, arr.Length + 1); - arr[arr.Length - 1] = item; - } -} - -public static partial class UsagePoint -{ - public static void Main() - { - int[] arr = new int[] { 1, 2, 3 }; - MyExtensions.Add(ref arr, 4); - Console.WriteLine(arr[3]); - } -}"); - } - - [Fact] - public async Task TestExtensionWithinExtendedTypeAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module Extensions - - Sub TestExtension(extendedClass As ExtendedClass) - End Sub -End Module - -Class ExtendedClass - Sub TestExtensionConsumer() - TestExtension() - End Sub -End Class", @" -internal static partial class Extensions -{ - public static void TestExtension(this ExtendedClass extendedClass) - { - } -} - -internal partial class ExtendedClass -{ - public void TestExtensionConsumer() - { - this.TestExtension(); - } -}"); - } - - [Fact] - public async Task TestExtensionWithinTypeDerivedFromExtendedTypeAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module Extensions - - Sub TestExtension(extendedClass As ExtendedClass) - End Sub -End Module - -Class ExtendedClass -End Class - -Class DerivedClass - Inherits ExtendedClass - - Sub TestExtensionConsumer() - TestExtension() - End Sub -End Class", @" -internal static partial class Extensions -{ - public static void TestExtension(this ExtendedClass extendedClass) - { - } -} - -internal partial class ExtendedClass -{ -} - -internal partial class DerivedClass : ExtendedClass -{ - - public void TestExtensionConsumer() - { - this.TestExtension(); - } -}"); - } - - [Fact] - public async Task TestExtensionWithinNestedExtendedTypeAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module Extensions - - Sub TestExtension(extendedClass As NestingClass.ExtendedClass) - End Sub -End Module - -Class NestingClass - Class ExtendedClass - Sub TestExtensionConsumer() - TestExtension() - End Sub - End Class -End Class", @" -internal static partial class Extensions -{ - public static void TestExtension(this NestingClass.ExtendedClass extendedClass) - { - } -} - -internal partial class NestingClass -{ - public partial class ExtendedClass - { - public void TestExtensionConsumer() - { - this.TestExtension(); - } - } -}"); - } - - [Fact] - public async Task TestExtensionWithMeWithinExtendedTypeAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Module Extensions - - Sub TestExtension(extendedClass As ExtendedClass) - End Sub -End Module - -Class ExtendedClass - Sub TestExtensionConsumer() - Me.TestExtension() - End Sub -End Class", @" -internal static partial class Extensions -{ - public static void TestExtension(this ExtendedClass extendedClass) - { - } -} - -internal partial class ExtendedClass -{ - public void TestExtensionConsumer() - { - this.TestExtension(); - } -}"); - } - - [Fact] - public async Task TestConstructorAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass(Of T As {Class, New}, T2 As Structure, T3) - Public Sub New( ByRef argument As T, ByRef argument2 As T2, ByVal argument3 As T3) - End Sub -End Class", @" -internal partial class TestClass - where T : class, new() - where T2 : struct -{ - public TestClass(out T argument, ref T2 argument2, T3 argument3) - { - } -} -1 target compilation errors: -CS0177: The out parameter 'argument' must be assigned to before control leaves the current method"); - } - - [Fact] - public async Task TestConstructorWithImplicitPublicAccessibilityAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Sub New() -End Sub", @"public SurroundingClass() -{ -}"); - } - - [Fact] - public async Task TestStaticConstructorAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Shared Sub New() -End Sub", @"static SurroundingClass() -{ -}"); - } - - [Fact] - public async Task TestDestructorAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Protected Overrides Sub Finalize() - End Sub -End Class", @" -internal partial class TestClass -{ - ~TestClass() - { - } -}"); - } - - [Fact] - public async Task Issue681_OverloadsOverridesPropertyAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Class C - Inherits B - - Public ReadOnly Overloads Overrides Property X() - Get - Return Nothing - End Get - End Property -End Class - -Public Class B - Public ReadOnly Overridable Property X() - Get - Return Nothing - End Get - End Property -End Class", @"public partial class C : B -{ - - public override object X - { - get - { - return null; - } - } -} - -public partial class B -{ - public virtual object X - { - get - { - return null; - } - } -}"); - } - - [Fact] - public async Task PartialFriendClassWithOverloadsAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Partial Friend MustInherit Class TestClass1 - Public Shared Sub CreateStatic() - End Sub - - Public Overloads Sub CreateInstance() - End Sub - - Public Overloads Sub CreateInstance(o As Object) - End Sub - - Public MustOverride Sub CreateAbstractInstance() - - Public Overridable Sub CreateVirtualInstance() - End Sub -End Class - -Friend Class TestClass2 - Inherits TestClass1 - Public Overloads Shared Sub CreateStatic() - End Sub - - Public Overloads Sub CreateInstance() - End Sub - - Public Overrides Sub CreateAbstractInstance() - End Sub - - Public Overloads Sub CreateVirtualInstance(o As Object) - End Sub -End Class", - @" -internal abstract partial class TestClass1 -{ - public static void CreateStatic() - { - } - - public void CreateInstance() - { - } - - public void CreateInstance(object o) - { - } - - public abstract void CreateAbstractInstance(); - - public virtual void CreateVirtualInstance() - { - } -} - -internal partial class TestClass2 : TestClass1 -{ - public new static void CreateStatic() - { - } - - public new void CreateInstance() - { - } - - public override void CreateAbstractInstance() - { - } - - public void CreateVirtualInstance(object o) - { - } -}"); - } - - [Fact] - public async Task ClassWithGloballyQualifiedAttributeAsync() - { - await TestConversionVisualBasicToCSharpAsync(@" -Class TestClass -End Class", @"using System.Diagnostics; - -[DebuggerDisplay(""Hello World"")] -internal partial class TestClass -{ -}"); - } - - [Fact] - public async Task FieldWithAttributeAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class TestClass - - Private Shared First As Integer -End Class", @"using System; - -internal partial class TestClass -{ - [ThreadStatic] - private static int First; -}"); - } - - [Fact] - public async Task FieldWithNonStaticInitializerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Class A - Private x As Integer = 2 - Private y(x) As Integer -End Class", @" -public partial class A -{ - private int x = 2; - private int[] y; - - public A() - { - y = new int[x + 1]; - } -}"); - } - - [Fact] - public async Task FieldWithInstanceOperationOfDifferingTypeAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Class DoesNotNeedConstructor - Private ReadOnly ClassVariable1 As New ParallelOptions With {.MaxDegreeOfParallelism = 5} -End Class", @"using System.Threading.Tasks; - -public partial class DoesNotNeedConstructor -{ - private readonly ParallelOptions ClassVariable1 = new ParallelOptions() { MaxDegreeOfParallelism = 5 }; -}"); - } - - [Fact] - public async Task Issue281FieldWithNonStaticLambdaInitializerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Imports System.IO - -Public Class Issue281 - Private lambda As System.Delegate = New ErrorEventHandler(Sub(a, b) Len(0)) - Private nonShared As System.Delegate = New ErrorEventHandler(AddressOf OnError) - - Sub OnError(s As Object, e As ErrorEventArgs) - End Sub -End Class", @"using System; -using System.IO; -using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic - -public partial class Issue281 -{ - private Delegate lambda = new ErrorEventHandler((a, b) => Strings.Len(0)); - private Delegate nonShared; - - public Issue281() - { - nonShared = new ErrorEventHandler(OnError); - } - - public void OnError(object s, ErrorEventArgs e) - { - } -}"); - } - - [Fact] - public async Task ParamArrayAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class TestClass - Private Sub SomeBools(ParamArray anyName As Boolean()) - End Sub -End Class", @" -internal partial class TestClass -{ - private void SomeBools(params bool[] anyName) - { - } -}"); - } - - [Fact] - public async Task ParamNamedBoolAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class TestClass - Private Sub SomeBools(ParamArray bool As Boolean()) - End Sub -End Class", @" -internal partial class TestClass -{ - private void SomeBools(params bool[] @bool) - { - } -}"); - } - - [Fact] - public async Task MethodWithNameArrayParameterAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Public Sub DoNothing(ByVal strs() As String) - Dim moreStrs() As String - End Sub -End Class", - @" -internal partial class TestClass -{ - public void DoNothing(string[] strs) - { - string[] moreStrs; - } -}"); - } - - [Fact] - public async Task UntypedParametersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Public Sub DoNothing(obj, objs()) - End Sub -End Class", - @" -internal partial class TestClass -{ - public void DoNothing(object obj, object[] objs) - { - } -}"); - } - - [Fact] - public async Task PartialClassAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Partial Class TestClass - Private Sub DoNothing() - Console.WriteLine(""Hello"") - End Sub -End Class - -Class TestClass ' VB doesn't require partial here (when just a single class omits it) - Partial Private Sub DoNothing() - End Sub -End Class", - @"using System; - -public partial class TestClass -{ - partial void DoNothing() - { - Console.WriteLine(""Hello""); - } -} - -public partial class TestClass // VB doesn't require partial here (when just a single class omits it) -{ - partial void DoNothing(); -}"); - } - - [Fact] - public async Task NestedClassAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class ClA - Public Shared Sub MA() - ClA.ClassB.MB() - MyClassC.MC() - End Sub - - Public Class ClassB - Public Shared Function MB() as ClassB - ClA.MA() - MyClassC.MC() - Return ClA.ClassB.MB() - End Function - End Class -End Class - -Class MyClassC - Public Shared Sub MC() - ClA.MA() - ClA.ClassB.MB() - End Sub -End Class", @" -internal partial class ClA -{ - public static void MA() - { - ClassB.MB(); - MyClassC.MC(); - } - - public partial class ClassB - { - public static ClassB MB() - { - MA(); - MyClassC.MC(); - return MB(); - } - } -} - -internal partial class MyClassC -{ - public static void MC() - { - ClA.MA(); - ClA.ClassB.MB(); - } -}"); - } - - [Fact] - public async Task LessQualifiedNestedClassAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Class ClA - Public Shared Sub MA() - ClassB.MB() - MyClassC.MC() - End Sub - - Public Class ClassB - Public Shared Function MB() as ClassB - MA() - MyClassC.MC() - Return MB() - End Function - End Class -End Class - -Class MyClassC - Public Shared Sub MC() - ClA.MA() - ClA.ClassB.MB() - End Sub -End Class", @" -internal partial class ClA -{ - public static void MA() - { - ClassB.MB(); - MyClassC.MC(); - } - - public partial class ClassB - { - public static ClassB MB() - { - MA(); - MyClassC.MC(); - return MB(); - } - } -} - -internal partial class MyClassC -{ - public static void MC() - { - ClA.MA(); - ClA.ClassB.MB(); - } -}"); - } - - [Fact] - public async Task TestAsyncMethodsAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" Class AsyncCode - Public Sub NotAsync() - Dim a1 = Async Function() 3 - Dim a2 = Async Function() - Return Await Task (Of Integer).FromResult(3) - End Function - Dim a3 = Async Sub() Await Task.CompletedTask - Dim a4 = Async Sub() - Await Task.CompletedTask - End Sub - End Sub - - Public Async Function AsyncFunc() As Task(Of Integer) - Return Await Task (Of Integer).FromResult(3) - End Function - Public Async Sub AsyncSub() - Await Task.CompletedTask - End Sub - End Class", @"using System.Threading.Tasks; - -internal partial class AsyncCode -{ - public void NotAsync() - { - async Task a1() => 3; - async Task a2() => await Task.FromResult(3); - async void a3() => await Task.CompletedTask; - async void a4() => await Task.CompletedTask; - } - - public async Task AsyncFunc() - { - return await Task.FromResult(3); - } - public async void AsyncSub() - { - await Task.CompletedTask; - } -} -"); - } - - [Fact] - public async Task TestAsyncMethodsWithNoReturnAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Friend Partial Module TaskExtensions - - Async Function [Then](Of T)(ByVal task As Task, ByVal f As Func(Of Task(Of T))) As Task(Of T) - Await task - Return Await f() - End Function - - - Async Function [Then](ByVal task As Task, ByVal f As Func(Of Task)) As Task - Await task - Await f() - End Function - - - Async Function [Then](Of T, U)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task(Of U))) As Task(Of U) - Return Await f(Await task) - End Function - - - Async Function [Then](Of T)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task)) As Task - Await f(Await task) - End Function - - - Async Function [ThenExit](Of T)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task)) As Task - Await f(Await task) - Exit Function - End Function -End Module", @"using System; -using System.Threading.Tasks; - -internal static partial class TaskExtensions -{ - public async static Task Then(this Task task, Func> f) - { - await task; - return await f(); - } - - public async static Task Then(this Task task, Func f) - { - await task; - await f(); - } - - public async static Task Then(this Task task, Func> f) - { - return await f(await task); - } - - public async static Task Then(this Task task, Func f) - { - await f(await task); - } - - public async static Task ThenExit(this Task task, Func f) - { - await f(await task); - return; - } -}"); - } - - [Fact] - public async Task TestExternDllImportAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Private Shared Function OpenProcess(ByVal dwDesiredAccess As AccessMask, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr -End Function", @"[DllImport(""kernel32.dll"", SetLastError = true)] -private static extern IntPtr OpenProcess(AccessMask dwDesiredAccess, bool bInheritHandle, uint dwProcessId); - -1 source compilation errors: -BC30002: Type 'AccessMask' is not defined. -1 target compilation errors: -CS0246: The type or namespace name 'AccessMask' could not be found (are you missing a using directive or an assembly reference?)"); - } - - [Fact] - public async Task Issue443_FixCaseForInterfaceMembersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Function FooDifferentCase( ByRef str2 As String) As Integer -End Interface - -Public Class Foo - Implements IFoo - Function fooDifferentCase( ByRef str2 As String) As Integer Implements IFoo.FOODIFFERENTCASE - str2 = 2.ToString() - Return 3 - End Function -End Class", @" -public partial interface IFoo -{ - int FooDifferentCase(out string str2); -} - -public partial class Foo : IFoo -{ - public int FooDifferentCase(out string str2) - { - str2 = 2.ToString(); - return 3; - } -} -"); - } - - [Fact] - public async Task Issue444_FixNameForRenamedInterfaceMembersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Function FooDifferentName(ByRef str As String, i As Integer) As Integer -End Interface - -Public Class Foo - Implements IFoo - - Function BarDifferentName(ByRef str As String, i As Integer) As Integer Implements IFoo.FooDifferentName - Return 4 - End Function -End Class", @" -public partial interface IFoo -{ - int FooDifferentName(ref string str, int i); -} - -public partial class Foo : IFoo -{ - - public int BarDifferentName(ref string str, int i) - { - return 4; - } - - int IFoo.FooDifferentName(ref string str, int i) => BarDifferentName(ref str, i); -} -"); - } - - [Fact] - public async Task IdenticalInterfaceMethodsWithRenamedInterfaceMembersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Interface IBar - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Class FooBar - Implements IFoo, IBar - - Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar - Return 4 - End Function - - Function Bar(ByRef str As String, i As Integer) As Integer Implements IBar.DoFooBar - Return 2 - End Function - -End Class", @" -public partial interface IFoo -{ - int DoFooBar(ref string str, int i); -} - -public partial interface IBar -{ - int DoFooBar(ref string str, int i); -} - -public partial class FooBar : IFoo, IBar -{ - - public int Foo(ref string str, int i) - { - return 4; - } - - int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); - - public int Bar(ref string str, int i) - { - return 2; - } - - int IBar.DoFooBar(ref string str, int i) => Bar(ref str, i); - -} -"); - } - - [Fact] - public async Task RenamedInterfaceCasingOnlyDifferenceConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Function DoFoo() As Integer - Property Prop As Integer -End Interface - -Public Class Foo - Implements IFoo - - Private Function doFoo() As Integer Implements IFoo.DoFoo - Return 4 - End Function - - Private Property prop As Integer Implements IFoo.Prop - - Private Function Consumer() As Integer - Dim foo As New Foo() - Dim interfaceInstance As IFoo = foo - Return foo.doFoo() + foo.DoFoo() + - interfaceInstance.doFoo() + interfaceInstance.DoFoo() + - foo.prop + foo.Prop + - interfaceInstance.prop + interfaceInstance.Prop - End Function - -End Class", @" -public partial interface IFoo -{ - int DoFoo(); - int Prop { get; set; } -} - -public partial class Foo : IFoo -{ - - private int doFoo() - { - return 4; - } - - int IFoo.DoFoo() => doFoo(); - - private int prop { get; set; } - int IFoo.Prop { get => prop; set => prop = value; } - - private int Consumer() - { - var foo = new Foo(); - IFoo interfaceInstance = foo; - return foo.doFoo() + foo.doFoo() + interfaceInstance.DoFoo() + interfaceInstance.DoFoo() + foo.prop + foo.prop + interfaceInstance.Prop + interfaceInstance.Prop; - } - -} -"); - } - - [Fact] - public async Task RenamedInterfaceCasingOnlyDifferenceForVirtualMemberConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Function DoFoo() As Integer - Property Prop As Integer -End Interface - -Public MustInherit Class BaseFoo - Implements IFoo - - Protected Friend Overridable Function doFoo() As Integer Implements IFoo.DoFoo - Return 4 - End Function - - Protected Friend Overridable Property prop As Integer Implements IFoo.Prop - -End Class - -Public Class Foo - Inherits BaseFoo - - Protected Friend Overrides Function DoFoo() As Integer - Return 5 - End Function - - Protected Friend Overrides Property Prop As Integer - - Private Function Consumer() As Integer - Dim foo As New Foo() - Dim interfaceInstance As IFoo = foo - Dim baseClass As BaseFoo = foo - Return foo.doFoo() + foo.DoFoo() + - interfaceInstance.doFoo() + interfaceInstance.DoFoo() + - baseClass.doFoo() + baseClass.DoFoo() + - foo.prop + foo.Prop + - interfaceInstance.prop + interfaceInstance.Prop + - baseClass.prop + baseClass.Prop - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(); - int Prop { get; set; } -} - -public abstract partial class BaseFoo : IFoo -{ - - protected internal virtual int doFoo() - { - return 4; - } - - int IFoo.DoFoo() => doFoo(); - - protected internal virtual int prop { get; set; } - int IFoo.Prop { get => prop; set => prop = value; } - -} - -public partial class Foo : BaseFoo -{ - - protected internal override int doFoo() - { - return 5; - } - - protected internal override int prop { get; set; } - - private int Consumer() - { - var foo = new Foo(); - IFoo interfaceInstance = foo; - BaseFoo baseClass = foo; - return foo.doFoo() + foo.doFoo() + interfaceInstance.DoFoo() + interfaceInstance.DoFoo() + baseClass.doFoo() + baseClass.doFoo() + foo.prop + foo.prop + interfaceInstance.Prop + interfaceInstance.Prop + baseClass.prop + baseClass.prop; - } -} -"); - } - - [Fact] - public async Task RenamedInterfaceCasingOnlyDifferenceWithOverloadedPropertyConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IUserContext - ReadOnly Property GroupID As String -End Interface - -Public Interface IFoo - ReadOnly Property ConnectedGroupId As String -End Interface - -Public MustInherit Class BaseFoo - Implements IUserContext - - Protected Friend ReadOnly Property ConnectedGroupID() As String Implements IUserContext.GroupID - -End Class - -Public Class Foo - Inherits BaseFoo - Implements IFoo - - Protected Friend Overloads ReadOnly Property ConnectedGroupID As String Implements IFoo.ConnectedGroupId ' Comment moves because this line gets split - Get - Return If("""", MyBase.ConnectedGroupID()) - End Get - End Property - - Private Function Consumer() As String - Dim foo As New Foo() - Dim ifoo As IFoo = foo - Dim baseFoo As BaseFoo = foo - Dim iUserContext As IUserContext = foo - Return foo.ConnectedGroupID & foo.ConnectedGroupId & - iFoo.ConnectedGroupID & iFoo.ConnectedGroupId & - baseFoo.ConnectedGroupID & baseFoo.ConnectedGroupId & - iUserContext.GroupId & iUserContext.GroupID - End Function - -End Class", @" -public partial interface IUserContext -{ - string GroupID { get; } -} - -public partial interface IFoo -{ - string ConnectedGroupId { get; } -} - -public abstract partial class BaseFoo : IUserContext -{ - - protected internal string ConnectedGroupID { get; private set; } - string IUserContext.GroupID { get => ConnectedGroupID; } - -} - -public partial class Foo : BaseFoo, IFoo -{ - - protected internal new string ConnectedGroupID - { - get - { - return """" ?? base.ConnectedGroupID; - } - } - - string IFoo.ConnectedGroupId { get => ConnectedGroupID; } // Comment moves because this line gets split - - private string Consumer() - { - var foo = new Foo(); - IFoo ifoo = foo; - BaseFoo baseFoo = foo; - IUserContext iUserContext = foo; - return foo.ConnectedGroupID + foo.ConnectedGroupID + ifoo.ConnectedGroupId + ifoo.ConnectedGroupId + baseFoo.ConnectedGroupID + baseFoo.ConnectedGroupID + iUserContext.GroupID + iUserContext.GroupID; - } - -} -"); - } - - [Fact] - public async Task RenamedMethodImplementsMultipleInterfacesAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Interface IBar - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Class FooBar - Implements IFoo, IBar - - Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar, IBar.DoFooBar - Return 4 - End Function - -End Class", @" -public partial interface IFoo -{ - int DoFooBar(ref string str, int i); -} - -public partial interface IBar -{ - int DoFooBar(ref string str, int i); -} - -public partial class FooBar : IFoo, IBar -{ - - public int Foo(ref string str, int i) - { - return 4; - } - - int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); - int IBar.DoFooBar(ref string str, int i) => Foo(ref str, i); - -}"); - } - - [Fact] - public async Task IdenticalInterfacePropertiesWithRenamedInterfaceMembersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property FooBarProp As Integer - End Interface - -Public Interface IBar - Property FooBarProp As Integer -End Interface - -Public Class FooBar - Implements IFoo, IBar - - Property Foo As Integer Implements IFoo.FooBarProp - - Property Bar As Integer Implements IBar.FooBarProp - -End Class", @" -public partial interface IFoo -{ - int FooBarProp { get; set; } -} - -public partial interface IBar -{ - int FooBarProp { get; set; } -} - -public partial class FooBar : IFoo, IBar -{ - - public int Foo { get; set; } - int IFoo.FooBarProp { get => Foo; set => Foo = value; } - - public int Bar { get; set; } - int IBar.FooBarProp { get => Bar; set => Bar = value; } - -}"); - } - - [Fact] - public async Task ExplicitInterfaceImplementationRequiredMethodParameters_749_Async() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Interface IBar - Function DoFooBar(ByRef str As String, i As Integer) As Integer -End Interface - -Public Class FooBar - Implements IFoo, IBar - - Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar - Return 4 - End Function - - Function Bar(ByRef str As String, i As Integer) As Integer Implements IBar.DoFooBar - Return 2 - End Function - -End Class", @" -public partial interface IFoo -{ - int DoFooBar(ref string str, int i); -} - -public partial interface IBar -{ - int DoFooBar(ref string str, int i); -} - -public partial class FooBar : IFoo, IBar -{ - - public int Foo(ref string str, int i) - { - return 4; - } - - int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); - - public int Bar(ref string str, int i) - { - return 2; - } - - int IBar.DoFooBar(ref string str, int i) => Bar(ref str, i); - -} -"); - } - - [Fact] - public async Task ExplicitInterfaceImplementationOptionalParameters_1062_Async() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface InterfaceWithOptionalParameters - Sub S(Optional i As Integer = 0) -End Interface - -Public Class ImplInterfaceWithOptionalParameters : Implements InterfaceWithOptionalParameters - Public Sub InterfaceWithOptionalParameters_S(Optional i As Integer = 0) Implements InterfaceWithOptionalParameters.S - End Sub -End Class", @" -public partial interface InterfaceWithOptionalParameters -{ - void S(int i = 0); -} - -public partial class ImplInterfaceWithOptionalParameters : InterfaceWithOptionalParameters -{ - public void InterfaceWithOptionalParameters_S(int i = 0) - { - } - - void InterfaceWithOptionalParameters.S(int i = 0) => InterfaceWithOptionalParameters_S(i); -} -"); - } - - [Fact] - public async Task OptionalParameterWithReservedName_1092_Async() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Class WithOptionalParameters - Sub S1(Optional a As Object = Nothing, Optional [default] As String = """") - End Sub - - Sub S() - S1(, ""a"") - End Sub -End Class", @" -public partial class WithOptionalParameters -{ - public void S1(object a = null, string @default = """") - { - } - - public void S() - { - S1(@default: ""a""); - } -} -"); - } - - - [Fact] - public async Task ExplicitInterfaceImplementationOptionalParametersAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property ExplicitProp(Optional str As String = """") As Integer - Function ExplicitFunc(Optional str2 As String = """", Optional i2 As Integer = 1) As Integer -End Interface - -Public Class Foo - Implements IFoo - - Private Function ExplicitFunc(Optional str As String = """", Optional i2 As Integer = 1) As Integer Implements IFoo.ExplicitFunc - Return 5 - End Function - - Private Property ExplicitProp(Optional str As String = """") As Integer Implements IFoo.ExplicitProp - Get - Return 5 - End Get - Set(value As Integer) - End Set - End Property -End Class", @" -public partial interface IFoo -{ - int get_ExplicitProp(string str = """"); - void set_ExplicitProp(string str = """", int value = default); - int ExplicitFunc(string str2 = """", int i2 = 1); -} - -public partial class Foo : IFoo -{ - - private int ExplicitFunc(string str = """", int i2 = 1) - { - return 5; - } - - int IFoo.ExplicitFunc(string str = """", int i2 = 1) => ExplicitFunc(str, i2); - - private int get_ExplicitProp(string str = """") - { - return 5; - } - private void set_ExplicitProp(string str = """", int value = default) - { - } - - int IFoo.get_ExplicitProp(string str = """") => get_ExplicitProp(str); - void IFoo.set_ExplicitProp(string str = """", int value = default) => set_ExplicitProp(str, value); -} -"); - } - - - [Fact] - public async Task ExplicitInterfaceImplementationOptionalMethodParameters_749_Async() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Function DoFooBar(ByRef str As String, Optional i As Integer = 4) As Integer -End Interface - -Public Interface IBar - Function DoFooBar(ByRef str As String, Optional i As Integer = 8) As Integer -End Interface - -Public Class FooBar - Implements IFoo, IBar - - Function Foo(ByRef str As String, Optional i As Integer = 4) As Integer Implements IFoo.DoFooBar - Return 4 - End Function - - Function Bar(ByRef str As String, Optional i As Integer = 8) As Integer Implements IBar.DoFooBar - Return 2 - End Function - -End Class", @" -public partial interface IFoo -{ - int DoFooBar(ref string str, int i = 4); -} - -public partial interface IBar -{ - int DoFooBar(ref string str, int i = 8); -} - -public partial class FooBar : IFoo, IBar -{ - - public int Foo(ref string str, int i = 4) - { - return 4; - } - - int IFoo.DoFooBar(ref string str, int i = 4) => Foo(ref str, i); - - public int Bar(ref string str, int i = 8) - { - return 2; - } - - int IBar.DoFooBar(ref string str, int i = 8) => Bar(ref str, i); - -} -"); - } - - [Fact] - public async Task RenamedInterfaceMethodFullyQualifiedAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Namespace TestNamespace - Public Interface IFoo - Function DoFoo(ByRef str As String, i As Integer) As Integer - End Interface -End Namespace - -Public Class Foo - Implements TestNamespace.IFoo - - Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements TestNamespace.IFoo.DoFoo - Return 4 - End Function -End Class", @" -namespace TestNamespace -{ - public partial interface IFoo - { - int DoFoo(ref string str, int i); - } -} - -public partial class Foo : TestNamespace.IFoo -{ - - public int DoFooRenamed(ref string str, int i) - { - return 4; - } - - int TestNamespace.IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); -}"); - } - - [Fact] - public async Task RenamedInterfacePropertyFullyQualifiedAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Namespace TestNamespace - Public Interface IFoo - Property FooProp As Integer - End Interface -End Namespace - -Public Class Foo - Implements TestNamespace.IFoo - - Property FooPropRenamed As Integer Implements TestNamespace.IFoo.FooProp - -End Class", @" -namespace TestNamespace -{ - public partial interface IFoo - { - int FooProp { get; set; } - } -} - -public partial class Foo : TestNamespace.IFoo -{ - - public int FooPropRenamed { get; set; } - int TestNamespace.IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } - -}"); - } - - [Fact] - public async Task RenamedInterfaceMethodConsumerCasingRenamedAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Function DoFoo(ByRef str As String, i As Integer) As Integer - End Interface - -Public Class Foo - Implements IFoo - - Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo - Return 4 - End Function -End Class - -Public Class FooConsumer - Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.DOFOORENAMED(str, i) + bar.DoFoo(str, i) - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(ref string str, int i); -} - -public partial class Foo : IFoo -{ - - public int DoFooRenamed(ref string str, int i) - { - return 4; - } - - int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); -} - -public partial class FooConsumer -{ - public int DoFooRenamedConsumer(ref string str, int i) - { - var foo = new Foo(); - IFoo bar = foo; - return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); - } -}"); - } - - [Fact] - public async Task RenamedInterfacePropertyConsumerCasingRenamedAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Property FooProp As Integer - End Interface - -Public Class Foo - Implements IFoo - - Property FooPropRenamed As Integer Implements IFoo.FooProp - -End Class - -Public Class FooConsumer - Function GetFooRenamed() As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.FOOPROPRENAMED + bar.FooProp - End Function -End Class", @" -public partial interface IFoo -{ - int FooProp { get; set; } -} - -public partial class Foo : IFoo -{ - - public int FooPropRenamed { get; set; } - int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } - -} - -public partial class FooConsumer -{ - public int GetFooRenamed() - { - var foo = new Foo(); - IFoo bar = foo; - return foo.FooPropRenamed + bar.FooProp; - } -}"); - } - - [Fact] - public async Task InterfaceMethodCasingRenamedConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Function DoFoo(str As String, i As Integer) As Integer - End Interface - -Public Class Foo - Implements IFoo - - Function dofoo(str As String, i As Integer) As Integer Implements IFoo.DoFoo - Return 4 - End Function -End Class - -Public Class FooConsumer - Function DoFooRenamedConsumer(str As String, i As Integer) As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.dofoo(str, i) + bar.DoFoo(str, i) - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(string str, int i); -} - -public partial class Foo : IFoo -{ - - public int DoFoo(string str, int i) - { - return 4; - } -} - -public partial class FooConsumer -{ - public int DoFooRenamedConsumer(string str, int i) - { - var foo = new Foo(); - IFoo bar = foo; - return foo.DoFoo(str, i) + bar.DoFoo(str, i); - } -}"); - } - - [Fact] - public async Task InterfacePropertyCasingRenamedConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Property FooProp As Integer - End Interface - -Public Class Foo - Implements IFoo - - Property fooprop As Integer Implements IFoo.FooProp - -End Class - -Public Class FooConsumer - Function GetFooRenamed() As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.fooprop + bar.FooProp - End Function -End Class", @" -public partial interface IFoo -{ - int FooProp { get; set; } -} - -public partial class Foo : IFoo -{ - - public int FooProp { get; set; } - -} - -public partial class FooConsumer -{ - public int GetFooRenamed() - { - var foo = new Foo(); - IFoo bar = foo; - return foo.FooProp + bar.FooProp; - } -}"); - } - - [Fact] - public async Task InterfaceRenamedMethodConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Function DoFoo(ByRef str As String, i As Integer) As Integer - End Interface - -Public Class Foo - Implements IFoo - - Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo - Return 4 - End Function -End Class - -Public Class FooConsumer - Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.DoFooRenamed(str, i) + bar.DoFoo(str, i) - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(ref string str, int i); -} - -public partial class Foo : IFoo -{ - - public int DoFooRenamed(ref string str, int i) - { - return 4; - } - - int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); -} - -public partial class FooConsumer -{ - public int DoFooRenamedConsumer(ref string str, int i) - { - var foo = new Foo(); - IFoo bar = foo; - return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); - } -}"); - } - - [Fact] - public async Task InterfaceRenamedPropertyConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Property FooProp As Integer - End Interface - -Public Class Foo - Implements IFoo - - Property FooPropRenamed As Integer Implements IFoo.FooProp - -End Class - -Public Class FooConsumer - Function GetFooRenamed() As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.FooPropRenamed + bar.FooProp - End Function -End Class", @" -public partial interface IFoo -{ - int FooProp { get; set; } -} - -public partial class Foo : IFoo -{ - - public int FooPropRenamed { get; set; } - int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } - -} - -public partial class FooConsumer -{ - public int GetFooRenamed() - { - var foo = new Foo(); - IFoo bar = foo; - return foo.FooPropRenamed + bar.FooProp; - } -}"); - } - - [Fact] - public async Task PartialInterfaceRenamedMethodConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Partial Interface IFoo - Function DoFoo(ByRef str As String, i As Integer) As Integer - End Interface - -Public Class Foo - Implements IFoo - - Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo - Return 4 - End Function -End Class - -Public Class FooConsumer - Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.DoFooRenamed(str, i) + bar.DoFoo(str, i) - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(ref string str, int i); -} - -public partial class Foo : IFoo -{ - - public int DoFooRenamed(ref string str, int i) - { - return 4; - } - - int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); -} - -public partial class FooConsumer -{ - public int DoFooRenamedConsumer(ref string str, int i) - { - var foo = new Foo(); - IFoo bar = foo; - return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); - } -}"); - } - - [Fact] - public async Task PartialInterfaceRenamedPropertyConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Partial Interface IFoo - Property FooProp As Integer - End Interface - -Public Class Foo - Implements IFoo - - Property FooPropRenamed As Integer Implements IFoo.FooProp - -End Class - -Public Class FooConsumer - Function GetFooRenamed() As Integer - Dim foo As New Foo - Dim bar As IFoo = foo - Return foo.FooPropRenamed + bar.FooProp - End Function -End Class", @" -public partial interface IFoo -{ - int FooProp { get; set; } -} - -public partial class Foo : IFoo -{ - - public int FooPropRenamed { get; set; } - int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } - -} - -public partial class FooConsumer -{ - public int GetFooRenamed() - { - var foo = new Foo(); - IFoo bar = foo; - return foo.FooPropRenamed + bar.FooProp; - } -}"); - } - - [Fact] - public async Task RenamedInterfaceMethodMyClassConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - Function DoFoo(ByRef str As String, i As Integer) As Integer - End Interface - -Public Class Foo - Implements IFoo - - Overridable Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo ' Comment ends up out of order, but attached to correct method - Return 4 - End Function - - Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer - Return MyClass.DoFooRenamed(str, i) - End Function -End Class", @" -public partial interface IFoo -{ - int DoFoo(ref string str, int i); -} - -public partial class Foo : IFoo -{ - - public int MyClassDoFooRenamed(ref string str, int i) - { - return 4; - } - - int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); - public virtual int DoFooRenamed(ref string str, int i) => MyClassDoFooRenamed(ref str, i); // Comment ends up out of order, but attached to correct method - - public int DoFooRenamedConsumer(ref string str, int i) - { - return MyClassDoFooRenamed(ref str, i); - } -}"); - } - - [Fact] - public async Task RenamedInterfacePropertyMyClassConsumerAsync() - { - await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo - ReadOnly Property DoFoo As Integer - WriteOnly Property DoBar As Integer - End Interface - -Public Class Foo - Implements IFoo - - Overridable ReadOnly Property DoFooRenamed As Integer Implements IFoo.DoFoo ' Comment ends up out of order, but attached to correct method - Get - Return 4 - End Get - End Property - - Overridable WriteOnly Property DoBarRenamed As Integer Implements IFoo.DoBar ' Comment ends up out of order, but attached to correct method - Set - Throw New Exception() - End Set - End Property - - Sub DoFooRenamedConsumer() - MyClass.DoBarRenamed = MyClass.DoFooRenamed - End Sub -End Class", @"using System; - -public partial interface IFoo -{ - int DoFoo { get; } - int DoBar { set; } -} - -public partial class Foo : IFoo -{ - - public int MyClassDoFooRenamed - { - get - { - return 4; - } - } - - int IFoo.DoFoo { get => DoFooRenamed; } - - public virtual int DoFooRenamed // Comment ends up out of order, but attached to correct method - { - get - { - return MyClassDoFooRenamed; - } - } - - public int MyClassDoBarRenamed - { - set - { - throw new Exception(); - } - } - - int IFoo.DoBar { set => DoBarRenamed = value; } - - public virtual int DoBarRenamed // Comment ends up out of order, but attached to correct method - { - set - { - MyClassDoBarRenamed = value; - } - } - - public void DoFooRenamedConsumer() - { - MyClassDoBarRenamed = MyClassDoFooRenamed; - } -}"); - } - - [Fact] - public async Task ExplicitInterfaceImplementationAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property ExplicitProp(str As String) As Integer - Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer -End Interface - -Public Class Foo - Implements IFoo - - Private Function ExplicitFunc(ByRef str As String, i As Integer) As Integer Implements IFoo.ExplicitFunc - Return 5 - End Function - - Private Property ExplicitProp(str As String) As Integer Implements IFoo.ExplicitProp - Get - Return 5 - End Get - Set(value As Integer) - End Set - End Property -End Class", @" -public partial interface IFoo -{ - int get_ExplicitProp(string str); - void set_ExplicitProp(string str, int value); - int ExplicitFunc(ref string str2, int i2); -} - -public partial class Foo : IFoo -{ - - private int ExplicitFunc(ref string str, int i) - { - return 5; - } - - int IFoo.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); - - private int get_ExplicitProp(string str) - { - return 5; - } - private void set_ExplicitProp(string str, int value) - { - } - - int IFoo.get_ExplicitProp(string str) => get_ExplicitProp(str); - void IFoo.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); -} -"); - } - - [Fact] - public async Task PropertyInterfaceImplementationKeepsVirtualModifierAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property PropParams(str As String) As Integer - Property Prop() As Integer -End Interface - -Public Class Foo - Implements IFoo - - Public Overridable Property PropParams(str As String) As Integer Implements IFoo.PropParams - Get - Return 5 - End Get - Set(value As Integer) - End Set - End Property - - Public Overridable Property Prop As Integer Implements IFoo.Prop - Get - Return 5 - End Get - Set(value As Integer) - End Set - End Property -End Class", @" -public partial interface IFoo -{ - int get_PropParams(string str); - void set_PropParams(string str, int value); - int Prop { get; set; } -} - -public partial class Foo : IFoo -{ - - public virtual int get_PropParams(string str) - { - return 5; - } - public virtual void set_PropParams(string str, int value) - { - } - - public virtual int Prop - { - get - { - return 5; - } - set - { - } - } -} -"); - } - - [Fact] - public async Task PrivateAutoPropertyImplementsMultipleInterfacesAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property ExplicitProp As Integer -End Interface - -Public Interface IBar - Property ExplicitProp As Integer -End Interface - -Public Class Foo - Implements IFoo, IBar - - Private Property ExplicitProp As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp -End Class", @" -public partial interface IFoo -{ - int ExplicitProp { get; set; } -} - -public partial interface IBar -{ - int ExplicitProp { get; set; } -} - -public partial class Foo : IFoo, IBar -{ - - private int ExplicitProp { get; set; } - int IFoo.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } - int IBar.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } -}"); - } - - - [Fact] - public async Task ImplementMultipleRenamedPropertiesFromInterfaceAsAbstractAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Property ExplicitProp As Integer -End Interface -Public Interface IBar - Property ExplicitProp As Integer -End Interface -Public MustInherit Class Foo - Implements IFoo, IBar - - Protected MustOverride Property ExplicitPropRenamed1 As Integer Implements IFoo.ExplicitProp - Protected MustOverride Property ExplicitPropRenamed2 As Integer Implements IBar.ExplicitProp -End Class", @" -public partial interface IFoo -{ - int ExplicitProp { get; set; } -} + await TestConversionVisualBasicToCSharpAsync(@" +Class TestClass +End Class", @"using System.Diagnostics; -public partial interface IBar -{ - int ExplicitProp { get; set; } -} -public abstract partial class Foo : IFoo, IBar +[DebuggerDisplay(""Hello World"")] +internal partial class TestClass { - - protected abstract int ExplicitPropRenamed1 { get; set; } - int IFoo.ExplicitProp { get => ExplicitPropRenamed1; set => ExplicitPropRenamed1 = value; } - protected abstract int ExplicitPropRenamed2 { get; set; } - int IBar.ExplicitProp { get => ExplicitPropRenamed2; set => ExplicitPropRenamed2 = value; } }"); } [Fact] - public async Task ExplicitInterfaceImplementationForVirtualMemberFromAnotherClassAsync() + public async Task ParamArrayAsync() { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Sub Save() - Property Prop As Integer -End Interface - -Public MustInherit Class BaseFoo - Protected Overridable Sub OnSave() - End Sub - - Protected Overridable Property MyProp As Integer = 5 -End Class - -Public Class Foo - Inherits BaseFoo - Implements IFoo - - Protected Overrides Sub OnSave() Implements IFoo.Save + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Sub SomeBools(ParamArray anyName As Boolean()) End Sub - - Protected Overrides Property MyProp As Integer = 6 Implements IFoo.Prop - End Class", @" -public partial interface IFoo -{ - void Save(); - int Prop { get; set; } -} - -public abstract partial class BaseFoo -{ - protected virtual void OnSave() - { - } - - protected virtual int MyProp { get; set; } = 5; -} - -public partial class Foo : BaseFoo, IFoo +internal partial class TestClass { - - protected override void OnSave() + private void SomeBools(params bool[] anyName) { } - - void IFoo.Save() => OnSave(); - - protected override int MyProp { get; set; } = 6; - int IFoo.Prop { get => MyProp; set => MyProp = value; } - }"); } [Fact] - public async Task ExplicitInterfaceImplementationWhereOnlyOneInterfaceMemberIsRenamedAsync() + public async Task ParamNamedBoolAsync() { - await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Sub Save() - Property A As Integer -End Interface - -Public Interface IBar - Sub OnSave() - Property B As Integer -End Interface - -Public Class Foo - Implements IFoo, IBar - - Public Overridable Sub Save() Implements IFoo.Save, IBar.OnSave + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + Private Sub SomeBools(ParamArray bool As Boolean()) End Sub - - Public Overridable Property A As Integer Implements IFoo.A, IBar.B - End Class", @" -public partial interface IFoo -{ - void Save(); - int A { get; set; } -} - -public partial interface IBar -{ - void OnSave(); - int B { get; set; } -} - -public partial class Foo : IFoo, IBar +internal partial class TestClass { - - public virtual void Save() + private void SomeBools(params bool[] @bool) { } - - void IFoo.Save() => Save(); - void IBar.OnSave() => Save(); - - public virtual int A { get; set; } - int IFoo.A { get => A; set => A = value; } - int IBar.B { get => A; set => A = value; } - }"); } [Fact] - public async Task ExplicitInterfaceImplementationWhereMemberShadowsBaseAsync() + public async Task MethodWithNameArrayParameterAsync() { await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Sub Save() - Property Prop As Integer -End Interface - -Public MustInherit Class BaseFoo - Public Overridable Sub OnSave() - End Sub - - Public Overridable Property MyProp As Integer = 5 -End Class - -Public Class Foo - Inherits BaseFoo - Implements IFoo - - Public Shadows Sub OnSave() Implements IFoo.Save + @"Class TestClass + Public Sub DoNothing(ByVal strs() As String) + Dim moreStrs() As String End Sub - - Public Shadows Property MyProp As Integer = 6 Implements IFoo.Prop - -End Class", @" -public partial interface IFoo -{ - void Save(); - int Prop { get; set; } -} - -public abstract partial class BaseFoo -{ - public virtual void OnSave() - { - } - - public virtual int MyProp { get; set; } = 5; -} - -public partial class Foo : BaseFoo, IFoo -{ - - public new void OnSave() - { - } - - void IFoo.Save() => OnSave(); - - public new int MyProp { get; set; } = 6; - int IFoo.Prop { get => MyProp; set => MyProp = value; } - -}"); - } - - [Fact] - public async Task PrivatePropertyAccessorBlocksImplementsMultipleInterfacesAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property ExplicitProp As Integer -End Interface - -Public Interface IBar - Property ExplicitProp As Integer -End Interface - -Public Class Foo - Implements IFoo, IBar - - Private Property ExplicitProp As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp ' Comment moves because this line gets split - Get - Return 5 - End Get - Set - End Set - End Property -End Class", @" -public partial interface IFoo -{ - int ExplicitProp { get; set; } -} - -public partial interface IBar -{ - int ExplicitProp { get; set; } -} - -public partial class Foo : IFoo, IBar +End Class", + @" +internal partial class TestClass { - - private int ExplicitProp + public void DoNothing(string[] strs) { - get - { - return 5; - } - set - { - } + string[] moreStrs; } - - int IFoo.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } - int IBar.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } // Comment moves because this line gets split }"); } [Fact] - public async Task NonPublicImplementsInterfacesAsync() + public async Task UntypedParametersAsync() { await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property FriendProp As Integer - Sub ProtectedSub() - Function PrivateFunc() As Integer - Sub ProtectedInternalSub() - Sub AbstractSub() -End Interface - -Public Interface IBar - Property FriendProp As Integer - Sub ProtectedSub() - Function PrivateFunc() As Integer - Sub ProtectedInternalSub() - Sub AbstractSub() -End Interface - -Public MustInherit Class BaseFoo - Implements IFoo, IBar - - Friend Overridable Property FriendProp As Integer Implements IFoo.FriendProp, IBar.FriendProp ' Comment moves because this line gets split - Get - Return 5 - End Get - Set - End Set - End Property - - Protected Sub ProtectedSub() Implements IFoo.ProtectedSub, IBar.ProtectedSub - End Sub - - Private Function PrivateFunc() As Integer Implements IFoo.PrivateFunc, IBar.PrivateFunc - End Function - - Protected Friend Overridable Sub ProtectedInternalSub() Implements IFoo.ProtectedInternalSub, IBar.ProtectedInternalSub - End Sub - - Protected MustOverride Sub AbstractSubRenamed() Implements IFoo.AbstractSub, IBar.AbstractSub -End Class - -Public Class Foo - Inherits BaseFoo - - Protected Friend Overrides Sub ProtectedInternalSub() - End Sub - - Protected Overrides Sub AbstractSubRenamed() + @"Class TestClass + Public Sub DoNothing(obj, objs()) End Sub -End Class -", @" -public partial interface IFoo -{ - int FriendProp { get; set; } - void ProtectedSub(); - int PrivateFunc(); - void ProtectedInternalSub(); - void AbstractSub(); -} - -public partial interface IBar -{ - int FriendProp { get; set; } - void ProtectedSub(); - int PrivateFunc(); - void ProtectedInternalSub(); - void AbstractSub(); -} - -public abstract partial class BaseFoo : IFoo, IBar -{ - - internal virtual int FriendProp - { - get - { - return 5; - } - set - { - } - } - - int IFoo.FriendProp { get => FriendProp; set => FriendProp = value; } - int IBar.FriendProp { get => FriendProp; set => FriendProp = value; } // Comment moves because this line gets split - - protected void ProtectedSub() - { - } - - void IFoo.ProtectedSub() => ProtectedSub(); - void IBar.ProtectedSub() => ProtectedSub(); - - private int PrivateFunc() - { - return default; - } - - int IFoo.PrivateFunc() => PrivateFunc(); - int IBar.PrivateFunc() => PrivateFunc(); - - protected internal virtual void ProtectedInternalSub() - { - } - - void IFoo.ProtectedInternalSub() => ProtectedInternalSub(); - void IBar.ProtectedInternalSub() => ProtectedInternalSub(); - - protected abstract void AbstractSubRenamed(); - void IFoo.AbstractSub() => AbstractSubRenamed(); - void IBar.AbstractSub() => AbstractSubRenamed(); -} - -public partial class Foo : BaseFoo +End Class", + @" +internal partial class TestClass { - - protected internal override void ProtectedInternalSub() - { - } - - protected override void AbstractSubRenamed() + public void DoNothing(object obj, object[] objs) { } }"); } [Fact] - public async Task ExplicitPropertyImplementationWithDirectAccessAsync() + public async Task PartialClassAsync() { await TestConversionVisualBasicToCSharpAsync( - @" -Public Interface IFoo - Property ExplicitProp As Integer - ReadOnly Property ExplicitReadOnlyProp As Integer -End Interface - -Public Class Foo - Implements IFoo - - Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp - ReadOnly Property ExplicitRenamedReadOnlyProp As Integer Implements IFoo.ExplicitReadOnlyProp - - Private Sub Consumer() - _ExplicitPropRenamed = 5 - _ExplicitRenamedReadOnlyProp = 10 + @"Public Partial Class TestClass + Private Sub DoNothing() + Console.WriteLine(""Hello"") End Sub +End Class -End Class", @" -public partial interface IFoo -{ - int ExplicitProp { get; set; } - int ExplicitReadOnlyProp { get; } -} +Class TestClass ' VB doesn't require partial here (when just a single class omits it) + Partial Private Sub DoNothing() + End Sub +End Class", + @"using System; -public partial class Foo : IFoo +public partial class TestClass { - - public int ExplicitPropRenamed { get; set; } - int IFoo.ExplicitProp { get => ExplicitPropRenamed; set => ExplicitPropRenamed = value; } - public int ExplicitRenamedReadOnlyProp { get; private set; } - int IFoo.ExplicitReadOnlyProp { get => ExplicitRenamedReadOnlyProp; } - - private void Consumer() + partial void DoNothing() { - ExplicitPropRenamed = 5; - ExplicitRenamedReadOnlyProp = 10; + Console.WriteLine(""Hello""); } +} +public partial class TestClass // VB doesn't require partial here (when just a single class omits it) +{ + partial void DoNothing(); }"); } - + [Fact] - public async Task ReadonlyRenamedPropertyImplementsMultipleInterfacesAsync() + public async Task NestedClassAsync() { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - ReadOnly Property ExplicitProp As Integer -End Interface + await TestConversionVisualBasicToCSharpAsync(@"Class ClA + Public Shared Sub MA() + ClA.ClassB.MB() + MyClassC.MC() + End Sub -Public Interface IBar - ReadOnly Property ExplicitProp As Integer -End Interface + Public Class ClassB + Public Shared Function MB() as ClassB + ClA.MA() + MyClassC.MC() + Return ClA.ClassB.MB() + End Function + End Class +End Class -Public Class Foo - Implements IFoo, IBar - - ReadOnly Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp +Class MyClassC + Public Shared Sub MC() + ClA.MA() + ClA.ClassB.MB() + End Sub End Class", @" -public partial interface IFoo +internal partial class ClA { - int ExplicitProp { get; } -} + public static void MA() + { + ClassB.MB(); + MyClassC.MC(); + } -public partial interface IBar -{ - int ExplicitProp { get; } + public partial class ClassB + { + public static ClassB MB() + { + MA(); + MyClassC.MC(); + return MB(); + } + } } -public partial class Foo : IFoo, IBar +internal partial class MyClassC { - - public int ExplicitPropRenamed { get; private set; } - int IFoo.ExplicitProp { get => ExplicitPropRenamed; } - int IBar.ExplicitProp { get => ExplicitPropRenamed; } + public static void MC() + { + ClA.MA(); + ClA.ClassB.MB(); + } }"); } [Fact] - public async Task WriteonlyPropertyImplementsMultipleInterfacesAsync() + public async Task LessQualifiedNestedClassAsync() { - await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - WriteOnly Property ExplicitProp As Integer -End Interface + await TestConversionVisualBasicToCSharpAsync(@"Class ClA + Public Shared Sub MA() + ClassB.MB() + MyClassC.MC() + End Sub -Public Interface IBar - WriteOnly Property ExplicitProp As Integer -End Interface + Public Class ClassB + Public Shared Function MB() as ClassB + MA() + MyClassC.MC() + Return MB() + End Function + End Class +End Class -Public Class Foo - Implements IFoo, IBar - - WriteOnly Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp ' Comment moves because this line gets split - Set - End Set - End Property +Class MyClassC + Public Shared Sub MC() + ClA.MA() + ClA.ClassB.MB() + End Sub End Class", @" -public partial interface IFoo -{ - int ExplicitProp { set; } -} - -public partial interface IBar -{ - int ExplicitProp { set; } -} - -public partial class Foo : IFoo, IBar +internal partial class ClA { + public static void MA() + { + ClassB.MB(); + MyClassC.MC(); + } - public int ExplicitPropRenamed + public partial class ClassB { - set + public static ClassB MB() { + MA(); + MyClassC.MC(); + return MB(); } } +} - int IFoo.ExplicitProp { set => ExplicitPropRenamed = value; } - int IBar.ExplicitProp { set => ExplicitPropRenamed = value; } // Comment moves because this line gets split +internal partial class MyClassC +{ + public static void MC() + { + ClA.MA(); + ClA.ClassB.MB(); + } }"); } [Fact] - public async Task PrivateMethodAndParameterizedPropertyImplementsMultipleInterfacesAsync() + public async Task TestAsyncMethodsAsync() { await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Property ExplicitProp(str As String) As Integer - Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer -End Interface - -Public Interface IBar - Property ExplicitProp(str As String) As Integer - Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer -End Interface - -Public Class Foo - Implements IFoo, IBar - - Private Function ExplicitFunc(ByRef str As String, i As Integer) As Integer Implements IFoo.ExplicitFunc, IBar.ExplicitFunc - Return 5 - End Function - - Private Property ExplicitProp(str As String) As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp - Get - Return 5 - End Get - Set(value As Integer) - End Set - End Property -End Class", @" -public partial interface IFoo -{ - int get_ExplicitProp(string str); - void set_ExplicitProp(string str, int value); - int ExplicitFunc(ref string str2, int i2); -} + @" Class AsyncCode + Public Sub NotAsync() + Dim a1 = Async Function() 3 + Dim a2 = Async Function() + Return Await Task (Of Integer).FromResult(3) + End Function + Dim a3 = Async Sub() Await Task.CompletedTask + Dim a4 = Async Sub() + Await Task.CompletedTask + End Sub + End Sub -public partial interface IBar -{ - int get_ExplicitProp(string str); - void set_ExplicitProp(string str, int value); - int ExplicitFunc(ref string str2, int i2); -} + Public Async Function AsyncFunc() As Task(Of Integer) + Return Await Task (Of Integer).FromResult(3) + End Function + Public Async Sub AsyncSub() + Await Task.CompletedTask + End Sub + End Class", @"using System.Threading.Tasks; -public partial class Foo : IFoo, IBar +internal partial class AsyncCode { - - private int ExplicitFunc(ref string str, int i) + public void NotAsync() { - return 5; + async Task a1() => 3; + async Task a2() => await Task.FromResult(3); + async void a3() => await Task.CompletedTask; + async void a4() => await Task.CompletedTask; } - int IFoo.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); - int IBar.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); - - private int get_ExplicitProp(string str) + public async Task AsyncFunc() { - return 5; + return await Task.FromResult(3); } - private void set_ExplicitProp(string str, int value) + public async void AsyncSub() { + await Task.CompletedTask; } - - int IFoo.get_ExplicitProp(string str) => get_ExplicitProp(str); - int IBar.get_ExplicitProp(string str) => get_ExplicitProp(str); - void IFoo.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); - void IBar.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); -}"); +} +"); } - /// - /// - /// - /// [Fact] - public async Task Issue444_InternalMemberDelegatingMethodAsync() + public async Task TestExternDllImportAsync() { await TestConversionVisualBasicToCSharpAsync( - @"Public Interface IFoo - Function FooDifferentName(ByRef str As String, i As Integer) As Integer -End Interface + @" +Private Shared Function OpenProcess(ByVal dwDesiredAccess As AccessMask, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr +End Function", @"[DllImport(""kernel32.dll"", SetLastError = true)] +private static extern IntPtr OpenProcess(AccessMask dwDesiredAccess, bool bInheritHandle, uint dwProcessId); -Friend Class Foo - Implements IFoo +1 source compilation errors: +BC30002: Type 'AccessMask' is not defined. +1 target compilation errors: +CS0246: The type or namespace name 'AccessMask' could not be found (are you missing a using directive or an assembly reference?)"); + } - Function BarDifferentName(ByRef str As String, i As Integer) As Integer Implements IFoo.FooDifferentName - Return 4 - End Function -End Class", @" -public partial interface IFoo -{ - int FooDifferentName(ref string str, int i); -} + [Fact] + public async Task OptionalParameterWithReservedName_1092_Async() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Class WithOptionalParameters + Sub S1(Optional a As Object = Nothing, Optional [default] As String = """") + End Sub -internal partial class Foo : IFoo + Sub S() + S1(, ""a"") + End Sub +End Class", @" +public partial class WithOptionalParameters { - - public int BarDifferentName(ref string str, int i) + public void S1(object a = null, string @default = """") { - return 4; } - int IFoo.FooDifferentName(ref string str, int i) => BarDifferentName(ref str, i); + public void S() + { + S1(@default: ""a""); + } } "); } @@ -3922,79 +954,6 @@ public enum MyEnum "); } - [Fact] - public async Task TestConstructorStaticLocalConvertedToFieldAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class StaticLocalConvertedToField - Sub New(x As Boolean) - Static sPrevPosition As Integer = 7 ' Comment moves with declaration - Console.WriteLine(sPrevPosition) - End Sub - Sub New(x As Integer) - Static sPrevPosition As Integer - Console.WriteLine(sPrevPosition) - End Sub -End Class", @"using System; - -internal partial class StaticLocalConvertedToField -{ - private int _sPrevPosition = 7; // Comment moves with declaration - public StaticLocalConvertedToField(bool x) - { - Console.WriteLine(_sPrevPosition); - } - - private int _sPrevPosition1 = default; - public StaticLocalConvertedToField(int x) - { - Console.WriteLine(_sPrevPosition1); - } -}"); - } - - [Fact] - public async Task TestPropertyStaticLocalConvertedToFieldAsync() - { - await TestConversionVisualBasicToCSharpAsync( - @"Class StaticLocalConvertedToField - Readonly Property OtherName() As Integer - Get - Static sPrevPosition As Integer = 3 ' Comment moves with declaration - Console.WriteLine(sPrevPosition) - Return sPrevPosition - End Get - End Property - Readonly Property OtherName(x As Integer) as Integer - Get - Static sPrevPosition As Integer - sPrevPosition += 1 - Return sPrevPosition - End Get - End Property -End Class", @"using System; - -internal partial class StaticLocalConvertedToField -{ - private int _OtherName_sPrevPosition = 3; // Comment moves with declaration - public int OtherName - { - get - { - Console.WriteLine(_OtherName_sPrevPosition); - return _OtherName_sPrevPosition; - } - } - - private int _OtherName_sPrevPosition1 = default; - public int get_OtherName(int x) - { - _OtherName_sPrevPosition1 += 1; - return _OtherName_sPrevPosition1; - } -}"); - } - [Fact] public async Task TestStaticLocalConvertedToFieldAsync() { @@ -4274,4 +1233,4 @@ private void OptionalByRefWithDefault([Optional][DefaultParameterValue(""a"")] r CS7036: There is no argument given that corresponds to the required formal parameter 'str1' of 'MissingByRefArgumentWithNoExplicitDefaultValue.ByRefNoDefault(ref string)' "); } -} \ No newline at end of file +} diff --git a/Tests/CSharp/MemberTests/MemberTests/ConstructorTests.cs b/Tests/CSharp/MemberTests/MemberTests/ConstructorTests.cs new file mode 100644 index 00000000..30238684 --- /dev/null +++ b/Tests/CSharp/MemberTests/MemberTests/ConstructorTests.cs @@ -0,0 +1,112 @@ +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; + +namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests.MemberTests; + +public class ConstructorTests : ConverterTestBase +{ + [Fact] + public async Task TestConstructorVisibilityAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class Class1 + Sub New(x As Boolean) + End Sub +End Class", @" +internal partial class Class1 +{ + public Class1(bool x) + { + } +}"); + } + + [Fact] + public async Task TestModuleConstructorAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module Module1 + Sub New() + Dim someValue As Integer = 0 + End Sub +End Module", @" +internal static partial class Module1 +{ + static Module1() + { + int someValue = 0; + } +}"); + } + + [Fact] + public async Task TestConstructorAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass(Of T As {Class, New}, T2 As Structure, T3) + Public Sub New( ByRef argument As T, ByRef argument2 As T2, ByVal argument3 As T3) + End Sub +End Class", @" +internal partial class TestClass + where T : class, new() + where T2 : struct +{ + public TestClass(out T argument, ref T2 argument2, T3 argument3) + { + } +} +1 target compilation errors: +CS0177: The out parameter 'argument' must be assigned to before control leaves the current method"); + } + + [Fact] + public async Task TestConstructorWithImplicitPublicAccessibilityAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Sub New() +End Sub", @"public SurroundingClass() +{ +}"); + } + + [Fact] + public async Task TestStaticConstructorAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Shared Sub New() +End Sub", @"static SurroundingClass() +{ +}"); + } + + [Fact] + public async Task TestConstructorStaticLocalConvertedToFieldAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class StaticLocalConvertedToField + Sub New(x As Boolean) + Static sPrevPosition As Integer = 7 ' Comment moves with declaration + Console.WriteLine(sPrevPosition) + End Sub + Sub New(x As Integer) + Static sPrevPosition As Integer + Console.WriteLine(sPrevPosition) + End Sub +End Class", @"using System; + +internal partial class StaticLocalConvertedToField +{ + private int _sPrevPosition = 7; // Comment moves with declaration + public StaticLocalConvertedToField(bool x) + { + Console.WriteLine(_sPrevPosition); + } + + private int _sPrevPosition1 = default; + public StaticLocalConvertedToField(int x) + { + Console.WriteLine(_sPrevPosition1); + } +}"); + } +} diff --git a/Tests/CSharp/MemberTests/MemberTests/ExtensionMethodTests.cs b/Tests/CSharp/MemberTests/MemberTests/ExtensionMethodTests.cs new file mode 100644 index 00000000..7d1289a8 --- /dev/null +++ b/Tests/CSharp/MemberTests/MemberTests/ExtensionMethodTests.cs @@ -0,0 +1,304 @@ +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; +using System; +using System.Runtime.CompilerServices; + +namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests.MemberTests // This namespace is as per prompt +{ + public class ExtensionMethodTests : ConverterTestBase + { + [Fact] + public async Task TestExtensionMethodAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module TestClass + + Sub TestMethod(ByVal str As String) + End Sub + + + Sub TestMethod2Parameters(ByVal str As String, other As String) + End Sub +End Module", @" +internal static partial class TestClass +{ + public static void TestMethod(this string str) + { + } + + public static void TestMethod2Parameters(this string str, string other) + { + } +}"); + } + + [Fact] + public async Task TestExtensionMethodWithExistingImportAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System.Runtime.CompilerServices ' Removed by simplifier + +Module TestClass + + Sub TestMethod(ByVal str As String) + End Sub +End Module", @" +internal static partial class TestClass +{ + public static void TestMethod(this string str) + { + } +}"); + } + + [Fact] + public async Task TestRefExtensionMethodAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System +Imports System.Runtime.CompilerServices ' Removed since the extension attribute is removed + +Public Module MyExtensions + + Public Sub Add(Of T)(ByRef arr As T(), item As T) + Array.Resize(arr, arr.Length + 1) + arr(arr.Length - 1) = item + End Sub +End Module + +Public Module UsagePoint + Public Sub Main() + Dim arr = New Integer() {1, 2, 3} + arr.Add(4) + System.Console.WriteLine(arr(3)) + End Sub +End Module", @"using System; + +public static partial class MyExtensions +{ + public static void Add(ref T[] arr, T item) + { + Array.Resize(ref arr, arr.Length + 1); + arr[arr.Length - 1] = item; + } +} + +public static partial class UsagePoint +{ + public static void Main() + { + int[] arr = new int[] { 1, 2, 3 }; + MyExtensions.Add(ref arr, 4); + Console.WriteLine(arr[3]); + } +}"); + } + + [Fact] + public async Task TestExtensionWithinExtendedTypeAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module Extensions + + Sub TestExtension(extendedClass As ExtendedClass) + End Sub +End Module + +Class ExtendedClass + Sub TestExtensionConsumer() + TestExtension() + End Sub +End Class", @" +internal static partial class Extensions +{ + public static void TestExtension(this ExtendedClass extendedClass) + { + } +} + +internal partial class ExtendedClass +{ + public void TestExtensionConsumer() + { + this.TestExtension(); + } +}"); + } + + [Fact] + public async Task TestExtensionWithinTypeDerivedFromExtendedTypeAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module Extensions + + Sub TestExtension(extendedClass As ExtendedClass) + End Sub +End Module + +Class ExtendedClass +End Class + +Class DerivedClass + Inherits ExtendedClass + + Sub TestExtensionConsumer() + TestExtension() + End Sub +End Class", @" +internal static partial class Extensions +{ + public static void TestExtension(this ExtendedClass extendedClass) + { + } +} + +internal partial class ExtendedClass +{ +} + +internal partial class DerivedClass : ExtendedClass +{ + + public void TestExtensionConsumer() + { + this.TestExtension(); + } +}"); + } + + [Fact] + public async Task TestExtensionWithinNestedExtendedTypeAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module Extensions + + Sub TestExtension(extendedClass As NestingClass.ExtendedClass) + End Sub +End Module + +Class NestingClass + Class ExtendedClass + Sub TestExtensionConsumer() + TestExtension() + End Sub + End Class +End Class", @" +internal static partial class Extensions +{ + public static void TestExtension(this NestingClass.ExtendedClass extendedClass) + { + } +} + +internal partial class NestingClass +{ + public partial class ExtendedClass + { + public void TestExtensionConsumer() + { + this.TestExtension(); + } + } +}"); + } + + [Fact] + public async Task TestExtensionWithMeWithinExtendedTypeAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module Extensions + + Sub TestExtension(extendedClass As ExtendedClass) + End Sub +End Module + +Class ExtendedClass + Sub TestExtensionConsumer() + Me.TestExtension() + End Sub +End Class", @" +internal static partial class Extensions +{ + public static void TestExtension(this ExtendedClass extendedClass) + { + } +} + +internal partial class ExtendedClass +{ + public void TestExtensionConsumer() + { + this.TestExtension(); + } +}"); + } + + [Fact] + public async Task TestAsyncMethodsWithNoReturnAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Friend Partial Module TaskExtensions + + Async Function [Then](Of T)(ByVal task As Task, ByVal f As Func(Of Task(Of T))) As Task(Of T) + Await task + Return Await f() + End Function + + + Async Function [Then](ByVal task As Task, ByVal f As Func(Of Task)) As Task + Await task + Await f() + End Function + + + Async Function [Then](Of T, U)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task(Of U))) As Task(Of U) + Return Await f(Await task) + End Function + + + Async Function [Then](Of T)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task)) As Task + Await f(Await task) + End Function + + + Async Function [ThenExit](Of T)(ByVal task As Task(Of T), ByVal f As Func(Of T, Task)) As Task + Await f(Await task) + Exit Function + End Function +End Module", @"using System; +using System.Threading.Tasks; + +internal static partial class TaskExtensions +{ + public async static Task Then(this Task task, Func> f) + { + await task; + return await f(); + } + + public async static Task Then(this Task task, Func f) + { + await task; + await f(); + } + + public async static Task Then(this Task task, Func> f) + { + return await f(await task); + } + + public async static Task Then(this Task task, Func f) + { + await f(await task); + } + + public async static Task ThenExit(this Task task, Func f) + { + await f(await task); + return; + } +}"); + } + } +} diff --git a/Tests/CSharp/MemberTests/MemberTests/FieldTests.cs b/Tests/CSharp/MemberTests/MemberTests/FieldTests.cs new file mode 100644 index 00000000..fb420473 --- /dev/null +++ b/Tests/CSharp/MemberTests/MemberTests/FieldTests.cs @@ -0,0 +1,184 @@ +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; + +namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests.MemberTests; + +public class FieldTests : ConverterTestBase +{ + [Fact] + public async Task TestFieldAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass + Const answer As Integer = 42 + Private value As Integer = 10 + ReadOnly v As Integer = 15 +End Class", @" +internal partial class TestClass +{ + private const int answer = 42; + private int value = 10; + private readonly int v = 15; +}"); + } + + [Fact] + public async Task TestMultiArrayFieldAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass + Dim Parts(), Taxes(), Deposits()(), Prepaid()(), FromDate, ToDate As String +End Class", @" +internal partial class TestClass +{ + private string[] Parts, Taxes; + private string[][] Deposits, Prepaid; + private string FromDate, ToDate; +}"); + } + + [Fact] + public async Task TestConstantFieldInModuleAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Module TestModule + Const answer As Integer = 42 +End Module", @" +internal static partial class TestModule +{ + private const int answer = 42; +}"); + } + + [Fact] + public async Task TestTypeInferredConstAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass + Const someConstField = 42 + Sub TestMethod() + Const someConst = System.DateTimeKind.Local + End Sub +End Class", @"using System; + +internal partial class TestClass +{ + private const int someConstField = 42; + public void TestMethod() + { + const DateTimeKind someConst = DateTimeKind.Local; + } +}"); + } + + [Fact] + public async Task TestTypeInferredVarAsync() + { + // VB doesn't infer the type of EnumVariable like you'd think, it just uses object + // VB compiler uses Conversions rather than any plainer casting + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass + Public Enum TestEnum As Integer + Test1 + End Enum + + Dim EnumVariable = TestEnum.Test1 + Public Sub AMethod() + Dim t1 As Integer = EnumVariable + End Sub +End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic + +internal partial class TestClass +{ + public enum TestEnum : int + { + Test1 + } + + private object EnumVariable = TestEnum.Test1; + public void AMethod() + { + int t1 = Conversions.ToInteger(EnumVariable); + } +}"); + } + + [Fact] + public async Task FieldWithAttributeAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class TestClass + + Private Shared First As Integer +End Class", @"using System; + +internal partial class TestClass +{ + [ThreadStatic] + private static int First; +}"); + } + + [Fact] + public async Task FieldWithNonStaticInitializerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class A + Private x As Integer = 2 + Private y(x) As Integer +End Class", @" +public partial class A +{ + private int x = 2; + private int[] y; + + public A() + { + y = new int[x + 1]; + } +}"); + } + + [Fact] + public async Task FieldWithInstanceOperationOfDifferingTypeAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class DoesNotNeedConstructor + Private ReadOnly ClassVariable1 As New ParallelOptions With {.MaxDegreeOfParallelism = 5} +End Class", @"using System.Threading.Tasks; + +public partial class DoesNotNeedConstructor +{ + private readonly ParallelOptions ClassVariable1 = new ParallelOptions() { MaxDegreeOfParallelism = 5 }; +}"); + } + + [Fact] + public async Task Issue281FieldWithNonStaticLambdaInitializerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Imports System.IO + +Public Class Issue281 + Private lambda As System.Delegate = New ErrorEventHandler(Sub(a, b) Len(0)) + Private nonShared As System.Delegate = New ErrorEventHandler(AddressOf OnError) + + Sub OnError(s As Object, e As ErrorEventArgs) + End Sub +End Class", @"using System; +using System.IO; +using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic + +public partial class Issue281 +{ + private Delegate lambda = new ErrorEventHandler((a, b) => Strings.Len(0)); + private Delegate nonShared; + + public Issue281() + { + nonShared = new ErrorEventHandler(OnError); + } + + public void OnError(object s, ErrorEventArgs e) + { + } +}"); + } +} diff --git a/Tests/CSharp/MemberTests/MemberTests/InterfaceImplementationTests.cs b/Tests/CSharp/MemberTests/MemberTests/InterfaceImplementationTests.cs new file mode 100644 index 00000000..853d9d51 --- /dev/null +++ b/Tests/CSharp/MemberTests/MemberTests/InterfaceImplementationTests.cs @@ -0,0 +1,2153 @@ +using System; +using System.Threading.Tasks; +using System.Runtime.InteropServices; // For Out/Optional attributes if used in test signatures +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; + +namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests.MemberTests // As per prompt +{ + public class InterfaceImplementationTests : ConverterTestBase + { + [Fact] + public async Task TestExplicitInterfaceOfParametrizedPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Interface IClass + ReadOnly Property ReadOnlyPropToRename(i as Integer) As String + WriteOnly Property WriteOnlyPropToRename(i as Integer) As String + Property PropToRename(i as Integer) As String + + ReadOnly Property ReadOnlyPropNonPublic(i as Integer) As String + WriteOnly Property WriteOnlyPropNonPublic(i as Integer) As String + Property PropNonPublic(i as Integer) As String + + ReadOnly Property ReadOnlyPropToRenameNonPublic(i as Integer) As String + WriteOnly Property WriteOnlyPropToRenameNonPublic(i as Integer) As String + Property PropToRenameNonPublic(i as Integer) As String + +End Interface + +Class ChildClass + Implements IClass + + Public ReadOnly Property ReadOnlyPropRenamed(i As Integer) As String Implements IClass.ReadOnlyPropToRename + Get + Throw New NotImplementedException + End Get + End Property + + Public Overridable WriteOnly Property WriteOnlyPropRenamed(i As Integer) As String Implements IClass.WriteOnlyPropToRename + Set + Throw New NotImplementedException + End Set + End Property + + Public Overridable Property PropRenamed(i As Integer) As String Implements IClass.PropToRename + Get + Throw New NotImplementedException + End Get + Set + Throw New NotImplementedException + End Set + End Property + + Private ReadOnly Property ReadOnlyPropNonPublic(i As Integer) As String Implements IClass.ReadOnlyPropNonPublic + Get + Throw New NotImplementedException + End Get + End Property + + Protected Friend Overridable WriteOnly Property WriteOnlyPropNonPublic(i As Integer) As String Implements IClass.WriteOnlyPropNonPublic + Set + Throw New NotImplementedException + End Set + End Property + + Friend Overridable Property PropNonPublic(i As Integer) As String Implements IClass.PropNonPublic + Get + Throw New NotImplementedException + End Get + Set + Throw New NotImplementedException + End Set + End Property + + Protected Friend Overridable ReadOnly Property ReadOnlyPropRenamedNonPublic(i As Integer) As String Implements IClass.ReadOnlyPropToRenameNonPublic + Get + Throw New NotImplementedException + End Get + End Property + + Private WriteOnly Property WriteOnlyPropRenamedNonPublic(i As Integer) As String Implements IClass.WriteOnlyPropToRenameNonPublic + Set + Throw New NotImplementedException + End Set + End Property + + Friend Overridable Property PropToRenameNonPublic(i As Integer) As String Implements IClass.PropToRenameNonPublic + Get + Throw New NotImplementedException + End Get + Set + Throw New NotImplementedException + End Set + End Property +End Class +", @"using System; + +internal partial interface IClass +{ + string get_ReadOnlyPropToRename(int i); + void set_WriteOnlyPropToRename(int i, string value); + string get_PropToRename(int i); + void set_PropToRename(int i, string value); + + string get_ReadOnlyPropNonPublic(int i); + void set_WriteOnlyPropNonPublic(int i, string value); + string get_PropNonPublic(int i); + void set_PropNonPublic(int i, string value); + + string get_ReadOnlyPropToRenameNonPublic(int i); + void set_WriteOnlyPropToRenameNonPublic(int i, string value); + string get_PropToRenameNonPublic(int i); + void set_PropToRenameNonPublic(int i, string value); + +} + +internal partial class ChildClass : IClass +{ + + public string get_ReadOnlyPropRenamed(int i) + { + throw new NotImplementedException(); + } + string IClass.get_ReadOnlyPropToRename(int i) => get_ReadOnlyPropRenamed(i); + + public virtual void set_WriteOnlyPropRenamed(int i, string value) + { + throw new NotImplementedException(); + } + void IClass.set_WriteOnlyPropToRename(int i, string value) => set_WriteOnlyPropRenamed(i, value); + + public virtual string get_PropRenamed(int i) + { + throw new NotImplementedException(); + } + public virtual void set_PropRenamed(int i, string value) + { + throw new NotImplementedException(); + } + + string IClass.get_PropToRename(int i) => get_PropRenamed(i); + void IClass.set_PropToRename(int i, string value) => set_PropRenamed(i, value); + + private string get_ReadOnlyPropNonPublic(int i) + { + throw new NotImplementedException(); + } + string IClass.get_ReadOnlyPropNonPublic(int i) => get_ReadOnlyPropNonPublic(i); + + protected internal virtual void set_WriteOnlyPropNonPublic(int i, string value) + { + throw new NotImplementedException(); + } + void IClass.set_WriteOnlyPropNonPublic(int i, string value) => set_WriteOnlyPropNonPublic(i, value); + + internal virtual string get_PropNonPublic(int i) + { + throw new NotImplementedException(); + } + internal virtual void set_PropNonPublic(int i, string value) + { + throw new NotImplementedException(); + } + + string IClass.get_PropNonPublic(int i) => get_PropNonPublic(i); + void IClass.set_PropNonPublic(int i, string value) => set_PropNonPublic(i, value); + + protected internal virtual string get_ReadOnlyPropRenamedNonPublic(int i) + { + throw new NotImplementedException(); + } + string IClass.get_ReadOnlyPropToRenameNonPublic(int i) => get_ReadOnlyPropRenamedNonPublic(i); + + private void set_WriteOnlyPropRenamedNonPublic(int i, string value) + { + throw new NotImplementedException(); + } + void IClass.set_WriteOnlyPropToRenameNonPublic(int i, string value) => set_WriteOnlyPropRenamedNonPublic(i, value); + + internal virtual string get_PropToRenameNonPublic(int i) + { + throw new NotImplementedException(); + } + internal virtual void set_PropToRenameNonPublic(int i, string value) + { + throw new NotImplementedException(); + } + + string IClass.get_PropToRenameNonPublic(int i) => get_PropToRenameNonPublic(i); + void IClass.set_PropToRenameNonPublic(int i, string value) => set_PropToRenameNonPublic(i, value); +}"); + } + + [Fact] + public async Task Issue443_FixCaseForInterfaceMembersAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Function FooDifferentCase( ByRef str2 As String) As Integer +End Interface + +Public Class Foo + Implements IFoo + Function fooDifferentCase( ByRef str2 As String) As Integer Implements IFoo.FOODIFFERENTCASE + str2 = 2.ToString() + Return 3 + End Function +End Class", @" +public partial interface IFoo +{ + int FooDifferentCase(out string str2); +} + +public partial class Foo : IFoo +{ + public int FooDifferentCase(out string str2) + { + str2 = 2.ToString(); + return 3; + } +} +"); + } + + [Fact] + public async Task Issue444_FixNameForRenamedInterfaceMembersAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Function FooDifferentName(ByRef str As String, i As Integer) As Integer +End Interface + +Public Class Foo + Implements IFoo + + Function BarDifferentName(ByRef str As String, i As Integer) As Integer Implements IFoo.FooDifferentName + Return 4 + End Function +End Class", @" +public partial interface IFoo +{ + int FooDifferentName(ref string str, int i); +} + +public partial class Foo : IFoo +{ + + public int BarDifferentName(ref string str, int i) + { + return 4; + } + + int IFoo.FooDifferentName(ref string str, int i) => BarDifferentName(ref str, i); +} +"); + } + + [Fact] + public async Task IdenticalInterfaceMethodsWithRenamedInterfaceMembersAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Interface IBar + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Class FooBar + Implements IFoo, IBar + + Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar + Return 4 + End Function + + Function Bar(ByRef str As String, i As Integer) As Integer Implements IBar.DoFooBar + Return 2 + End Function + +End Class", @" +public partial interface IFoo +{ + int DoFooBar(ref string str, int i); +} + +public partial interface IBar +{ + int DoFooBar(ref string str, int i); +} + +public partial class FooBar : IFoo, IBar +{ + + public int Foo(ref string str, int i) + { + return 4; + } + + int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); + + public int Bar(ref string str, int i) + { + return 2; + } + + int IBar.DoFooBar(ref string str, int i) => Bar(ref str, i); + +} +"); + } + + [Fact] + public async Task RenamedInterfaceCasingOnlyDifferenceConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Function DoFoo() As Integer + Property Prop As Integer +End Interface + +Public Class Foo + Implements IFoo + + Private Function doFoo() As Integer Implements IFoo.DoFoo + Return 4 + End Function + + Private Property prop As Integer Implements IFoo.Prop + + Private Function Consumer() As Integer + Dim foo As New Foo() + Dim interfaceInstance As IFoo = foo + Return foo.doFoo() + foo.DoFoo() + + interfaceInstance.doFoo() + interfaceInstance.DoFoo() + + foo.prop + foo.Prop + + interfaceInstance.prop + interfaceInstance.Prop + End Function + +End Class", @" +public partial interface IFoo +{ + int DoFoo(); + int Prop { get; set; } +} + +public partial class Foo : IFoo +{ + + private int doFoo() + { + return 4; + } + + int IFoo.DoFoo() => doFoo(); + + private int prop { get; set; } + int IFoo.Prop { get => prop; set => prop = value; } + + private int Consumer() + { + var foo = new Foo(); + IFoo interfaceInstance = foo; + return foo.doFoo() + foo.doFoo() + interfaceInstance.DoFoo() + interfaceInstance.DoFoo() + foo.prop + foo.prop + interfaceInstance.Prop + interfaceInstance.Prop; + } + +} +"); + } + + [Fact] + public async Task RenamedInterfaceCasingOnlyDifferenceForVirtualMemberConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Function DoFoo() As Integer + Property Prop As Integer +End Interface + +Public MustInherit Class BaseFoo + Implements IFoo + + Protected Friend Overridable Function doFoo() As Integer Implements IFoo.DoFoo + Return 4 + End Function + + Protected Friend Overridable Property prop As Integer Implements IFoo.Prop + +End Class + +Public Class Foo + Inherits BaseFoo + + Protected Friend Overrides Function DoFoo() As Integer + Return 5 + End Function + + Protected Friend Overrides Property Prop As Integer + + Private Function Consumer() As Integer + Dim foo As New Foo() + Dim interfaceInstance As IFoo = foo + Dim baseClass As BaseFoo = foo + Return foo.doFoo() + foo.DoFoo() + + interfaceInstance.doFoo() + interfaceInstance.DoFoo() + + baseClass.doFoo() + baseClass.DoFoo() + + foo.prop + foo.Prop + + interfaceInstance.prop + interfaceInstance.Prop + + baseClass.prop + baseClass.Prop + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(); + int Prop { get; set; } +} + +public abstract partial class BaseFoo : IFoo +{ + + protected internal virtual int doFoo() + { + return 4; + } + + int IFoo.DoFoo() => doFoo(); + + protected internal virtual int prop { get; set; } + int IFoo.Prop { get => prop; set => prop = value; } + +} + +public partial class Foo : BaseFoo +{ + + protected internal override int doFoo() + { + return 5; + } + + protected internal override int prop { get; set; } + + private int Consumer() + { + var foo = new Foo(); + IFoo interfaceInstance = foo; + BaseFoo baseClass = foo; + return foo.doFoo() + foo.doFoo() + interfaceInstance.DoFoo() + interfaceInstance.DoFoo() + baseClass.doFoo() + baseClass.doFoo() + foo.prop + foo.prop + interfaceInstance.Prop + interfaceInstance.Prop + baseClass.prop + baseClass.prop; + } +} +"); + } + + [Fact] + public async Task RenamedInterfaceCasingOnlyDifferenceWithOverloadedPropertyConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IUserContext + ReadOnly Property GroupID As String +End Interface + +Public Interface IFoo + ReadOnly Property ConnectedGroupId As String +End Interface + +Public MustInherit Class BaseFoo + Implements IUserContext + + Protected Friend ReadOnly Property ConnectedGroupID() As String Implements IUserContext.GroupID + +End Class + +Public Class Foo + Inherits BaseFoo + Implements IFoo + + Protected Friend Overloads ReadOnly Property ConnectedGroupID As String Implements IFoo.ConnectedGroupId ' Comment moves because this line gets split + Get + Return If("""", MyBase.ConnectedGroupID()) + End Get + End Property + + Private Function Consumer() As String + Dim foo As New Foo() + Dim ifoo As IFoo = foo + Dim baseFoo As BaseFoo = foo + Dim iUserContext As IUserContext = foo + Return foo.ConnectedGroupID & foo.ConnectedGroupId & + iFoo.ConnectedGroupID & iFoo.ConnectedGroupId & + baseFoo.ConnectedGroupID & baseFoo.ConnectedGroupId & + iUserContext.GroupId & iUserContext.GroupID + End Function + +End Class", @" +public partial interface IUserContext +{ + string GroupID { get; } +} + +public partial interface IFoo +{ + string ConnectedGroupId { get; } +} + +public abstract partial class BaseFoo : IUserContext +{ + + protected internal string ConnectedGroupID { get; private set; } + string IUserContext.GroupID { get => ConnectedGroupID; } + +} + +public partial class Foo : BaseFoo, IFoo +{ + + protected internal new string ConnectedGroupID + { + get + { + return """" ?? base.ConnectedGroupID; + } + } + + string IFoo.ConnectedGroupId { get => ConnectedGroupID; } // Comment moves because this line gets split + + private string Consumer() + { + var foo = new Foo(); + IFoo ifoo = foo; + BaseFoo baseFoo = foo; + IUserContext iUserContext = foo; + return foo.ConnectedGroupID + foo.ConnectedGroupID + ifoo.ConnectedGroupId + ifoo.ConnectedGroupId + baseFoo.ConnectedGroupID + baseFoo.ConnectedGroupID + iUserContext.GroupID + iUserContext.GroupID; + } + +} +"); + } + + [Fact] + public async Task RenamedMethodImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Interface IBar + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Class FooBar + Implements IFoo, IBar + + Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar, IBar.DoFooBar + Return 4 + End Function + +End Class", @" +public partial interface IFoo +{ + int DoFooBar(ref string str, int i); +} + +public partial interface IBar +{ + int DoFooBar(ref string str, int i); +} + +public partial class FooBar : IFoo, IBar +{ + + public int Foo(ref string str, int i) + { + return 4; + } + + int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); + int IBar.DoFooBar(ref string str, int i) => Foo(ref str, i); + +}"); + } + + [Fact] + public async Task IdenticalInterfacePropertiesWithRenamedInterfaceMembersAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property FooBarProp As Integer + End Interface + +Public Interface IBar + Property FooBarProp As Integer +End Interface + +Public Class FooBar + Implements IFoo, IBar + + Property Foo As Integer Implements IFoo.FooBarProp + + Property Bar As Integer Implements IBar.FooBarProp + +End Class", @" +public partial interface IFoo +{ + int FooBarProp { get; set; } +} + +public partial interface IBar +{ + int FooBarProp { get; set; } +} + +public partial class FooBar : IFoo, IBar +{ + + public int Foo { get; set; } + int IFoo.FooBarProp { get => Foo; set => Foo = value; } + + public int Bar { get; set; } + int IBar.FooBarProp { get => Bar; set => Bar = value; } + +}"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationRequiredMethodParameters_749_Async() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Interface IBar + Function DoFooBar(ByRef str As String, i As Integer) As Integer +End Interface + +Public Class FooBar + Implements IFoo, IBar + + Function Foo(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFooBar + Return 4 + End Function + + Function Bar(ByRef str As String, i As Integer) As Integer Implements IBar.DoFooBar + Return 2 + End Function + +End Class", @" +public partial interface IFoo +{ + int DoFooBar(ref string str, int i); +} + +public partial interface IBar +{ + int DoFooBar(ref string str, int i); +} + +public partial class FooBar : IFoo, IBar +{ + + public int Foo(ref string str, int i) + { + return 4; + } + + int IFoo.DoFooBar(ref string str, int i) => Foo(ref str, i); + + public int Bar(ref string str, int i) + { + return 2; + } + + int IBar.DoFooBar(ref string str, int i) => Bar(ref str, i); + +} +"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationOptionalParameters_1062_Async() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface InterfaceWithOptionalParameters + Sub S(Optional i As Integer = 0) +End Interface + +Public Class ImplInterfaceWithOptionalParameters : Implements InterfaceWithOptionalParameters + Public Sub InterfaceWithOptionalParameters_S(Optional i As Integer = 0) Implements InterfaceWithOptionalParameters.S + End Sub +End Class", @" +public partial interface InterfaceWithOptionalParameters +{ + void S(int i = 0); +} + +public partial class ImplInterfaceWithOptionalParameters : InterfaceWithOptionalParameters +{ + public void InterfaceWithOptionalParameters_S(int i = 0) + { + } + + void InterfaceWithOptionalParameters.S(int i = 0) => InterfaceWithOptionalParameters_S(i); +} +"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationOptionalParametersAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property ExplicitProp(Optional str As String = """") As Integer + Function ExplicitFunc(Optional str2 As String = """", Optional i2 As Integer = 1) As Integer +End Interface + +Public Class Foo + Implements IFoo + + Private Function ExplicitFunc(Optional str As String = """", Optional i2 As Integer = 1) As Integer Implements IFoo.ExplicitFunc + Return 5 + End Function + + Private Property ExplicitProp(Optional str As String = """") As Integer Implements IFoo.ExplicitProp + Get + Return 5 + End Get + Set(value As Integer) + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int get_ExplicitProp(string str = """"); + void set_ExplicitProp(string str = """", int value = default); + int ExplicitFunc(string str2 = """", int i2 = 1); +} + +public partial class Foo : IFoo +{ + + private int ExplicitFunc(string str = """", int i2 = 1) + { + return 5; + } + + int IFoo.ExplicitFunc(string str = """", int i2 = 1) => ExplicitFunc(str, i2); + + private int get_ExplicitProp(string str = """") + { + return 5; + } + private void set_ExplicitProp(string str = """", int value = default) + { + } + + int IFoo.get_ExplicitProp(string str = """") => get_ExplicitProp(str); + void IFoo.set_ExplicitProp(string str = """", int value = default) => set_ExplicitProp(str, value); +} +"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationOptionalMethodParameters_749_Async() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Function DoFooBar(ByRef str As String, Optional i As Integer = 4) As Integer +End Interface + +Public Interface IBar + Function DoFooBar(ByRef str As String, Optional i As Integer = 8) As Integer +End Interface + +Public Class FooBar + Implements IFoo, IBar + + Function Foo(ByRef str As String, Optional i As Integer = 4) As Integer Implements IFoo.DoFooBar + Return 4 + End Function + + Function Bar(ByRef str As String, Optional i As Integer = 8) As Integer Implements IBar.DoFooBar + Return 2 + End Function + +End Class", @" +public partial interface IFoo +{ + int DoFooBar(ref string str, int i = 4); +} + +public partial interface IBar +{ + int DoFooBar(ref string str, int i = 8); +} + +public partial class FooBar : IFoo, IBar +{ + + public int Foo(ref string str, int i = 4) + { + return 4; + } + + int IFoo.DoFooBar(ref string str, int i = 4) => Foo(ref str, i); + + public int Bar(ref string str, int i = 8) + { + return 2; + } + + int IBar.DoFooBar(ref string str, int i = 8) => Bar(ref str, i); + +} +"); + } + + [Fact] + public async Task RenamedInterfaceMethodFullyQualifiedAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Namespace TestNamespace + Public Interface IFoo + Function DoFoo(ByRef str As String, i As Integer) As Integer + End Interface +End Namespace + +Public Class Foo + Implements TestNamespace.IFoo + + Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements TestNamespace.IFoo.DoFoo + Return 4 + End Function +End Class", @" +namespace TestNamespace +{ + public partial interface IFoo + { + int DoFoo(ref string str, int i); + } +} + +public partial class Foo : TestNamespace.IFoo +{ + + public int DoFooRenamed(ref string str, int i) + { + return 4; + } + + int TestNamespace.IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); +}"); + } + + [Fact] + public async Task RenamedInterfacePropertyFullyQualifiedAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Namespace TestNamespace + Public Interface IFoo + Property FooProp As Integer + End Interface +End Namespace + +Public Class Foo + Implements TestNamespace.IFoo + + Property FooPropRenamed As Integer Implements TestNamespace.IFoo.FooProp + +End Class", @" +namespace TestNamespace +{ + public partial interface IFoo + { + int FooProp { get; set; } + } +} + +public partial class Foo : TestNamespace.IFoo +{ + + public int FooPropRenamed { get; set; } + int TestNamespace.IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } + +}"); + } + + [Fact] + public async Task RenamedInterfaceMethodConsumerCasingRenamedAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Function DoFoo(ByRef str As String, i As Integer) As Integer + End Interface + +Public Class Foo + Implements IFoo + + Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo + Return 4 + End Function +End Class + +Public Class FooConsumer + Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.DOFOORENAMED(str, i) + bar.DoFoo(str, i) + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(ref string str, int i); +} + +public partial class Foo : IFoo +{ + + public int DoFooRenamed(ref string str, int i) + { + return 4; + } + + int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); +} + +public partial class FooConsumer +{ + public int DoFooRenamedConsumer(ref string str, int i) + { + var foo = new Foo(); + IFoo bar = foo; + return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); + } +}"); + } + + [Fact] + public async Task RenamedInterfacePropertyConsumerCasingRenamedAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Property FooProp As Integer + End Interface + +Public Class Foo + Implements IFoo + + Property FooPropRenamed As Integer Implements IFoo.FooProp + +End Class + +Public Class FooConsumer + Function GetFooRenamed() As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.FOOPROPRENAMED + bar.FooProp + End Function +End Class", @" +public partial interface IFoo +{ + int FooProp { get; set; } +} + +public partial class Foo : IFoo +{ + + public int FooPropRenamed { get; set; } + int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } + +} + +public partial class FooConsumer +{ + public int GetFooRenamed() + { + var foo = new Foo(); + IFoo bar = foo; + return foo.FooPropRenamed + bar.FooProp; + } +}"); + } + + [Fact] + public async Task InterfaceMethodCasingRenamedConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Function DoFoo(str As String, i As Integer) As Integer + End Interface + +Public Class Foo + Implements IFoo + + Function dofoo(str As String, i As Integer) As Integer Implements IFoo.DoFoo + Return 4 + End Function +End Class + +Public Class FooConsumer + Function DoFooRenamedConsumer(str As String, i As Integer) As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.dofoo(str, i) + bar.DoFoo(str, i) + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(string str, int i); +} + +public partial class Foo : IFoo +{ + + public int DoFoo(string str, int i) + { + return 4; + } +} + +public partial class FooConsumer +{ + public int DoFooRenamedConsumer(string str, int i) + { + var foo = new Foo(); + IFoo bar = foo; + return foo.DoFoo(str, i) + bar.DoFoo(str, i); + } +}"); + } + + [Fact] + public async Task InterfacePropertyCasingRenamedConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Property FooProp As Integer + End Interface + +Public Class Foo + Implements IFoo + + Property fooprop As Integer Implements IFoo.FooProp + +End Class + +Public Class FooConsumer + Function GetFooRenamed() As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.fooprop + bar.FooProp + End Function +End Class", @" +public partial interface IFoo +{ + int FooProp { get; set; } +} + +public partial class Foo : IFoo +{ + + public int FooProp { get; set; } + +} + +public partial class FooConsumer +{ + public int GetFooRenamed() + { + var foo = new Foo(); + IFoo bar = foo; + return foo.FooProp + bar.FooProp; + } +}"); + } + + [Fact] + public async Task InterfaceRenamedMethodConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Function DoFoo(ByRef str As String, i As Integer) As Integer + End Interface + +Public Class Foo + Implements IFoo + + Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo + Return 4 + End Function +End Class + +Public Class FooConsumer + Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.DoFooRenamed(str, i) + bar.DoFoo(str, i) + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(ref string str, int i); +} + +public partial class Foo : IFoo +{ + + public int DoFooRenamed(ref string str, int i) + { + return 4; + } + + int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); +} + +public partial class FooConsumer +{ + public int DoFooRenamedConsumer(ref string str, int i) + { + var foo = new Foo(); + IFoo bar = foo; + return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); + } +}"); + } + + [Fact] + public async Task InterfaceRenamedPropertyConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Property FooProp As Integer + End Interface + +Public Class Foo + Implements IFoo + + Property FooPropRenamed As Integer Implements IFoo.FooProp + +End Class + +Public Class FooConsumer + Function GetFooRenamed() As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.FooPropRenamed + bar.FooProp + End Function +End Class", @" +public partial interface IFoo +{ + int FooProp { get; set; } +} + +public partial class Foo : IFoo +{ + + public int FooPropRenamed { get; set; } + int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } + +} + +public partial class FooConsumer +{ + public int GetFooRenamed() + { + var foo = new Foo(); + IFoo bar = foo; + return foo.FooPropRenamed + bar.FooProp; + } +}"); + } + + [Fact] + public async Task PartialInterfaceRenamedMethodConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Partial Interface IFoo + Function DoFoo(ByRef str As String, i As Integer) As Integer + End Interface + +Public Class Foo + Implements IFoo + + Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo + Return 4 + End Function +End Class + +Public Class FooConsumer + Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.DoFooRenamed(str, i) + bar.DoFoo(str, i) + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(ref string str, int i); +} + +public partial class Foo : IFoo +{ + + public int DoFooRenamed(ref string str, int i) + { + return 4; + } + + int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); +} + +public partial class FooConsumer +{ + public int DoFooRenamedConsumer(ref string str, int i) + { + var foo = new Foo(); + IFoo bar = foo; + return foo.DoFooRenamed(ref str, i) + bar.DoFoo(ref str, i); + } +}"); + } + + [Fact] + public async Task PartialInterfaceRenamedPropertyConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Partial Interface IFoo + Property FooProp As Integer + End Interface + +Public Class Foo + Implements IFoo + + Property FooPropRenamed As Integer Implements IFoo.FooProp + +End Class + +Public Class FooConsumer + Function GetFooRenamed() As Integer + Dim foo As New Foo + Dim bar As IFoo = foo + Return foo.FooPropRenamed + bar.FooProp + End Function +End Class", @" +public partial interface IFoo +{ + int FooProp { get; set; } +} + +public partial class Foo : IFoo +{ + + public int FooPropRenamed { get; set; } + int IFoo.FooProp { get => FooPropRenamed; set => FooPropRenamed = value; } + +} + +public partial class FooConsumer +{ + public int GetFooRenamed() + { + var foo = new Foo(); + IFoo bar = foo; + return foo.FooPropRenamed + bar.FooProp; + } +}"); + } + + [Fact] + public async Task RenamedInterfaceMethodMyClassConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + Function DoFoo(ByRef str As String, i As Integer) As Integer + End Interface + +Public Class Foo + Implements IFoo + + Overridable Function DoFooRenamed(ByRef str As String, i As Integer) As Integer Implements IFoo.DoFoo ' Comment ends up out of order, but attached to correct method + Return 4 + End Function + + Function DoFooRenamedConsumer(ByRef str As String, i As Integer) As Integer + Return MyClass.DoFooRenamed(str, i) + End Function +End Class", @" +public partial interface IFoo +{ + int DoFoo(ref string str, int i); +} + +public partial class Foo : IFoo +{ + + public int MyClassDoFooRenamed(ref string str, int i) + { + return 4; + } + + int IFoo.DoFoo(ref string str, int i) => DoFooRenamed(ref str, i); + public virtual int DoFooRenamed(ref string str, int i) => MyClassDoFooRenamed(ref str, i); // Comment ends up out of order, but attached to correct method + + public int DoFooRenamedConsumer(ref string str, int i) + { + return MyClassDoFooRenamed(ref str, i); + } +}"); + } + + [Fact] + public async Task RenamedInterfacePropertyMyClassConsumerAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Interface IFoo + ReadOnly Property DoFoo As Integer + WriteOnly Property DoBar As Integer + End Interface + +Public Class Foo + Implements IFoo + + Overridable ReadOnly Property DoFooRenamed As Integer Implements IFoo.DoFoo ' Comment ends up out of order, but attached to correct method + Get + Return 4 + End Get + End Property + + Overridable WriteOnly Property DoBarRenamed As Integer Implements IFoo.DoBar ' Comment ends up out of order, but attached to correct method + Set + Throw New Exception() + End Set + End Property + + Sub DoFooRenamedConsumer() + MyClass.DoBarRenamed = MyClass.DoFooRenamed + End Sub +End Class", @"using System; + +public partial interface IFoo +{ + int DoFoo { get; } + int DoBar { set; } +} + +public partial class Foo : IFoo +{ + + public int MyClassDoFooRenamed + { + get + { + return 4; + } + } + + int IFoo.DoFoo { get => DoFooRenamed; } + + public virtual int DoFooRenamed // Comment ends up out of order, but attached to correct method + { + get + { + return MyClassDoFooRenamed; + } + } + + public int MyClassDoBarRenamed + { + set + { + throw new Exception(); + } + } + + int IFoo.DoBar { set => DoBarRenamed = value; } + + public virtual int DoBarRenamed // Comment ends up out of order, but attached to correct method + { + set + { + MyClassDoBarRenamed = value; + } + } + + public void DoFooRenamedConsumer() + { + MyClassDoBarRenamed = MyClassDoFooRenamed; + } +}"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property ExplicitProp(str As String) As Integer + Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer +End Interface + +Public Class Foo + Implements IFoo + + Private Function ExplicitFunc(ByRef str As String, i As Integer) As Integer Implements IFoo.ExplicitFunc + Return 5 + End Function + + Private Property ExplicitProp(str As String) As Integer Implements IFoo.ExplicitProp + Get + Return 5 + End Get + Set(value As Integer) + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int get_ExplicitProp(string str); + void set_ExplicitProp(string str, int value); + int ExplicitFunc(ref string str2, int i2); +} + +public partial class Foo : IFoo +{ + + private int ExplicitFunc(ref string str, int i) + { + return 5; + } + + int IFoo.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); + + private int get_ExplicitProp(string str) + { + return 5; + } + private void set_ExplicitProp(string str, int value) + { + } + + int IFoo.get_ExplicitProp(string str) => get_ExplicitProp(str); + void IFoo.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); +} +"); + } + + [Fact] + public async Task PropertyInterfaceImplementationKeepsVirtualModifierAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property PropParams(str As String) As Integer + Property Prop() As Integer +End Interface + +Public Class Foo + Implements IFoo + + Public Overridable Property PropParams(str As String) As Integer Implements IFoo.PropParams + Get + Return 5 + End Get + Set(value As Integer) + End Set + End Property + + Public Overridable Property Prop As Integer Implements IFoo.Prop + Get + Return 5 + End Get + Set(value As Integer) + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int get_PropParams(string str); + void set_PropParams(string str, int value); + int Prop { get; set; } +} + +public partial class Foo : IFoo +{ + + public virtual int get_PropParams(string str) + { + return 5; + } + public virtual void set_PropParams(string str, int value) + { + } + + public virtual int Prop + { + get + { + return 5; + } + set + { + } + } +} +"); + } + + [Fact] + public async Task PrivateAutoPropertyImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property ExplicitProp As Integer +End Interface + +Public Interface IBar + Property ExplicitProp As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + Private Property ExplicitProp As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { get; set; } +} + +public partial interface IBar +{ + int ExplicitProp { get; set; } +} + +public partial class Foo : IFoo, IBar +{ + + private int ExplicitProp { get; set; } + int IFoo.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } + int IBar.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } +}"); + } + + + [Fact] + public async Task ImplementMultipleRenamedPropertiesFromInterfaceAsAbstractAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Property ExplicitProp As Integer +End Interface +Public Interface IBar + Property ExplicitProp As Integer +End Interface +Public MustInherit Class Foo + Implements IFoo, IBar + + Protected MustOverride Property ExplicitPropRenamed1 As Integer Implements IFoo.ExplicitProp + Protected MustOverride Property ExplicitPropRenamed2 As Integer Implements IBar.ExplicitProp +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { get; set; } +} + +public partial interface IBar +{ + int ExplicitProp { get; set; } +} +public abstract partial class Foo : IFoo, IBar +{ + + protected abstract int ExplicitPropRenamed1 { get; set; } + int IFoo.ExplicitProp { get => ExplicitPropRenamed1; set => ExplicitPropRenamed1 = value; } + protected abstract int ExplicitPropRenamed2 { get; set; } + int IBar.ExplicitProp { get => ExplicitPropRenamed2; set => ExplicitPropRenamed2 = value; } +}"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationForVirtualMemberFromAnotherClassAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Sub Save() + Property Prop As Integer +End Interface + +Public MustInherit Class BaseFoo + Protected Overridable Sub OnSave() + End Sub + + Protected Overridable Property MyProp As Integer = 5 +End Class + +Public Class Foo + Inherits BaseFoo + Implements IFoo + + Protected Overrides Sub OnSave() Implements IFoo.Save + End Sub + + Protected Overrides Property MyProp As Integer = 6 Implements IFoo.Prop + +End Class", @" +public partial interface IFoo +{ + void Save(); + int Prop { get; set; } +} + +public abstract partial class BaseFoo +{ + protected virtual void OnSave() + { + } + + protected virtual int MyProp { get; set; } = 5; +} + +public partial class Foo : BaseFoo, IFoo +{ + + protected override void OnSave() + { + } + + void IFoo.Save() => OnSave(); + + protected override int MyProp { get; set; } = 6; + int IFoo.Prop { get => MyProp; set => MyProp = value; } + +}"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationWhereOnlyOneInterfaceMemberIsRenamedAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Sub Save() + Property A As Integer +End Interface + +Public Interface IBar + Sub OnSave() + Property B As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + Public Overridable Sub Save() Implements IFoo.Save, IBar.OnSave + End Sub + + Public Overridable Property A As Integer Implements IFoo.A, IBar.B + +End Class", @" +public partial interface IFoo +{ + void Save(); + int A { get; set; } +} + +public partial interface IBar +{ + void OnSave(); + int B { get; set; } +} + +public partial class Foo : IFoo, IBar +{ + + public virtual void Save() + { + } + + void IFoo.Save() => Save(); + void IBar.OnSave() => Save(); + + public virtual int A { get; set; } + int IFoo.A { get => A; set => A = value; } + int IBar.B { get => A; set => A = value; } + +}"); + } + + [Fact] + public async Task ExplicitInterfaceImplementationWhereMemberShadowsBaseAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Sub Save() + Property Prop As Integer +End Interface + +Public MustInherit Class BaseFoo + Public Overridable Sub OnSave() + End Sub + + Public Overridable Property MyProp As Integer = 5 +End Class + +Public Class Foo + Inherits BaseFoo + Implements IFoo + + Public Shadows Sub OnSave() Implements IFoo.Save + End Sub + + Public Shadows Property MyProp As Integer = 6 Implements IFoo.Prop + +End Class", @" +public partial interface IFoo +{ + void Save(); + int Prop { get; set; } +} + +public abstract partial class BaseFoo +{ + public virtual void OnSave() + { + } + + public virtual int MyProp { get; set; } = 5; +} + +public partial class Foo : BaseFoo, IFoo +{ + + public new void OnSave() + { + } + + void IFoo.Save() => OnSave(); + + public new int MyProp { get; set; } = 6; + int IFoo.Prop { get => MyProp; set => MyProp = value; } + +}"); + } + + [Fact] + public async Task PrivatePropertyAccessorBlocksImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property ExplicitProp As Integer +End Interface + +Public Interface IBar + Property ExplicitProp As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + Private Property ExplicitProp As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp ' Comment moves because this line gets split + Get + Return 5 + End Get + Set + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { get; set; } +} + +public partial interface IBar +{ + int ExplicitProp { get; set; } +} + +public partial class Foo : IFoo, IBar +{ + + private int ExplicitProp + { + get + { + return 5; + } + set + { + } + } + + int IFoo.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } + int IBar.ExplicitProp { get => ExplicitProp; set => ExplicitProp = value; } // Comment moves because this line gets split +}"); + } + + [Fact] + public async Task NonPublicImplementsInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property FriendProp As Integer + Sub ProtectedSub() + Function PrivateFunc() As Integer + Sub ProtectedInternalSub() + Sub AbstractSub() +End Interface + +Public Interface IBar + Property FriendProp As Integer + Sub ProtectedSub() + Function PrivateFunc() As Integer + Sub ProtectedInternalSub() + Sub AbstractSub() +End Interface + +Public MustInherit Class BaseFoo + Implements IFoo, IBar + + Friend Overridable Property FriendProp As Integer Implements IFoo.FriendProp, IBar.FriendProp ' Comment moves because this line gets split + Get + Return 5 + End Get + Set + End Set + End Property + + Protected Sub ProtectedSub() Implements IFoo.ProtectedSub, IBar.ProtectedSub + End Sub + + Private Function PrivateFunc() As Integer Implements IFoo.PrivateFunc, IBar.PrivateFunc + End Function + + Protected Friend Overridable Sub ProtectedInternalSub() Implements IFoo.ProtectedInternalSub, IBar.ProtectedInternalSub + End Sub + + Protected MustOverride Sub AbstractSubRenamed() Implements IFoo.AbstractSub, IBar.AbstractSub +End Class + +Public Class Foo + Inherits BaseFoo + + Protected Friend Overrides Sub ProtectedInternalSub() + End Sub + + Protected Overrides Sub AbstractSubRenamed() + End Sub +End Class +", @" +public partial interface IFoo +{ + int FriendProp { get; set; } + void ProtectedSub(); + int PrivateFunc(); + void ProtectedInternalSub(); + void AbstractSub(); +} + +public partial interface IBar +{ + int FriendProp { get; set; } + void ProtectedSub(); + int PrivateFunc(); + void ProtectedInternalSub(); + void AbstractSub(); +} + +public abstract partial class BaseFoo : IFoo, IBar +{ + + internal virtual int FriendProp + { + get + { + return 5; + } + set + { + } + } + + int IFoo.FriendProp { get => FriendProp; set => FriendProp = value; } + int IBar.FriendProp { get => FriendProp; set => FriendProp = value; } // Comment moves because this line gets split + + protected void ProtectedSub() + { + } + + void IFoo.ProtectedSub() => ProtectedSub(); + void IBar.ProtectedSub() => ProtectedSub(); + + private int PrivateFunc() + { + return default; + } + + int IFoo.PrivateFunc() => PrivateFunc(); + int IBar.PrivateFunc() => PrivateFunc(); + + protected internal virtual void ProtectedInternalSub() + { + } + + void IFoo.ProtectedInternalSub() => ProtectedInternalSub(); + void IBar.ProtectedInternalSub() => ProtectedInternalSub(); + + protected abstract void AbstractSubRenamed(); + void IFoo.AbstractSub() => AbstractSubRenamed(); + void IBar.AbstractSub() => AbstractSubRenamed(); +} + +public partial class Foo : BaseFoo +{ + + protected internal override void ProtectedInternalSub() + { + } + + protected override void AbstractSubRenamed() + { + } +}"); + } + + [Fact] + public async Task ExplicitPropertyImplementationWithDirectAccessAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Interface IFoo + Property ExplicitProp As Integer + ReadOnly Property ExplicitReadOnlyProp As Integer +End Interface + +Public Class Foo + Implements IFoo + + Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp + ReadOnly Property ExplicitRenamedReadOnlyProp As Integer Implements IFoo.ExplicitReadOnlyProp + + Private Sub Consumer() + _ExplicitPropRenamed = 5 + _ExplicitRenamedReadOnlyProp = 10 + End Sub + +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { get; set; } + int ExplicitReadOnlyProp { get; } +} + +public partial class Foo : IFoo +{ + + public int ExplicitPropRenamed { get; set; } + int IFoo.ExplicitProp { get => ExplicitPropRenamed; set => ExplicitPropRenamed = value; } + public int ExplicitRenamedReadOnlyProp { get; private set; } + int IFoo.ExplicitReadOnlyProp { get => ExplicitRenamedReadOnlyProp; } + + private void Consumer() + { + ExplicitPropRenamed = 5; + ExplicitRenamedReadOnlyProp = 10; + } + +}"); + } + + [Fact] + public async Task ReadonlyRenamedPropertyImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + ReadOnly Property ExplicitProp As Integer +End Interface + +Public Interface IBar + ReadOnly Property ExplicitProp As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + ReadOnly Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { get; } +} + +public partial interface IBar +{ + int ExplicitProp { get; } +} + +public partial class Foo : IFoo, IBar +{ + + public int ExplicitPropRenamed { get; private set; } + int IFoo.ExplicitProp { get => ExplicitPropRenamed; } + int IBar.ExplicitProp { get => ExplicitPropRenamed; } +}"); + } + + [Fact] + public async Task WriteonlyPropertyImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + WriteOnly Property ExplicitProp As Integer +End Interface + +Public Interface IBar + WriteOnly Property ExplicitProp As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + WriteOnly Property ExplicitPropRenamed As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp ' Comment moves because this line gets split + Set + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int ExplicitProp { set; } +} + +public partial interface IBar +{ + int ExplicitProp { set; } +} + +public partial class Foo : IFoo, IBar +{ + + public int ExplicitPropRenamed + { + set + { + } + } + + int IFoo.ExplicitProp { set => ExplicitPropRenamed = value; } + int IBar.ExplicitProp { set => ExplicitPropRenamed = value; } // Comment moves because this line gets split +}"); + } + + [Fact] + public async Task PrivateMethodAndParameterizedPropertyImplementsMultipleInterfacesAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Property ExplicitProp(str As String) As Integer + Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer +End Interface + +Public Interface IBar + Property ExplicitProp(str As String) As Integer + Function ExplicitFunc(ByRef str2 As String, i2 As Integer) As Integer +End Interface + +Public Class Foo + Implements IFoo, IBar + + Private Function ExplicitFunc(ByRef str As String, i As Integer) As Integer Implements IFoo.ExplicitFunc, IBar.ExplicitFunc + Return 5 + End Function + + Private Property ExplicitProp(str As String) As Integer Implements IFoo.ExplicitProp, IBar.ExplicitProp + Get + Return 5 + End Get + Set(value As Integer) + End Set + End Property +End Class", @" +public partial interface IFoo +{ + int get_ExplicitProp(string str); + void set_ExplicitProp(string str, int value); + int ExplicitFunc(ref string str2, int i2); +} + +public partial interface IBar +{ + int get_ExplicitProp(string str); + void set_ExplicitProp(string str, int value); + int ExplicitFunc(ref string str2, int i2); +} + +public partial class Foo : IFoo, IBar +{ + + private int ExplicitFunc(ref string str, int i) + { + return 5; + } + + int IFoo.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); + int IBar.ExplicitFunc(ref string str, int i) => ExplicitFunc(ref str, i); + + private int get_ExplicitProp(string str) + { + return 5; + } + private void set_ExplicitProp(string str, int value) + { + } + + int IFoo.get_ExplicitProp(string str) => get_ExplicitProp(str); + int IBar.get_ExplicitProp(string str) => get_ExplicitProp(str); + void IFoo.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); + void IBar.set_ExplicitProp(string str, int value) => set_ExplicitProp(str, value); +}"); + } + + /// + /// + /// + /// + [Fact] + public async Task Issue444_InternalMemberDelegatingMethodAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Public Interface IFoo + Function FooDifferentName(ByRef str As String, i As Integer) As Integer +End Interface + +Friend Class Foo + Implements IFoo + + Function BarDifferentName(ByRef str As String, i As Integer) As Integer Implements IFoo.FooDifferentName + Return 4 + End Function +End Class", @" +public partial interface IFoo +{ + int FooDifferentName(ref string str, int i); +} + +internal partial class Foo : IFoo +{ + + public int BarDifferentName(ref string str, int i) + { + return 4; + } + + int IFoo.FooDifferentName(ref string str, int i) => BarDifferentName(ref str, i); +} +"); + } + } +} diff --git a/Tests/CSharp/MemberTests/MemberTests/PropertyTests.cs b/Tests/CSharp/MemberTests/MemberTests/PropertyTests.cs new file mode 100644 index 00000000..c9de6543 --- /dev/null +++ b/Tests/CSharp/MemberTests/MemberTests/PropertyTests.cs @@ -0,0 +1,333 @@ +using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Tests.TestRunners; +using Xunit; +using System; // For NotImplementedException + +namespace ICSharpCode.CodeConverter.Tests.CSharp.MemberTests.MemberTests; + +public class PropertyTests : ConverterTestBase +{ + [Fact] + public async Task TestAbstractMethodAndPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"MustInherit Class TestClass + Public MustOverride Sub TestMethod() + Public MustOverride ReadOnly Property AbstractProperty As String +End Class", @" +internal abstract partial class TestClass +{ + public abstract void TestMethod(); + public abstract string AbstractProperty { get; } +}"); + } + + [Fact] + public async Task TestAbstractReadOnlyAndWriteOnlyPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"MustInherit Class TestClass + Public MustOverride ReadOnly Property ReadOnlyProp As String + Public MustOverride WriteOnly Property WriteOnlyProp As String +End Class + +Class ChildClass + Inherits TestClass + + Public Overrides ReadOnly Property ReadOnlyProp As String + Public Overrides WriteOnly Property WriteOnlyProp As String + Set + End Set + End Property +End Class +", @" +internal abstract partial class TestClass +{ + public abstract string ReadOnlyProp { get; } + public abstract string WriteOnlyProp { set; } +} + +internal partial class ChildClass : TestClass +{ + + public override string ReadOnlyProp { get; } + public override string WriteOnlyProp + { + set + { + } + } +}"); + } + + [Fact] + public async Task TestReadOnlyOrWriteOnlyPropertyImplementedByNormalPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Interface IClass + ReadOnly Property ReadOnlyPropParam(i as Integer) As Integer + ReadOnly Property ReadOnlyProp As Integer + + WriteOnly Property WriteOnlyPropParam(i as Integer) As Integer + WriteOnly Property WriteOnlyProp As Integer +End Interface + +Class ChildClass + Implements IClass + + Public Overridable Property RenamedPropertyParam(i As Integer) As Integer Implements IClass.ReadOnlyPropParam + Get + Return 1 + End Get + Set + End Set + End Property + + Public Overridable Property RenamedReadOnlyProperty As Integer Implements IClass.ReadOnlyProp ' Comment moves because this line gets split + Get + Return 2 + End Get + Set + End Set + End Property + + Public Overridable Property RenamedWriteOnlyPropParam(i As Integer) As Integer Implements IClass.WriteOnlyPropParam + Get + Return 1 + End Get + Set + End Set + End Property + + Public Overridable Property RenamedWriteOnlyProperty As Integer Implements IClass.WriteOnlyProp ' Comment moves because this line gets split + Get + Return 2 + End Get + Set + End Set + End Property +End Class +", @" +internal partial interface IClass +{ + int get_ReadOnlyPropParam(int i); + int ReadOnlyProp { get; } + + void set_WriteOnlyPropParam(int i, int value); + int WriteOnlyProp { set; } +} + +internal partial class ChildClass : IClass +{ + + public virtual int get_RenamedPropertyParam(int i) + { + return 1; + } + public virtual void set_RenamedPropertyParam(int i, int value) + { + } + int IClass.get_ReadOnlyPropParam(int i) => get_RenamedPropertyParam(i); + + public virtual int RenamedReadOnlyProperty + { + get + { + return 2; + } + set + { + } + } + + int IClass.ReadOnlyProp { get => RenamedReadOnlyProperty; } // Comment moves because this line gets split + + public virtual int get_RenamedWriteOnlyPropParam(int i) + { + return 1; + } + public virtual void set_RenamedWriteOnlyPropParam(int i, int value) + { + } + void IClass.set_WriteOnlyPropParam(int i, int value) => set_RenamedWriteOnlyPropParam(i, value); + + public virtual int RenamedWriteOnlyProperty + { + get + { + return 2; + } + set + { + } + } + + int IClass.WriteOnlyProp { set => RenamedWriteOnlyProperty = value; } // Comment moves because this line gets split +}"); + } + + [Fact] + public async Task SetterProperty1053Async() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Property Prop(ByVal i As Integer) As String + Get + Static bGet As Boolean + bGet = False + End Get + + Set(ByVal s As String) + Static bSet As Boolean + bSet = False + End Set +End Property +", @" +internal partial class SurroundingClass +{ + private bool _Prop_bGet; + private bool _Prop_bSet; + + public string get_Prop(int i) + { + _Prop_bGet = false; + return default; + } + + public void set_Prop(int i, string value) + { + _Prop_bSet = false; + } + +}"); + } + + [Fact] + public async Task StaticLocalsInPropertyGetterAndSetterAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @" +Public Property Prop As String + Get + Static b As Boolean + b = True + End Get + + Set(ByVal s As String) + Static b As Boolean + b = False + End Set +End Property +", @" +internal partial class SurroundingClass +{ + private bool _Prop_b; + private bool _Prop_b1; + + public string Prop + { + get + { + _Prop_b = true; + return default; + } + + set + { + _Prop_b1 = false; + } + } + +}"); + } + + [Fact] + public async Task TestReadOnlyAndWriteOnlyParametrizedPropertyAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Interface IClass + ReadOnly Property ReadOnlyProp(i as Integer) As String + WriteOnly Property WriteOnlyProp(i as Integer) As String +End Interface + +Class ChildClass + Implements IClass + + Public Overridable ReadOnly Property ReadOnlyProp(i As Integer) As String Implements IClass.ReadOnlyProp + Get + Throw New NotImplementedException + End Get + End Property + + Public Overridable WriteOnly Property WriteOnlyProp(i As Integer) As String Implements IClass.WriteOnlyProp + Set + Throw New NotImplementedException + End Set + End Property +End Class +", @"using System; + +internal partial interface IClass +{ + string get_ReadOnlyProp(int i); + void set_WriteOnlyProp(int i, string value); +} + +internal partial class ChildClass : IClass +{ + + public virtual string get_ReadOnlyProp(int i) + { + throw new NotImplementedException(); + } + + public virtual void set_WriteOnlyProp(int i, string value) + { + throw new NotImplementedException(); + } +}"); + } + + [Fact] + public async Task TestPropertyStaticLocalConvertedToFieldAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Class StaticLocalConvertedToField + Readonly Property OtherName() As Integer + Get + Static sPrevPosition As Integer = 3 ' Comment moves with declaration + Console.WriteLine(sPrevPosition) + Return sPrevPosition + End Get + End Property + Readonly Property OtherName(x As Integer) as Integer + Get + Static sPrevPosition As Integer + sPrevPosition += 1 + Return sPrevPosition + End Get + End Property +End Class", @"using System; + +internal partial class StaticLocalConvertedToField +{ + private int _OtherName_sPrevPosition = 3; // Comment moves with declaration + public int OtherName + { + get + { + Console.WriteLine(_OtherName_sPrevPosition); + return _OtherName_sPrevPosition; + } + } + + private int _OtherName_sPrevPosition1 = default; + public int get_OtherName(int x) + { + _OtherName_sPrevPosition1 += 1; + return _OtherName_sPrevPosition1; + } +}"); + } +}