Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CodeConverter/CSharp/DeclarationNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ public override async Task<CSharpSyntaxNode> VisitMethodBlock(VBSyntax.MethodBlo
var methodBlock = await node.SubOrFunctionStatement.AcceptAsync<BaseMethodDeclarationSyntax>(TriviaConvertingDeclarationVisitor, SourceTriviaMapKind.SubNodesOnly);

var declaredSymbol = ModelExtensions.GetDeclaredSymbol(_semanticModel, node);
if (!declaredSymbol.CanHaveMethodBody()) {
if (declaredSymbol?.CanHaveMethodBody() == false) {
return methodBlock;
}
var csReturnVariableOrNull = CommonConversions.GetRetVariableNameOrNull(node);
Expand Down Expand Up @@ -786,7 +786,8 @@ public override async Task<CSharpSyntaxNode> VisitMethodStatement(VBSyntax.Metho
null
);

return hasBody && declaredSymbol.CanHaveMethodBody() ? decl : decl.WithSemicolonToken(SemicolonToken);
bool canHaveMethodBody = declaredSymbol?.CanHaveMethodBody() != false;
return hasBody && canHaveMethodBody ? decl : decl.WithSemicolonToken(SemicolonToken);
}

public override async Task<CSharpSyntaxNode> VisitEventBlock(VBSyntax.EventBlockSyntax node)
Expand Down
47 changes: 19 additions & 28 deletions CodeConverter/Util/ISymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
namespace ICSharpCode.CodeConverter.Util;

internal static class ISymbolExtensions
Expand All @@ -18,12 +19,9 @@ public static ISymbol GetBaseSymbol(this ISymbol symbol, Func<ISymbol, bool> sel
: symbol;
}

public static bool IsDefinedInSource(this ISymbol symbol)
{
return symbol.Locations.Any(loc => loc.IsInSource);
}
public static bool IsDefinedInSource(this ISymbol symbol) => symbol.Locations.Any(loc => loc.IsInSource);

public static TSymbol ExtractBestMatch<TSymbol>(this SymbolInfo info, Func<TSymbol, bool> isMatch = null) where TSymbol : class, ISymbol
public static TSymbol? ExtractBestMatch<TSymbol>(this SymbolInfo info, Func<TSymbol, bool>? isMatch = null) where TSymbol : class, ISymbol
{
isMatch ??= (_ => true);
if (info.Symbol == null && info.CandidateSymbols.Length == 0)
Expand All @@ -38,16 +36,16 @@ public static TSymbol ExtractBestMatch<TSymbol>(this SymbolInfo info, Func<TSymb
return null;
}

public static string ToCSharpDisplayString(this ISymbol symbol, SymbolDisplayFormat format = null)
public static string? ToCSharpDisplayString(this ISymbol symbol, SymbolDisplayFormat? format = null)
{
if (TryGetSpecialVBTypeConversion(symbol, out var cSharpDisplayString)) return cSharpDisplayString;

return symbol.ToDisplayString(format);
}

private static bool TryGetSpecialVBTypeConversion(ISymbol symbol, out string cSharpDisplayString)
private static bool TryGetSpecialVBTypeConversion(ISymbol symbol, out string? cSharpDisplayString)
{
var containingNamespace = symbol?.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat);
var containingNamespace = symbol.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat);
if (containingNamespace == "Microsoft.VisualBasic" || containingNamespace == "System") {
if (symbol is ITypeSymbol && TypesToConvertToDateTime.Contains(symbol.Name)) {
{
Expand All @@ -68,35 +66,28 @@ private static bool TryGetSpecialVBTypeConversion(ISymbol symbol, out string cSh
return false;
}

public static bool IsPartialMethodImplementation(this ISymbol declaredSymbol)
{
return declaredSymbol is IMethodSymbol ms && ms.PartialDefinitionPart != null;
}
public static bool IsPartialMethodImplementation(this ISymbol? declaredSymbol) =>
declaredSymbol is IMethodSymbol {PartialDefinitionPart: not null};

public static bool CanHaveMethodBody(this ISymbol declaredSymbol)
{
return !(declaredSymbol is IMethodSymbol ms) || ms.PartialImplementationPart == null && !ms.IsExtern;
}
public static bool CanHaveMethodBody(this ISymbol? declaredSymbol) =>
declaredSymbol is IMethodSymbol {IsExtern: false} && !IsPartialMethodDefinition(declaredSymbol);

public static bool IsPartialMethodDefinition(this ISymbol declaredSymbol)
{
return declaredSymbol is IMethodSymbol ms && ms.PartialImplementationPart != null;
}
public static bool IsPartialMethodDefinition(this ISymbol? declaredSymbol) =>
declaredSymbol is IMethodSymbol {PartialImplementationPart: not null}
or IMethodSymbol {IsPartialDefinition: true};

public static bool IsPartialClassDefinition(this ISymbol declaredSymbol)
public static bool IsPartialClassDefinition(this ISymbol? declaredSymbol)
{
return declaredSymbol is ITypeSymbol ts && (ts.DeclaringSyntaxReferences.Length > 1
|| ts.ContainingAssembly.Name == ForcePartialTypesAssemblyName);
return declaredSymbol is ITypeSymbol {DeclaringSyntaxReferences.Length: > 1}
or ITypeSymbol {ContainingAssembly.Name: ForcePartialTypesAssemblyName};
}

public static bool IsReducedTypeParameterMethod(this ISymbol symbol)
{
return symbol is IMethodSymbol ms && ms.ReducedFrom?.TypeParameters.Length > ms.TypeParameters.Length;
}
public static bool IsReducedTypeParameterMethod(this ISymbol? symbol) =>
symbol is IMethodSymbol ms && ms.ReducedFrom?.TypeParameters.Length > ms.TypeParameters.Length;

/// <summary>
/// Since non value types can't be ref types for extension methods in C#, convert to a static invocation
/// https://github.com/icsharpcode/CodeConverter/issues/785
/// </summary>
public static bool ValidCSharpExtensionMethodParameter(this IParameterSymbol vbSymbol) => vbSymbol != null && (vbSymbol.RefKind != RefKind.Ref || vbSymbol.Type.IsValueType);
public static bool ValidCSharpExtensionMethodParameter(this IParameterSymbol? vbSymbol) => vbSymbol != null && (vbSymbol.RefKind != RefKind.Ref || vbSymbol.Type.IsValueType);
}
7 changes: 7 additions & 0 deletions Tests/CSharp/MemberTests/MemberTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,13 @@ public partial class TestClass // VB doesn't require partial here (when just a s
}");
}

[Fact]
public async Task Issue1097_PartialMethodAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Partial Private Sub DummyMethod()
End Sub", @"partial void DummyMethod();");
}

[Fact]
public async Task NestedClassAsync()
{
Expand Down
Loading