Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# -> VB Checked/unchecked improvements #976

Merged
merged 3 commits into from
Jan 4, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

### C# -> VB

* Return contents of checked/unchecked expressions/statements [#965](https://github.com/icsharpcode/CodeConverter/issues/965) [#975](https://github.com/icsharpcode/CodeConverter/issues/975)

## [9.1.1] - 2022-12-19

Expand Down
3 changes: 2 additions & 1 deletion CodeConverter/VB/CSToVBProjectContentsConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public async Task InitializeSourceAsync(Project project)

public async Task<SyntaxNode> SingleFirstPassAsync(Document document)
{
return await CSharpConverter.ConvertCompilationTreeAsync(document, _vbViewOfCsSymbols, _vbReferenceProject, OptionalOperations, _cancellationToken);
bool outputWillBeOverflowChecked = false; //Future: If it's a project conversion, set RemoveIntegerChecks (default true) to match CSharp's compilation option of CheckOverflow (default false)
return await CSharpConverter.ConvertCompilationTreeAsync(document, _vbViewOfCsSymbols, _vbReferenceProject, OptionalOperations, outputWillBeOverflowChecked, _cancellationToken);
}

public async Task<(Project project, List<WipFileConversion<DocumentId>> firstPassDocIds)>
Expand Down
4 changes: 2 additions & 2 deletions CodeConverter/VB/CSharpConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace ICSharpCode.CodeConverter.VB;
internal static class CSharpConverter
{
public static async Task<SyntaxNode> ConvertCompilationTreeAsync(Document document,
VisualBasicCompilation vbViewOfCsSymbols, Project vbReferenceProject, OptionalOperations optionalOperations, CancellationToken cancellationToken)
VisualBasicCompilation vbViewOfCsSymbols, Project vbReferenceProject, OptionalOperations optionalOperations, bool outputWillBeOverflowChecked, CancellationToken cancellationToken)
{
document = await document.WithExpandedRootAsync(cancellationToken);
var compilation = await document.Project.GetCompilationAsync(cancellationToken);
Expand All @@ -18,7 +18,7 @@ public static async Task<SyntaxNode> ConvertCompilationTreeAsync(Document docume
var vbSyntaxGenerator = SyntaxGenerator.GetGenerator(vbReferenceProject);
_ = tree.GetLineSpan(root.FullSpan, cancellationToken).EndLinePosition.Line;

var visualBasicSyntaxVisitor = new NodesVisitor((CS.CSharpCompilation)compilation, semanticModel, vbViewOfCsSymbols, vbSyntaxGenerator);
var visualBasicSyntaxVisitor = new NodesVisitor((CS.CSharpCompilation)compilation, semanticModel, vbViewOfCsSymbols, vbSyntaxGenerator, outputWillBeOverflowChecked);
var converted = (VBSyntax.CompilationUnitSyntax)root.Accept(visualBasicSyntaxVisitor.TriviaConvertingVisitor);

return optionalOperations.MapSourceTriviaToTargetHandled(root, converted, document);
Expand Down
4 changes: 3 additions & 1 deletion CodeConverter/VB/CommonConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ namespace ICSharpCode.CodeConverter.VB;
internal class CommonConversions
{
public SyntaxGenerator VbSyntaxGenerator { get; }
public bool OutputWillBeOverflowChecked { get; }
private readonly CommentConvertingVisitorWrapper<VisualBasicSyntaxNode> _nodesVisitor;
private readonly SemanticModel _semanticModel;

public CommonConversions(SemanticModel semanticModel, SyntaxGenerator vbSyntaxGenerator,
CommentConvertingVisitorWrapper<VisualBasicSyntaxNode> nodesVisitor)
CommentConvertingVisitorWrapper<VisualBasicSyntaxNode> nodesVisitor, bool outputWillBeOverflowChecked)
{
VbSyntaxGenerator = vbSyntaxGenerator;
OutputWillBeOverflowChecked = outputWillBeOverflowChecked;
_semanticModel = semanticModel;
_nodesVisitor = nodesVisitor;
}
Expand Down
5 changes: 4 additions & 1 deletion CodeConverter/VB/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,10 @@ public override SyntaxList<StatementSyntax> VisitEmptyStatement(CSSyntax.EmptySt

public override SyntaxList<StatementSyntax> VisitCheckedStatement(CSSyntax.CheckedStatementSyntax node)
{
return WrapInComment(ConvertBlock(node.Block), "Visual Basic does not support checked statements!");
var inputWasChecked = node.Keyword.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.CheckedKeyword);
var innerBlock = ConvertBlock(node.Block);
if (_commonConversions.OutputWillBeOverflowChecked == inputWasChecked) return innerBlock;
return WrapInComment(innerBlock, "Visual Basic does not support checked statements!");
}

private static SyntaxList<StatementSyntax> WrapInComment(SyntaxList<StatementSyntax> nodes, string comment)
Expand Down
24 changes: 21 additions & 3 deletions CodeConverter/VB/NodesVisitor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Immutable;
using System;
using System.Collections.Immutable;
using ICSharpCode.CodeConverter.Util.FromRoslyn;
using ICSharpCode.CodeConverter.VB.Trivia;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
Expand All @@ -12,6 +14,9 @@
using ExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax;
using IdentifierNameSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.IdentifierNameSyntax;
using InterpolatedStringContentSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.InterpolatedStringContentSyntax;
using InterpolationAlignmentClauseSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.InterpolationAlignmentClauseSyntax;
using InterpolationFormatClauseSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.InterpolationFormatClauseSyntax;
using MemberAccessExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.MemberAccessExpressionSyntax;
using NameSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.NameSyntax;
using OrderingSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.OrderingSyntax;
using ParameterListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ParameterListSyntax;
Expand All @@ -22,6 +27,7 @@
using SyntaxFactory = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory;
using SyntaxFacts = Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts;
using SyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind;
using TupleElementSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TupleElementSyntax;
using TypeArgumentListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeArgumentListSyntax;
using TypeParameterConstraintClauseSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeParameterConstraintClauseSyntax;
using TypeParameterListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeParameterListSyntax;
Expand All @@ -46,6 +52,7 @@ internal class NodesVisitor : CS.CSharpSyntaxVisitor<VisualBasicSyntaxNode>
private readonly HashSet<string> _addedNames = new();

private int _placeholder = 1;
private readonly bool _outputWillBeOverflowChecked;
public CommentConvertingVisitorWrapper<VisualBasicSyntaxNode> TriviaConvertingVisitor { get; }
public LanguageVersion LanguageVersion { get => _vbViewOfCsSymbols.Options.ParseOptions.LanguageVersion; }

Expand All @@ -55,14 +62,15 @@ private string GeneratePlaceholder(string v)
}

public NodesVisitor(CS.CSharpCompilation compilation, SemanticModel semanticModel,
VisualBasicCompilation vbViewOfCsSymbols, SyntaxGenerator vbSyntaxGenerator)
VisualBasicCompilation vbViewOfCsSymbols, SyntaxGenerator vbSyntaxGenerator, bool outputWillBeOverflowChecked)
{
_compilation = compilation;
_semanticModel = semanticModel;
_vbViewOfCsSymbols = vbViewOfCsSymbols;
_vbSyntaxGenerator = vbSyntaxGenerator;
_outputWillBeOverflowChecked = outputWillBeOverflowChecked;
TriviaConvertingVisitor = new CommentConvertingVisitorWrapper<VisualBasicSyntaxNode>(this);
_commonConversions = new CommonConversions(semanticModel, vbSyntaxGenerator, TriviaConvertingVisitor);
_commonConversions = new CommonConversions(semanticModel, vbSyntaxGenerator, TriviaConvertingVisitor, _outputWillBeOverflowChecked);
_cSharpHelperMethodDefinition = new CSharpHelperMethodDefinition();
}

Expand Down Expand Up @@ -891,6 +899,16 @@ public override VisualBasicSyntaxNode VisitInterpolatedStringText(CSSyntax.Inter
return SyntaxFactory.InterpolatedStringText(SyntaxFactory.InterpolatedStringTextToken(ConvertUserText(node.TextToken), node.TextToken.ValueText));
}

public override VisualBasicSyntaxNode VisitCheckedExpression(CheckedExpressionSyntax node)
{
var inputWasChecked = node.Keyword.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.CheckedKeyword);
var innerExpression = node.Expression.Accept(TriviaConvertingVisitor);
if (_outputWillBeOverflowChecked == inputWasChecked) return innerExpression;

return innerExpression.WithAdditionalAnnotations(new SyntaxAnnotation(AnnotationConstants.ConversionErrorAnnotationKind,
$"WARNING: Expression at character {node.FullSpan.Start} contained the {node.Keyword} keyword before conversion"));
}

