Skip to content

Commit 3e22ad6

Browse files
If no project file: Hint at installing the VisualBasic nuget package
(for the relevant using statements) - Relates to #388
1 parent 79b9bdb commit 3e22ad6

18 files changed

+69
-59
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ All notable changes to the code converter will be documented in this file.
33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
44

55
## [Unreleased]
6-
6+
* Optimize away some redundant casts and conversions with strings/chars - [#388](https://github.com/icsharpcode/CodeConverter/issues/388)
77

88
### Vsix
99

CodeConverter/CSharp/VBToCSConversion.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using ICSharpCode.CodeConverter.Shared;
55
using ICSharpCode.CodeConverter.Util;
66
using Microsoft.CodeAnalysis;
7+
using CS = Microsoft.CodeAnalysis.CSharp;
78
using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax;
89
using SyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind;
910
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
@@ -21,7 +22,7 @@ namespace ICSharpCode.CodeConverter.CSharp
2122
public class VBToCSConversion : ILanguageConversion
2223
{
2324
private const string UnresolvedNamespaceDiagnosticId = "CS0246";
24-
25+
private const string FabricatedAssemblyName = ISymbolExtensions.ForcePartialTypesAssemblyName;
2526
private VBToCSProjectContentsConverter _vbToCsProjectContentsConverter;
2627
private IProgress<ConversionProgress> _progress;
2728
private CancellationToken _cancellationToken;
@@ -151,7 +152,16 @@ public List<SyntaxNode> FindSingleImportantChild(SyntaxNode annotatedNode)
151152

152153
public async Task<Document> SingleSecondPassAsync(Document doc)
153154
{
154-
return await doc.SimplifyStatementsAsync<CSSyntax.UsingDirectiveSyntax, CSSyntax.ExpressionSyntax>(UnresolvedNamespaceDiagnosticId, _cancellationToken);
155+
var simplifiedDocument = await doc.SimplifyStatementsAsync<CSSyntax.UsingDirectiveSyntax, CSSyntax.ExpressionSyntax>(UnresolvedNamespaceDiagnosticId, _cancellationToken);
156+
157+
// Can't add a reference to Microsoft.VisualBasic if there's no project file, so hint to install the package
158+
if (_vbToCsProjectContentsConverter.SourceProject.AssemblyName == FabricatedAssemblyName) {
159+
var simpleRoot = await simplifiedDocument.GetSyntaxRootAsync();
160+
var vbUsings = simpleRoot.ChildNodes().OfType<CSSyntax.UsingDirectiveSyntax>().Where(u => u.Name.ToString().Contains("Microsoft.VisualBasic"));
161+
var commentedRoot = simpleRoot.ReplaceNodes(vbUsings, (_, r) => r.WithTrailingTrivia(CS.SyntaxFactory.Comment(" // Install-Package Microsoft.VisualBasic").Yield().Concat(r.GetTrailingTrivia())));
162+
simplifiedDocument = simplifiedDocument.WithSyntaxRoot(commentedRoot);
163+
}
164+
return simplifiedDocument;
155165
}
156166

157167
public SyntaxTree CreateTree(string text)
@@ -173,7 +183,7 @@ public async Task<Document> CreateProjectDocumentFromTreeAsync(SyntaxTree tree,
173183
private async Task<Project> CreateEmptyVbProjectAsync(IEnumerable<MetadataReference> references)
174184
{
175185
return await VisualBasicCompiler.CreateCompilationOptions(ConversionOptions.RootNamespaceOverride)
176-
.CreateProjectAsync(references, VisualBasicParseOptions.Default, ISymbolExtensions.ForcePartialTypesAssemblyName);
186+
.CreateProjectAsync(references, VisualBasicParseOptions.Default, FabricatedAssemblyName);
177187
}
178188
}
179189
}

CodeConverter/CSharp/VBToCSProjectContentsConverter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public VBToCSProjectContentsConverter(ConversionOptions conversionOptions, IProg
3737
}
3838

3939
public string RootNamespace => _conversionOptions.RootNamespaceOverride ??
40-
((VisualBasicCompilationOptions)Project.CompilationOptions).RootNamespace;
40+
((VisualBasicCompilationOptions)SourceProject.CompilationOptions).RootNamespace;
4141

4242
public async Task InitializeSourceAsync(Project project)
4343
{
@@ -46,13 +46,13 @@ public async Task InitializeSourceAsync(Project project)
4646
_csharpReferenceProject = project.CreateReferenceOnlyProjectFromAnyOptions(cSharpCompilationOptions, CSharpCompiler.ParseOptions);
4747
_csharpViewOfVbSymbols = (CSharpCompilation) await _csharpReferenceProject.GetCompilationAsync(_cancellationToken);
4848
_designerToResxRelativePath = project.ReadVbEmbeddedResources().ToDictionary(r => r.LastGenOutput, r => r.RelativePath);
49-
Project = await project.WithAdditionalDocs(_designerToResxRelativePath.Values)
49+
SourceProject = await project.WithAdditionalDocs(_designerToResxRelativePath.Values)
5050
.WithRenamedMergedMyNamespaceAsync(_cancellationToken);
5151
}
5252

5353
public string LanguageVersion { get { return LangVersion.Latest.ToDisplayString(); } }
5454

55-
public Project Project { get; private set; }
55+
public Project SourceProject { get; private set; }
5656

5757
public async Task<SyntaxNode> SingleFirstPassAsync(Document document)
5858
{
@@ -61,7 +61,7 @@ public async Task<SyntaxNode> SingleFirstPassAsync(Document document)
6161

6262
public async Task<(Project project, List<WipFileConversion<DocumentId>> firstPassDocIds)> GetConvertedProjectAsync(WipFileConversion<SyntaxNode>[] firstPassResults)
6363
{
64-
var projDirPath = Project.GetDirectoryPath();
64+
var projDirPath = SourceProject.GetDirectoryPath();
6565
var (project, docIds) = _convertedCsProject.WithDocuments(firstPassResults.Select(r => r.WithTargetPath(GetTargetPath(projDirPath, r))).ToArray());
6666
return (await project.RenameMergedNamespacesAsync(_cancellationToken), docIds);
6767
}
@@ -78,7 +78,7 @@ private static string GetPathRelativeToProject(string projDirPath, string p)
7878

7979
public async IAsyncEnumerable<ConversionResult> GetAdditionalConversionResults(IReadOnlyCollection<TextDocument> additionalDocumentsToConvert, [EnumeratorCancellation] CancellationToken cancellationToken)
8080
{
81-
string projDirPath = Project.GetDirectoryPath();
81+
string projDirPath = SourceProject.GetDirectoryPath();
8282
foreach (var doc in additionalDocumentsToConvert) {
8383
string newPath = Path.Combine(projDirPath, Path.GetFileName(doc.FilePath));
8484
if (newPath != doc.FilePath) {

CodeConverter/Shared/IProjectContentsConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface IProjectContentsConverter
1111
Task InitializeSourceAsync(Project project);
1212
string LanguageVersion { get; }
1313
string RootNamespace { get; }
14-
Project Project { get; }
14+
Project SourceProject { get; }
1515
Task<SyntaxNode> SingleFirstPassAsync(Document document);
1616
Task<(Project project, List<WipFileConversion<DocumentId>> firstPassDocIds)> GetConvertedProjectAsync(WipFileConversion<SyntaxNode>[] firstPassResults);
1717
public IAsyncEnumerable<ConversionResult> GetAdditionalConversionResults(IReadOnlyCollection<TextDocument> additionalDocumentsToConvert, CancellationToken cancellationToken);

CodeConverter/Shared/ProjectConversion.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private ProjectConversion(IProjectContentsConverter projectContentsConverter, IE
6565

6666
var projectContentsConverter = await languageConversion.CreateProjectContentsConverterAsync(document.Project, progress, cancellationToken);
6767

68-
document = projectContentsConverter.Project.GetDocument(document.Id);
68+
document = projectContentsConverter.SourceProject.GetDocument(document.Id);
6969

7070
var conversion = new ProjectConversion(projectContentsConverter, new[] { document }, Enumerable.Empty<TextDocument>(), languageConversion, cancellationToken, conversionOptions.ShowCompilationErrors, returnSelectedNode);
7171
var conversionResults = await conversion.Convert(progress).ToArrayAsync();
@@ -87,8 +87,8 @@ public static async IAsyncEnumerable<ConversionResult> ConvertProject(Project pr
8787
using var roslynEntryPoint = await RoslynEntryPointAsync(progress);
8888

8989
var projectContentsConverter = await languageConversion.CreateProjectContentsConverterAsync(project, progress, cancellationToken);
90-
var sourceFilePaths = project.Documents.Concat(projectContentsConverter.Project.AdditionalDocuments).Select(d => d.FilePath).ToImmutableHashSet();
91-
project = projectContentsConverter.Project;
90+
var sourceFilePaths = project.Documents.Concat(projectContentsConverter.SourceProject.AdditionalDocuments).Select(d => d.FilePath).ToImmutableHashSet();
91+
project = projectContentsConverter.SourceProject;
9292
var convertProjectContents = ConvertProjectContents(projectContentsConverter, languageConversion, progress, cancellationToken);
9393

9494
var results = WithProjectFile(projectContentsConverter, languageConversion, sourceFilePaths, convertProjectContents, replacements);
@@ -98,7 +98,7 @@ public static async IAsyncEnumerable<ConversionResult> ConvertProject(Project pr
9898
/// <remarks>Perf: Keep lazy so that we don't keep an extra copy of all files in memory at once</remarks>
9999
private static async IAsyncEnumerable<ConversionResult> WithProjectFile(IProjectContentsConverter projectContentsConverter, ILanguageConversion languageConversion, ImmutableHashSet<string> originalSourcePaths, IAsyncEnumerable<ConversionResult> convertProjectContents, (string Find, string Replace, bool FirstOnly)[] replacements)
100100
{
101-
var project = projectContentsConverter.Project;
101+
var project = projectContentsConverter.SourceProject;
102102
var projectDir = project.GetDirectoryPath();
103103
var addedTargetFiles = new List<string>();
104104
var sourceToTargetMap = new List<(string, string)>();
@@ -161,14 +161,14 @@ private static async IAsyncEnumerable<ConversionResult> ConvertProjectContents(
161161
IProjectContentsConverter projectContentsConverter, ILanguageConversion languageConversion,
162162
IProgress<ConversionProgress> progress, [EnumeratorCancellation] CancellationToken cancellationToken)
163163
{
164-
var documentsWithLengths = await projectContentsConverter.Project.Documents
164+
var documentsWithLengths = await projectContentsConverter.SourceProject.Documents
165165
.Where(d => !BannedPaths.Any(d.FilePath.Contains))
166166
.SelectAsync(async d => (Doc: d, Length: (await d.GetTextAsync()).Length));
167167

168168
//Perf heuristic: Decrease memory pressure on the simplification phase by converting large files first https://github.com/icsharpcode/CodeConverter/issues/524#issuecomment-590301594
169169
var documentsToConvert = documentsWithLengths.OrderByDescending(d => d.Length).Select(d => d.Doc);
170170

171-
var projectConversion = new ProjectConversion(projectContentsConverter, documentsToConvert, projectContentsConverter.Project.AdditionalDocuments, languageConversion, cancellationToken, false);
171+
var projectConversion = new ProjectConversion(projectContentsConverter, documentsToConvert, projectContentsConverter.SourceProject.AdditionalDocuments, languageConversion, cancellationToken, false);
172172

173173
var results = projectConversion.Convert(progress);
174174
await foreach (var result in results) yield return result;
@@ -181,9 +181,9 @@ private async IAsyncEnumerable<ConversionResult> Convert(IProgress<ConversionPro
181181
var firstPassResults = _documentsToConvert.ParallelSelectAwait(d => FirstPassLoggedAsync(d, phaseProgress), Env.MaxDop, _cancellationToken);
182182
var (proj1, docs1) = await _projectContentsConverter.GetConvertedProjectAsync(await firstPassResults.ToArrayAsync());
183183

184-
var warnings = await GetProjectWarningsAsync(_projectContentsConverter.Project, proj1);
184+
var warnings = await GetProjectWarningsAsync(_projectContentsConverter.SourceProject, proj1);
185185
if (!string.IsNullOrWhiteSpace(warnings)) {
186-
var warningPath = Path.Combine(_projectContentsConverter.Project.GetDirectoryPath(), "ConversionWarnings.txt");
186+
var warningPath = Path.Combine(_projectContentsConverter.SourceProject.GetDirectoryPath(), "ConversionWarnings.txt");
187187
yield return new ConversionResult() { SourcePathOrNull = warningPath, Exceptions = new[] { warnings } };
188188
}
189189

@@ -339,7 +339,7 @@ private WipFileConversion<T> LogEnd<T>(WipFileConversion<T> convertedFile, strin
339339

340340
private string PathRelativeToSolutionDir(string path)
341341
{
342-
return path.Replace(this._projectContentsConverter.Project.Solution.GetDirectoryPath() + Path.DirectorySeparatorChar, "");
342+
return path.Replace(this._projectContentsConverter.SourceProject.Solution.GetDirectoryPath() + Path.DirectorySeparatorChar, "");
343343
}
344344

345345
private static async Task<IDisposable> RoslynEntryPointAsync(IProgress<ConversionProgress> progress)

CodeConverter/VB/CSToVBProjectContentsConverter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public CSToVBProjectContentsConverter(ConversionOptions conversionOptions, IProg
4444
}
4545

4646
public string RootNamespace { get; }
47-
public Project Project { get; private set; }
47+
public Project SourceProject { get; private set; }
4848

4949
public string LanguageVersion { get { return _vbParseOptions.LanguageVersion.ToDisplayString(); } }
5050

@@ -57,7 +57,7 @@ public async Task InitializeSourceAsync(Project project)
5757
_convertedVbProject = project.ToProjectFromAnyOptions(_vbCompilationOptions, _vbParseOptions);
5858
_vbReferenceProject = project.CreateReferenceOnlyProjectFromAnyOptions(_vbCompilationOptions, _vbParseOptions);
5959
_vbViewOfCsSymbols = (VisualBasicCompilation)await _vbReferenceProject.GetCompilationAsync(_cancellationToken);
60-
Project = project;
60+
SourceProject = project;
6161
}
6262

6363
public async Task<SyntaxNode> SingleFirstPassAsync(Document document)

Tests/CSharp/ExpressionTests/AccessExpressionTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ Public Sub compareAccess()
542542
End Sub
543543
End Class",
544544
@"using System;
545-
using Microsoft.VisualBasic;
545+
using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
546546
547547
public partial class Issue479
548548
{
@@ -591,8 +591,8 @@ End Sub
591591
@"using System;
592592
using System.IO;
593593
using SIO = System.IO;
594-
using Microsoft.VisualBasic;
595-
using VB = Microsoft.VisualBasic;
594+
using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
595+
using VB = Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
596596
597597
public partial class Test
598598
{

Tests/CSharp/ExpressionTests/BinaryExpressionTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ await TestConversionVisualBasicToCSharpAsync(@"Public Class Class1
6161
Sub Foo()
6262
Dim x = """" Like ""*x*""
6363
End Sub
64-
End Class", @"using Microsoft.VisualBasic;
65-
using Microsoft.VisualBasic.CompilerServices;
64+
End Class", @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
65+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
6666
6767
public partial class Class1
6868
{
@@ -135,7 +135,7 @@ Private Sub TestMethod()
135135
Dim z = x + y
136136
Dim z2 = y + x
137137
End Sub
138-
End Class", @"using Microsoft.VisualBasic.CompilerServices;
138+
End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
139139
140140
internal partial class TestClass
141141
{

Tests/CSharp/ExpressionTests/ByRefTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Function Bar3(ByRef c1 As Class1) As String
166166
Return """"
167167
End Function
168168
169-
End Class", @"using Microsoft.VisualBasic;
169+
End Class", @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
170170
171171
public partial class Class1
172172
{
@@ -369,7 +369,7 @@ End If
369369
Console.WriteLine(someInt)
370370
End Sub
371371
End Class", @"using System;
372-
using Microsoft.VisualBasic.CompilerServices;
372+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
373373
374374
public partial class MyTestClass
375375
{
@@ -487,7 +487,7 @@ Public lst As List(Of String) = New List(Of String)({ 1.ToString(), 2.ToString()
487487
Public lst2 As List(Of Object) = New List(Of Object)({ 1.ToString(), 2.ToString(), 3.ToString()})
488488
End Module", @"using System.Collections.Generic;
489489
using System.Diagnostics;
490-
using Microsoft.VisualBasic.CompilerServices;
490+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
491491
492492
public partial class Issue567
493493
{

Tests/CSharp/ExpressionTests/ExpressionTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ End Sub
8787
8888
Sub Bar(x as Integer)
8989
End Sub
90-
End Class", @"using Microsoft.VisualBasic.CompilerServices;
90+
End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
9191
9292
public partial class Class1
9393
{
@@ -116,7 +116,7 @@ Sub Main()
116116
Foo(0)
117117
End Sub
118118
End Class",
119-
@"using Microsoft.VisualBasic;
119+
@"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
120120
121121
public partial class Class1
122122
{
@@ -1014,7 +1014,7 @@ End Function
10141014
Dim test3 = Function(a, b) a Mod b
10151015
test(3)
10161016
End Sub
1017-
End Class", @"using Microsoft.VisualBasic.CompilerServices;
1017+
End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
10181018
10191019
internal partial class TestClass
10201020
{
@@ -1128,7 +1128,7 @@ await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
11281128
Public Function TestMethod() As String
11291129
Return vbCrLf
11301130
End Function
1131-
End Class", @"using Microsoft.VisualBasic;
1131+
End Class", @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
11321132
11331133
internal partial class TestClass
11341134
{
@@ -1332,7 +1332,7 @@ await TestConversionVisualBasicToCSharpAsync(@"Public Class Class1
13321332
Sub Foo()
13331333
Dim x = DateAdd(""m"", 5, Now)
13341334
End Sub
1335-
End Class", @"using Microsoft.VisualBasic;
1335+
End Class", @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
13361336
13371337
public partial class Class1
13381338
{
@@ -1366,7 +1366,7 @@ await TestConversionVisualBasicToCSharpAsync(@"Module Module1
13661366
Sub Main()
13671367
Dim x = Microsoft.VisualBasic.Timer
13681368
End Sub
1369-
End Module", @"using Microsoft.VisualBasic;
1369+
End Module", @"using Microsoft.VisualBasic; // Install-Package Microsoft.VisualBasic
13701370
13711371
internal static partial class Module1
13721372
{
@@ -1384,7 +1384,7 @@ await TestConversionVisualBasicToCSharpAsync(@"Module Module1
13841384
Sub Main()
13851385
Dim x As Short = If(True, CShort(50), 100S)
13861386
End Sub
1387-
End Module", @"using Microsoft.VisualBasic.CompilerServices;
1387+
End Module", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
13881388
13891389
internal static partial class Module1
13901390
{

0 commit comments

Comments
 (0)