Skip to content

Commit

Permalink
Ensure casing matches for namespaces [#421]
Browse files Browse the repository at this point in the history
Fixes #421
  • Loading branch information
mrmonday authored and GrahamTheCoder committed Nov 19, 2019
1 parent 81f9aa2 commit 4c229e8
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All notable changes to the code converter will be documented here.
* Prefer renamed imports for name resolution (#401)
* Correctly convert ambiguous names (#332)
* Ensure correct visibility for constructors (#422)
* Ensure casing is correct for namespaces (#421)

### C# -> VB
* Convert property accessors with visiblity modifiers (#92)
Expand Down
15 changes: 13 additions & 2 deletions ICSharpCode.CodeConverter/CSharp/CommonConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,17 @@ private TypeSyntax GetFuncTypeSyntax(IMethodSymbol method)
public TypeSyntax GetTypeSyntax(ITypeSymbol typeSymbol, bool useImplicitType = false)
{
if (useImplicitType || typeSymbol == null) return CreateVarTypeName();
return (TypeSyntax) CsSyntaxGenerator.TypeExpression(typeSymbol);
var vbType = SyntaxFactory.ParseTypeName(typeSymbol.ToDisplayString());
var originalNames = vbType.DescendantNodes().OfType<CSSyntax.IdentifierNameSyntax>().Select(i => i.ToString()).ToList();
var syntax = (TypeSyntax) CsSyntaxGenerator.TypeExpression(typeSymbol);

return syntax.ReplaceNodes(syntax.DescendantNodes().OfType<CSSyntax.IdentifierNameSyntax>(), (oldNode, n) => {
var originalName = originalNames.Where(on => string.Equals(on, oldNode.ToString(), StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (originalName != null) {
return SyntaxFactory.IdentifierName(originalName);
}
return oldNode;
});
}

private static TypeSyntax CreateVarTypeName()
Expand Down Expand Up @@ -216,7 +226,8 @@ public SyntaxToken ConvertIdentifier(SyntaxToken id, bool isAttribute = false)
if (id.SyntaxTree == _semanticModel.SyntaxTree) {
var symbol = _semanticModel.GetSymbolInfo(id.Parent).Symbol;
if (symbol != null && !String.IsNullOrWhiteSpace(symbol.Name)) {
if (text.Equals(symbol.Name, StringComparison.OrdinalIgnoreCase)) {
bool isDeclaration = symbol.Locations.Any(l => l.SourceSpan == id.Span);
if (!isDeclaration && text.Equals(symbol.Name, StringComparison.OrdinalIgnoreCase)) {
text = symbol.Name;
}

Expand Down
9 changes: 8 additions & 1 deletion ICSharpCode.CodeConverter/CSharp/DeclarationNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,14 @@ public override async Task<CSharpSyntaxNode> VisitSimpleImportsClause(VBSyntax.S
public override async Task<CSharpSyntaxNode> VisitNamespaceBlock(VBSyntax.NamespaceBlockSyntax node)
{
var members = (await node.Members.SelectAsync(ConvertMember)).Where(m => m != null);
var namespaceToDeclare = _semanticModel.GetDeclaredSymbol(node)?.ToDisplayString() ?? node.NamespaceStatement.Name.ToString();
var sym = _semanticModel.GetDeclaredSymbol(node);
var sourceName = (await node.NamespaceStatement.Name.AcceptAsync(_triviaConvertingExpressionVisitor)).ToString();
var namespaceToDeclare = sym?.ToDisplayString() ?? sourceName;
int lastIndex = namespaceToDeclare.LastIndexOf(sourceName, StringComparison.OrdinalIgnoreCase);
if (lastIndex >= 0 && lastIndex + sourceName.Length == namespaceToDeclare.Length) {
// Prefer source casing
namespaceToDeclare = namespaceToDeclare.Substring(0, lastIndex) + sourceName;
}
var parentNamespaceSyntax = node.GetAncestor<VBSyntax.NamespaceBlockSyntax>();
var parentNamespaceDecl = parentNamespaceSyntax != null ? _semanticModel.GetDeclaredSymbol(parentNamespaceSyntax) : null;
var parentNamespaceFullName = parentNamespaceDecl?.ToDisplayString() ?? _topAncestorNamespace;
Expand Down
54 changes: 54 additions & 0 deletions Tests/CSharp/NamespaceLevelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,60 @@ internal partial class TestClass<T>
}");
}

[Fact]
public async Task TestMixedCaseNamespace()
{
await TestConversionVisualBasicToCSharpWithoutComments(@"Namespace [Aaa]
Friend Class A
End Class
End Namespace
Namespace Global.aaa
Friend Class B
End Class
End Namespace
Friend Module C
Sub Main()
Dim x = New aaa.A
Dim y = New Aaa.B
Dim z = New Aaa.A
Dim a = New aaa.B
Dim b = New aaa.a
Dim c = New aaa.b
Dim d = New AAA.A
Dim e = New AAA.B
End Sub
End Module", @"namespace Aaa
{
internal partial class A
{
}
}
namespace aaa
{
internal partial class B
{
}
}
internal static partial class C
{
public static void Main()
{
var x = new Aaa.A();
var y = new aaa.B();
var z = new Aaa.A();
var a = new aaa.B();
var b = new Aaa.A();
var c = new aaa.B();
var d = new Aaa.A();
var e = new aaa.B();
}
}");
}

[Fact]
public async Task TestInternalStaticClass()
{
Expand Down

0 comments on commit 4c229e8

Please sign in to comment.