private static string ConvertUserText(SyntaxToken token)
{
if (CS.CSharpExtensions.IsVerbatimStringLiteral(token)) return token.Text;
Expand Down
19 changes: 19 additions & 0 deletions Tests/VB/ExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ namespace ICSharpCode.CodeConverter.Tests.VB;

public class ExpressionTests : ConverterTestBase
{
[Fact]
public async Task ReturnUncheckedAsync()
{
await TestConversionCSharpToVisualBasicAsync(@"internal static class Hash
{
internal static int Combine(int newKey, int currentKey)
{
return unchecked((currentKey * (int)0xA5555529) + newKey);
}
}", @"Friend Module Hash
Friend Function Combine(ByVal newKey As Integer, ByVal currentKey As Integer) As Integer
Return currentKey * CInt(&HA5555529UI) + newKey
End Function
End Module

1 target compilation errors:
BC30439: Constant expression not representable in type 'Integer'.");
}

[Fact]
public async Task MultilineStringAsync()
{
Expand Down
32 changes: 32 additions & 0 deletions Tests/VB/SpecialConversionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ namespace ICSharpCode.CodeConverter.Tests.VB;

public class SpecialConversionTests : ConverterTestBase
{
[Fact]
public async Task UncheckedConstantAsync()
{
await TestConversionCSharpToVisualBasicAsync(
@"internal partial class TestClass
{
private const int GENERIC_READ = unchecked((int)0x80000000);
}", @"Friend Partial Class TestClass
Private Const GENERIC_READ As Integer = &H80000000UI
End Class

1 target compilation errors:
BC30439: Constant expression not representable in type 'Integer'.");
}

[Fact]
public async Task CheckedAsync()
{
await TestConversionCSharpToVisualBasicAsync(
@"using System;

internal partial class TestClass
{
private int GENERIC_READ = checked((int) Math.Pow(46341, 2));
}", @"Imports System

Friend Partial Class TestClass
Private GENERIC_READ As Integer = Math.Pow(46341, 2)
End Class
WARNING: Expression at character 85 contained the checked keyword before conversion");
}

[Fact]
public async Task TestSimpleInlineAssignAsync() {
await TestConversionCSharpToVisualBasicAsync(
Expand Down