@@ -35,7 +35,6 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
3535 private readonly QueryConverter _queryConverter ;
3636 private readonly Lazy < IReadOnlyDictionary < ITypeSymbol , string > > _convertMethodsLookupByReturnType ;
3737 private readonly LambdaConverter _lambdaConverter ;
38- private readonly INamedTypeSymbol _vbBooleanTypeSymbol ;
3938 private readonly VisualBasicNullableExpressionsConverter _visualBasicNullableTypesConverter ;
4039 private readonly Dictionary < string , Stack < ( SyntaxNode Scope , string TempName ) > > _tempNameForAnonymousScope = new ( ) ;
4140 private readonly HashSet < string > _generatedNames = new ( StringComparer . OrdinalIgnoreCase ) ;
@@ -58,7 +57,6 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
5857 // If this isn't needed, the assembly with Conversions may not be referenced, so this must be done lazily
5958 _convertMethodsLookupByReturnType =
6059 new Lazy < IReadOnlyDictionary < ITypeSymbol , string > > ( ( ) => CreateConvertMethodsLookupByReturnType ( semanticModel ) ) ;
61- _vbBooleanTypeSymbol = _semanticModel . Compilation . GetTypeByMetadataName ( "System.Boolean" ) ;
6260 }
6361
6462 private static IReadOnlyDictionary < ITypeSymbol , string > CreateConvertMethodsLookupByReturnType (
@@ -775,7 +773,7 @@ public override async Task<CSharpSyntaxNode> VisitBinaryConditionalExpression(VB
775773 public override async Task < CSharpSyntaxNode > VisitTernaryConditionalExpression ( VBasic . Syntax . TernaryConditionalExpressionSyntax node )
776774 {
777775 var condition = await node . Condition . AcceptAsync < ExpressionSyntax > ( TriviaConvertingExpressionVisitor ) ;
778- condition = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Condition , condition , forceTargetType : _vbBooleanTypeSymbol ) ;
776+ condition = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Condition , condition , forceTargetType : CommonConversions . KnownTypes . Boolean ) ;
779777
780778 var whenTrue = await node . WhenTrue . AcceptAsync < ExpressionSyntax > ( TriviaConvertingExpressionVisitor ) ;
781779 whenTrue = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . WhenTrue , whenTrue ) ;
@@ -900,7 +898,7 @@ private async Task<CSharpSyntaxNode> ConvertBinaryExpressionAsync(VBasic.Syntax.
900898 omitConversion = lhsTypeInfo . Type . SpecialType == SpecialType . System_String ||
901899 rhsTypeInfo . Type . SpecialType == SpecialType . System_String ;
902900 if ( lhsTypeInfo . ConvertedType . SpecialType != SpecialType . System_String ) {
903- forceLhsTargetType = _semanticModel . Compilation . GetTypeByMetadataName ( "System. String" ) ;
901+ forceLhsTargetType = CommonConversions . KnownTypes . String ;
904902 }
905903 }
906904 }
@@ -938,6 +936,8 @@ private async Task<CSharpSyntaxNode> ConvertBinaryExpressionAsync(VBasic.Syntax.
938936 return node . Parent . IsKind ( VBasic . SyntaxKind . SimpleArgument ) ? exp : exp . AddParens ( ) ;
939937 }
940938
939+
940+
941941 private async Task < ExpressionSyntax > RewriteBinaryOperatorOrNullAsync ( VBSyntax . BinaryExpressionSyntax node ) =>
942942 await _operatorConverter . ConvertRewrittenBinaryOperatorOrNullAsync ( node , TriviaConvertingExpressionVisitor . IsWithinQuery ) ;
943943
@@ -1731,17 +1731,22 @@ async Task<ArgumentSyntax> ConvertArg(VBSyntax.ArgumentSyntax arg, int argIndex)
17311731 {
17321732 var argName = arg is VBSyntax . SimpleArgumentSyntax { IsNamed : true } namedArg ? namedArg . NameColonEquals . Name . Identifier . Text : null ;
17331733 var parameterSymbol = invocationSymbol ? . GetParameters ( ) . GetArgument ( argName , argIndex ) ;
1734+ var convertedArg = await ConvertArgForParameter ( arg , parameterSymbol ) ;
17341735
1735- if ( parameterSymbol != null ) {
1736+ if ( convertedArg is not null && parameterSymbol is not null ) {
17361737 processedParameters . Add ( parameterSymbol . Name ) ;
17371738 }
17381739
1740+ return convertedArg ;
1741+ }
1742+
1743+ async Task < ArgumentSyntax > ConvertArgForParameter ( VBSyntax . ArgumentSyntax arg , IParameterSymbol parameterSymbol )
1744+ {
17391745 if ( arg . IsOmitted ) {
17401746 if ( invocationSymbol != null && ! invocationHasOverloads ) {
17411747 forceNamedParameters = true ;
17421748 return null ; //Prefer to skip omitted and use named parameters when the symbol has only one overload
17431749 }
1744-
17451750 return ConvertOmittedArgument ( parameterSymbol ) ;
17461751 }
17471752
@@ -1818,8 +1823,54 @@ private ArgumentSyntax CreateExtraArgOrNull(IParameterSymbol p, bool requiresCom
18181823 private ArgumentSyntax CreateOptionalRefArg ( IParameterSymbol p , RefKind refKind )
18191824 {
18201825 string prefix = $ "arg{ p . Name } ";
1821- var local = _typeContext . PerScopeState . Hoist ( new AdditionalDeclaration ( prefix , CommonConversions . Literal ( p . ExplicitDefaultValue ) , CommonConversions . GetTypeSyntax ( p . Type ) ) ) ;
1826+ var type = CommonConversions . GetTypeSyntax ( p . Type ) ;
1827+ ExpressionSyntax initializer ;
1828+ if ( p . HasExplicitDefaultValue ) {
1829+ initializer = CommonConversions . Literal ( p . ExplicitDefaultValue ) ;
1830+ } else if ( HasOptionalAttribute ( p ) ) {
1831+ if ( TryGetDefaultParameterValueAttributeValue ( p , out var defaultValue ) ) {
1832+ initializer = CommonConversions . Literal ( defaultValue ) ;
1833+ } else {
1834+ initializer = SyntaxFactory . DefaultExpression ( type ) ;
1835+ }
1836+ } else {
1837+ //invalid VB.NET code
1838+ return null ;
1839+ }
1840+ var local = _typeContext . PerScopeState . Hoist ( new AdditionalDeclaration ( prefix , initializer , type ) ) ;
18221841 return ( ArgumentSyntax ) CommonConversions . CsSyntaxGenerator . Argument ( p . Name , refKind , local . IdentifierName ) ;
1842+
1843+ bool HasOptionalAttribute ( IParameterSymbol p )
1844+ {
1845+ var optionalAttribute = CommonConversions . KnownTypes . OptionalAttribute ;
1846+ if ( optionalAttribute == null ) {
1847+ return false ;
1848+ }
1849+
1850+ return p . GetAttributes ( ) . Any ( a => SymbolEqualityComparer . IncludeNullability . Equals ( a . AttributeClass , optionalAttribute ) ) ;
1851+ }
1852+
1853+ bool TryGetDefaultParameterValueAttributeValue ( IParameterSymbol p , out object defaultValue )
1854+ {
1855+ defaultValue = null ;
1856+
1857+ var defaultParameterValueAttribute = CommonConversions . KnownTypes . DefaultParameterValueAttribute ;
1858+ if ( defaultParameterValueAttribute == null ) {
1859+ return false ;
1860+ }
1861+
1862+ var attributeData = p . GetAttributes ( ) . FirstOrDefault ( a => SymbolEqualityComparer . IncludeNullability . Equals ( a . AttributeClass , defaultParameterValueAttribute ) ) ;
1863+ if ( attributeData == null ) {
1864+ return false ;
1865+ }
1866+
1867+ if ( attributeData . ConstructorArguments . Length == 0 ) {
1868+ return false ;
1869+ }
1870+
1871+ defaultValue = attributeData . ConstructorArguments . First ( ) . Value ;
1872+ return true ;
1873+ }
18231874 }
18241875
18251876 private RefConversion NeedsVariableForArgument ( VBasic . Syntax . ArgumentSyntax node , RefKind refKind )
@@ -1833,9 +1884,14 @@ private RefConversion NeedsVariableForArgument(VBasic.Syntax.ArgumentSyntax node
18331884 RefConversion GetRefConversion ( VBSyntax . ExpressionSyntax expression )
18341885 {
18351886 var symbolInfo = GetSymbolInfoInDocument < ISymbol > ( expression ) ;
1836- if ( symbolInfo is IPropertySymbol propertySymbol ) {
1887+ if ( symbolInfo is IPropertySymbol propertySymbol
1888+ // a property in VB.NET code can be ReturnsByRef if it's defined in a C# assembly the VB.NET code references
1889+ && ! propertySymbol . ReturnsByRef && ! propertySymbol . ReturnsByRefReadonly ) {
18371890 return propertySymbol . IsReadOnly ? RefConversion . PreAssigment : RefConversion . PreAndPostAssignment ;
18381891 }
1892+ else if ( symbolInfo is IFieldSymbol { IsConst : true } or ILocalSymbol { IsConst : true } ) {
1893+ return RefConversion . PreAssigment ;
1894+ }
18391895
18401896 if ( DeclaredInUsing ( symbolInfo ) ) return RefConversion . PreAssigment ;
18411897
@@ -1858,7 +1914,16 @@ RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
18581914 bool IsRefArrayAcces ( VBSyntax . ExpressionSyntax expression )
18591915 {
18601916 if ( ! ( expression is VBSyntax . InvocationExpressionSyntax ies ) ) return false ;
1861- return _semanticModel . GetOperation ( ies ) . IsArrayElementAccess ( ) && GetRefConversion ( ies . Expression ) == RefConversion . Inline ;
1917+ var op = _semanticModel . GetOperation ( ies ) ;
1918+ return ( op . IsArrayElementAccess ( ) || IsReturnsByRefPropertyElementAccess ( op ) )
1919+ && GetRefConversion ( ies . Expression ) == RefConversion . Inline ;
1920+
1921+ static bool IsReturnsByRefPropertyElementAccess ( IOperation op )
1922+ {
1923+ return op . IsPropertyElementAccess ( )
1924+ && op is IPropertyReferenceOperation { Property : { } prop }
1925+ && ( prop . ReturnsByRef || prop . ReturnsByRefReadonly ) ;
1926+ }
18621927 }
18631928 }
18641929
@@ -1895,7 +1960,7 @@ private ISymbol GetInvocationSymbol(SyntaxNode invocation)
18951960 ( VBSyntax . InvocationExpressionSyntax e ) => _semanticModel . GetSymbolInfo ( e ) . ExtractBestMatch < ISymbol > ( ) ,
18961961 ( VBSyntax . ObjectCreationExpressionSyntax e ) => _semanticModel . GetSymbolInfo ( e ) . ExtractBestMatch < ISymbol > ( ) ,
18971962 ( VBSyntax . RaiseEventStatementSyntax e ) => _semanticModel . GetSymbolInfo ( e . Name ) . ExtractBestMatch < ISymbol > ( ) ,
1898- ( VBSyntax . MidExpressionSyntax _ ) => _semanticModel . Compilation . GetTypeByMetadataName ( "Microsoft.VisualBasic.CompilerServices.StringType" ) ? . GetMembers ( "MidStmtStr" ) . FirstOrDefault ( ) ,
1963+ ( VBSyntax . MidExpressionSyntax _ ) => CommonConversions . KnownTypes . VbCompilerStringType ? . GetMembers ( "MidStmtStr" ) . FirstOrDefault ( ) ,
18991964 _ => throw new NotSupportedException ( ) ) ;
19001965 return symbol ;
19011966 }
0 commit comments