Skip to content

Commit 1d8143a

Browse files
Merge pull request #1187 from icsharpcode/split-files
Split files
2 parents a4ccb63 + e3573e6 commit 1d8143a

20 files changed

+5556
-5376
lines changed

CodeConverter/CSharp/AccessorDeclarationNodeConverter.cs

Lines changed: 542 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
using Microsoft.VisualBasic;
2+
3+
namespace ICSharpCode.CodeConverter.CSharp;
4+
5+
internal class ArgumentConverter
6+
{
7+
public CommonConversions CommonConversions { get; }
8+
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
9+
private readonly ITypeContext _typeContext;
10+
private readonly SemanticModel _semanticModel;
11+
private CommentConvertingVisitorWrapper TriviaConvertingExpressionVisitor { get; }
12+
13+
public ArgumentConverter(VisualBasicEqualityComparison visualBasicEqualityComparison, ITypeContext typeContext, SemanticModel semanticModel, CommonConversions commonConversions)
14+
{
15+
CommonConversions = commonConversions;
16+
_visualBasicEqualityComparison = visualBasicEqualityComparison;
17+
_typeContext = typeContext;
18+
_semanticModel = semanticModel;
19+
TriviaConvertingExpressionVisitor = commonConversions.TriviaConvertingExpressionVisitor;
20+
}
21+
22+
public async Task<CSharpSyntaxNode> ConvertSimpleArgumentAsync(VBSyntax.SimpleArgumentSyntax node)
23+
{
24+
var argList = (VBasic.Syntax.ArgumentListSyntax)node.Parent;
25+
var invocation = argList.Parent;
26+
if (invocation is VBasic.Syntax.ArrayCreationExpressionSyntax)
27+
return await node.Expression.AcceptAsync<CSharpSyntaxNode>(TriviaConvertingExpressionVisitor);
28+
var symbol = GetInvocationSymbol(invocation);
29+
SyntaxToken token = default(SyntaxToken);
30+
var convertedArgExpression = (await node.Expression.AcceptAsync<CSSyntax.ExpressionSyntax>(TriviaConvertingExpressionVisitor)).SkipIntoParens();
31+
var typeConversionAnalyzer = CommonConversions.TypeConversionAnalyzer;
32+
var baseSymbol = symbol?.OriginalDefinition.GetBaseSymbol();
33+
var possibleParameters = (CommonConversions.GetCsOriginalSymbolOrNull(baseSymbol) ?? symbol)?.GetParameters();
34+
if (possibleParameters.HasValue) {
35+
var refType = _semanticModel.GetRefConversionType(node, argList, possibleParameters.Value, out var argName, out var refKind);
36+
token = CommonConversions.GetRefToken(refKind);
37+
if (refType != SemanticModelExtensions.RefConversion.Inline) {
38+
convertedArgExpression = HoistByRefDeclaration(node, convertedArgExpression, refType, argName, refKind);
39+
} else {
40+
convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression, defaultToCast: refKind != RefKind.None);
41+
}
42+
} else {
43+
convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression);
44+
}
45+
46+
var nameColon = node.IsNamed ? CS.SyntaxFactory.NameColon(await node.NameColonEquals.Name.AcceptAsync<CSSyntax.IdentifierNameSyntax>(TriviaConvertingExpressionVisitor)) : null;
47+
return CS.SyntaxFactory.Argument(nameColon, token, convertedArgExpression);
48+
}
49+
50+
public async Task<IEnumerable<CSSyntax.ArgumentSyntax>> ConvertArgumentsAsync(VBasic.Syntax.ArgumentListSyntax node)
51+
{
52+
ISymbol invocationSymbol = GetInvocationSymbol(node.Parent);
53+
var forceNamedParameters = false;
54+
var invocationHasOverloads = invocationSymbol.HasOverloads();
55+
56+
var processedParameters = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
57+
var argumentSyntaxs = (await node.Arguments.SelectAsync(ConvertArg)).Where(a => a != null);
58+
return Enumerable.Concat(argumentSyntaxs, GetAdditionalRequiredArgs(node.Arguments, processedParameters, invocationSymbol, invocationHasOverloads));
59+
60+
async Task<CSSyntax.ArgumentSyntax> ConvertArg(VBSyntax.ArgumentSyntax arg, int argIndex)
61+
{
62+
var argName = arg is VBSyntax.SimpleArgumentSyntax { IsNamed: true } namedArg ? namedArg.NameColonEquals.Name.Identifier.Text : null;
63+
var parameterSymbol = invocationSymbol?.GetParameters().GetArgument(argName, argIndex);
64+
var convertedArg = await ConvertArgForParameter(arg, parameterSymbol);
65+
66+
if (convertedArg is not null && parameterSymbol is not null) {
67+
processedParameters.Add(parameterSymbol.Name);
68+
}
69+
70+
return convertedArg;
71+
}
72+
73+
async Task<CSSyntax.ArgumentSyntax> ConvertArgForParameter(VBSyntax.ArgumentSyntax arg, IParameterSymbol parameterSymbol)
74+
{
75+
if (arg.IsOmitted) {
76+
if (invocationSymbol != null && !invocationHasOverloads) {
77+
forceNamedParameters = true;
78+
return null; //Prefer to skip omitted and use named parameters when the symbol has only one overload
79+
}
80+
return ConvertOmittedArgument(parameterSymbol);
81+
}
82+
83+
var argSyntax = await arg.AcceptAsync<CSSyntax.ArgumentSyntax>(TriviaConvertingExpressionVisitor);
84+
if (forceNamedParameters && !arg.IsNamed && parameterSymbol != null) {
85+
return argSyntax.WithNameColon(CS.SyntaxFactory.NameColon(CS.SyntaxFactory.IdentifierName(CommonConversions.CsEscapedIdentifier(parameterSymbol.Name))));
86+
}
87+
88+
return argSyntax;
89+
}
90+
91+
CSSyntax.ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter)
92+
{
93+
if (parameter == null) {
94+
return CS.SyntaxFactory.Argument(CS.SyntaxFactory.LiteralExpression(CS.SyntaxKind.DefaultLiteralExpression));
95+
}
96+
97+
var csRefKind = CommonConversions.GetCsRefKind(parameter);
98+
return csRefKind != RefKind.None
99+
? CreateOptionalRefArg(parameter, csRefKind)
100+
: CS.SyntaxFactory.Argument(CommonConversions.Literal(parameter.ExplicitDefaultValue));
101+
}
102+
}
103+
104+
public async Task<CSSyntax.AttributeArgumentSyntax> ToAttributeArgumentAsync(VBasic.Syntax.ArgumentSyntax arg)
105+
{
106+
if (!(arg is VBasic.Syntax.SimpleArgumentSyntax))
107+
throw new NotSupportedException();
108+
var a = (VBasic.Syntax.SimpleArgumentSyntax)arg;
109+
var attr = CS.SyntaxFactory.AttributeArgument(await a.Expression.AcceptAsync<CSSyntax.ExpressionSyntax>(TriviaConvertingExpressionVisitor));
110+
if (a.IsNamed) {
111+
attr = attr.WithNameEquals(CS.SyntaxFactory.NameEquals(await a.NameColonEquals.Name.AcceptAsync<CSSyntax.IdentifierNameSyntax>(TriviaConvertingExpressionVisitor)));
112+
}
113+
return attr;
114+
}
115+
116+
public async Task<CSSyntax.ArgumentListSyntax> ConvertArgumentListOrEmptyAsync(SyntaxNode node, VBSyntax.ArgumentListSyntax argumentList)
117+
{
118+
return await argumentList.AcceptAsync<CSSyntax.ArgumentListSyntax>(TriviaConvertingExpressionVisitor) ?? CreateArgList(_semanticModel.GetSymbolInfo(node).Symbol);
119+
}
120+
121+
122+
private CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.SimpleArgumentSyntax node, CSSyntax.ExpressionSyntax refLValue, SemanticModelExtensions.RefConversion refType, string argName, RefKind refKind)
123+
{
124+
string prefix = $"arg{argName}";
125+
var expressionTypeInfo = _semanticModel.GetTypeInfo(node.Expression);
126+
bool useVar = expressionTypeInfo.Type?.Equals(expressionTypeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability) == true && !CommonConversions.ShouldPreferExplicitType(node.Expression, expressionTypeInfo.ConvertedType, out var _);
127+
var typeSyntax = CommonConversions.GetTypeSyntax(expressionTypeInfo.ConvertedType, useVar);
128+
129+
if (refLValue is CSSyntax.ElementAccessExpressionSyntax eae) {
130+
//Hoist out the container so we can assign back to the same one after (like VB does)
131+
var tmpContainer = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration("tmp", eae.Expression, ValidSyntaxFactory.VarType));
132+
refLValue = eae.WithExpression(tmpContainer.IdentifierName);
133+
}
134+
135+
var withCast = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, refLValue, defaultToCast: refKind != RefKind.None);
136+
137+
var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, withCast, typeSyntax));
138+
139+
if (refType == SemanticModelExtensions.RefConversion.PreAndPostAssignment) {
140+
var convertedLocalIdentifier = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, local.IdentifierName, forceSourceType: expressionTypeInfo.ConvertedType, forceTargetType: expressionTypeInfo.Type);
141+
_typeContext.PerScopeState.Hoist(new AdditionalAssignment(refLValue, convertedLocalIdentifier));
142+
}
143+
144+
return local.IdentifierName;
145+
}
146+
147+
private ISymbol GetInvocationSymbol(SyntaxNode invocation)
148+
{
149+
var symbol = invocation.TypeSwitch(
150+
(VBSyntax.InvocationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
151+
(VBSyntax.ObjectCreationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
152+
(VBSyntax.RaiseEventStatementSyntax e) => _semanticModel.GetSymbolInfo(e.Name).ExtractBestMatch<ISymbol>(),
153+
(VBSyntax.MidExpressionSyntax _) => CommonConversions.KnownTypes.VbCompilerStringType?.GetMembers("MidStmtStr").FirstOrDefault(),
154+
_ => throw new NotSupportedException());
155+
return symbol;
156+
}
157+
158+
private IEnumerable<CSSyntax.ArgumentSyntax> GetAdditionalRequiredArgs(
159+
IEnumerable<VBSyntax.ArgumentSyntax> arguments,
160+
ISymbol invocationSymbol)
161+
{
162+
var invocationHasOverloads = invocationSymbol.HasOverloads();
163+
return GetAdditionalRequiredArgs(arguments, processedParametersNames: null, invocationSymbol, invocationHasOverloads);
164+
}
165+
166+
private IEnumerable<CSSyntax.ArgumentSyntax> GetAdditionalRequiredArgs(
167+
IEnumerable<VBSyntax.ArgumentSyntax> arguments,
168+
ICollection<string> processedParametersNames,
169+
ISymbol invocationSymbol,
170+
bool invocationHasOverloads)
171+
{
172+
if (invocationSymbol is null) {
173+
yield break;
174+
}
175+
176+
var invocationHasOmittedArgs = arguments.Any(t => t.IsOmitted);
177+
var expandOptionalArgs = invocationHasOmittedArgs && invocationHasOverloads;
178+
var missingArgs = invocationSymbol.GetParameters().Where(t => processedParametersNames is null || !processedParametersNames.Contains(t.Name));
179+
var requiresCompareMethod = _visualBasicEqualityComparison.OptionCompareTextCaseInsensitive && RequiresStringCompareMethodToBeAppended(invocationSymbol);
180+
181+
foreach (var parameterSymbol in missingArgs) {
182+
var extraArg = CreateExtraArgOrNull(parameterSymbol, requiresCompareMethod, expandOptionalArgs);
183+
if (extraArg != null) {
184+
yield return extraArg;
185+
}
186+
}
187+
}
188+
189+
190+
private static bool RequiresStringCompareMethodToBeAppended(ISymbol symbol) =>
191+
symbol?.ContainingType.Name == nameof(Strings) &&
192+
symbol.ContainingType.ContainingNamespace.Name == nameof(Microsoft.VisualBasic) &&
193+
symbol.ContainingType.ContainingNamespace.ContainingNamespace.Name == nameof(Microsoft) &&
194+
symbol.Name is "InStr" or "InStrRev" or "Replace" or "Split" or "StrComp";
195+
196+
private CSSyntax.ArgumentSyntax CreateExtraArgOrNull(IParameterSymbol p, bool requiresCompareMethod, bool expandOptionalArgs)
197+
{
198+
var csRefKind = CommonConversions.GetCsRefKind(p);
199+
if (csRefKind != RefKind.None) {
200+
return CreateOptionalRefArg(p, csRefKind);
201+
}
202+
203+
if (requiresCompareMethod && p.Type.GetFullMetadataName() == "Microsoft.VisualBasic.CompareMethod") {
204+
return (CSSyntax.ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, _visualBasicEqualityComparison.CompareMethodExpression);
205+
}
206+
207+
if (expandOptionalArgs && p.HasExplicitDefaultValue) {
208+
return (CSSyntax.ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, RefKind.None, CommonConversions.Literal(p.ExplicitDefaultValue));
209+
}
210+
211+
return null;
212+
}
213+
214+
private CSSyntax.ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind refKind)
215+
{
216+
string prefix = $"arg{p.Name}";
217+
var type = CommonConversions.GetTypeSyntax(p.Type);
218+
CSSyntax.ExpressionSyntax initializer;
219+
if (p.HasExplicitDefaultValue) {
220+
initializer = CommonConversions.Literal(p.ExplicitDefaultValue);
221+
} else if (HasOptionalAttribute(p)) {
222+
if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)) {
223+
initializer = CommonConversions.Literal(defaultValue);
224+
} else {
225+
initializer = CS.SyntaxFactory.DefaultExpression(type);
226+
}
227+
} else {
228+
//invalid VB.NET code
229+
return null;
230+
}
231+
var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, initializer, type));
232+
return (CSSyntax.ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.Name, refKind, local.IdentifierName);
233+
234+
bool HasOptionalAttribute(IParameterSymbol p)
235+
{
236+
var optionalAttribute = CommonConversions.KnownTypes.OptionalAttribute;
237+
if (optionalAttribute == null) {
238+
return false;
239+
}
240+
241+
return p.GetAttributes().Any(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, optionalAttribute));
242+
}
243+
244+
bool TryGetDefaultParameterValueAttributeValue(IParameterSymbol p, out object defaultValue)
245+
{
246+
defaultValue = null;
247+
248+
var defaultParameterValueAttribute = CommonConversions.KnownTypes.DefaultParameterValueAttribute;
249+
if (defaultParameterValueAttribute == null) {
250+
return false;
251+
}
252+
253+
var attributeData = p.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.IncludeNullability.Equals(a.AttributeClass, defaultParameterValueAttribute));
254+
if (attributeData == null) {
255+
return false;
256+
}
257+
258+
if (attributeData.ConstructorArguments.Length == 0) {
259+
return false;
260+
}
261+
262+
defaultValue = attributeData.ConstructorArguments.First().Value;
263+
return true;
264+
}
265+
}
266+
267+
public CSSyntax.ArgumentListSyntax CreateArgList(ISymbol invocationSymbol)
268+
{
269+
return CS.SyntaxFactory.ArgumentList(CS.SyntaxFactory.SeparatedList(
270+
GetAdditionalRequiredArgs(Array.Empty<VBSyntax.ArgumentSyntax>(), invocationSymbol))
271+
);
272+
}
273+
}

0 commit comments

Comments
 (0)