Browse Source

Fixed SD2-1465 - Convert integer casts correctly between VB and C#

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4570 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
34e76133c3
  1. 108
      src/Libraries/NRefactory/Project/Src/PrettyPrinter/VBNet/VBNetOutputVisitor.cs
  2. 48
      src/Libraries/NRefactory/Project/Src/Visitors/ToCSharpConvertVisitor.cs
  3. 34
      src/Libraries/NRefactory/Project/Src/Visitors/ToVBNetConvertVisitor.cs
  4. 4
      src/Libraries/NRefactory/Project/Src/Visitors/ToVBNetRenameConflictingVariables.cs
  5. 6
      src/Libraries/NRefactory/Test/Output/CSharp/VBNetToCSharpConverterTest.cs
  6. 36
      src/Libraries/NRefactory/Test/Output/VBNet/CSharpToVBNetConverterTest.cs
  7. 25
      src/Libraries/NRefactory/Test/Output/VBNet/VBNetOutputTest.cs
  8. 42
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs
  9. 14
      src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs

108
src/Libraries/NRefactory/Project/Src/PrettyPrinter/VBNet/VBNetOutputVisitor.cs

@ -2080,53 +2080,70 @@ namespace ICSharpCode.NRefactory.PrettyPrinter @@ -2080,53 +2080,70 @@ namespace ICSharpCode.NRefactory.PrettyPrinter
}
public override object TrackedVisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data)
{
outputFormatter.PrintText(ToVBNetString(primitiveExpression));
return null;
}
internal static string ToVBNetString(PrimitiveExpression primitiveExpression)
{
object val = primitiveExpression.Value;
if (val == null) {
outputFormatter.PrintToken(Tokens.Nothing);
return null;
return "Nothing";
}
if (val is bool) {
if ((bool)primitiveExpression.Value) {
outputFormatter.PrintToken(Tokens.True);
return "True";
} else {
outputFormatter.PrintToken(Tokens.False);
return "False";
}
return null;
}
if (val is string) {
outputFormatter.PrintText(ConvertString((string)val));
return null;
return ConvertString((string)val);
}
if (val is char) {
outputFormatter.PrintText(ConvertCharLiteral((char)primitiveExpression.Value));
return null;
return ConvertCharLiteral((char)primitiveExpression.Value);
}
if (val is decimal) {
outputFormatter.PrintText(((decimal)primitiveExpression.Value).ToString(NumberFormatInfo.InvariantInfo) + "D");
return null;
return ((decimal)primitiveExpression.Value).ToString(NumberFormatInfo.InvariantInfo) + "D";
}
if (val is float) {
outputFormatter.PrintText(((float)primitiveExpression.Value).ToString(NumberFormatInfo.InvariantInfo) + "F");
return null;
return ((float)primitiveExpression.Value).ToString(NumberFormatInfo.InvariantInfo) + "F";
}
if (val is double) {
string text = ((double)val).ToString(NumberFormatInfo.InvariantInfo);
if (text.IndexOf('.') < 0 && text.IndexOf('E') < 0)
return text + ".0";
else
return text;
}
if (val is IFormattable) {
StringBuilder b = new StringBuilder();
if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) {
outputFormatter.PrintText("&H");
outputFormatter.PrintText(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
b.Append("&H");
b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
} else {
outputFormatter.PrintText(((IFormattable)val).ToString(null, NumberFormatInfo.InvariantInfo));
b.Append(((IFormattable)val).ToString(null, NumberFormatInfo.InvariantInfo));
}
if (val is ushort || val is uint || val is ulong) {
b.Append('U');
if (val is uint)
b.Append('I');
}
if (val is long || val is ulong)
b.Append('L');
if (val is short || val is ushort)
b.Append('S');
return b.ToString();
} else {
outputFormatter.PrintText(val.ToString());
return val.ToString();
}
return null;
}
public override object TrackedVisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
@ -2348,12 +2365,18 @@ namespace ICSharpCode.NRefactory.PrettyPrinter @@ -2348,12 +2365,18 @@ namespace ICSharpCode.NRefactory.PrettyPrinter
case UnaryOperatorType.Dereference:
outputFormatter.PrintToken(Tokens.Times);
TrackedVisit(unaryOperatorExpression.Expression, data);
return null;
case UnaryOperatorType.AddressOf:
outputFormatter.PrintToken(Tokens.AddressOf);
TrackedVisit(unaryOperatorExpression.Expression, data);
return null;
default:
Error("unknown unary operator: " + unaryOperatorExpression.Op.ToString(), unaryOperatorExpression.StartLocation);
outputFormatter.PrintText(unaryOperatorExpression.Op.ToString());
outputFormatter.PrintText("(");
TrackedVisit(unaryOperatorExpression.Expression, data);
outputFormatter.PrintText(")");
return null;
}
}
@ -2422,32 +2445,27 @@ namespace ICSharpCode.NRefactory.PrettyPrinter @@ -2422,32 +2445,27 @@ namespace ICSharpCode.NRefactory.PrettyPrinter
public override object TrackedVisitSizeOfExpression(SizeOfExpression sizeOfExpression, object data)
{
if (!sizeOfExpression.TypeReference.IsArrayType) {
if (sizeOfExpression.TypeReference.PointerNestingLevel != 0) {
outputFormatter.PrintText("IntPtr.Size");
return null;
} else {
switch (sizeOfExpression.TypeReference.Type) {
case "System.Byte":
case "System.SByte":
outputFormatter.PrintText("1");
return null;
case "System.Char":
case "System.Int16":
case "System.UInt16":
outputFormatter.PrintText("2");
return null;
case "System.Single":
case "System.Int32":
case "System.UInt32":
outputFormatter.PrintText("4");
return null;
case "System.Double":
case "System.Int64":
case "System.UInt64":
outputFormatter.PrintText("8");
return null;
}
if (!sizeOfExpression.TypeReference.IsArrayType && sizeOfExpression.TypeReference.PointerNestingLevel == 0) {
switch (sizeOfExpression.TypeReference.Type) {
case "System.Byte":
case "System.SByte":
outputFormatter.PrintText("1");
return null;
case "System.Char":
case "System.Int16":
case "System.UInt16":
outputFormatter.PrintText("2");
return null;
case "System.Single":
case "System.Int32":
case "System.UInt32":
outputFormatter.PrintText("4");
return null;
case "System.Double":
case "System.Int64":
case "System.UInt64":
outputFormatter.PrintText("8");
return null;
}
}
UnsupportedNode(sizeOfExpression);

48
src/Libraries/NRefactory/Project/Src/Visitors/ToCSharpConvertVisitor.cs

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
// <version>$Revision$</version>
// </file>
using ICSharpCode.NRefactory.AstBuilder;
using System;
using ICSharpCode.NRefactory.Ast;
@ -24,6 +25,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -24,6 +25,7 @@ namespace ICSharpCode.NRefactory.Visitors
// => create additional member for implementing the interface
// or convert to implicit interface implementation
// Modules: make all members static
// Use Convert.ToInt32 for VB casts
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
@ -226,5 +228,51 @@ namespace ICSharpCode.NRefactory.Visitors @@ -226,5 +228,51 @@ namespace ICSharpCode.NRefactory.Visitors
}
return base.VisitSwitchSection(switchSection, data);
}
public override object VisitCastExpression(CastExpression castExpression, object data)
{
base.VisitCastExpression(castExpression, data);
if (castExpression.CastType == CastType.Conversion || castExpression.CastType == CastType.PrimitiveConversion) {
switch (castExpression.CastTo.Type) {
case "System.Boolean":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToBoolean");
case "System.Byte":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToByte");
case "System.Char":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToChar");
case "System.DateTime":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToDateTime");
case "System.Decimal":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToDecimal");
case "System.Double":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToDouble");
case "System.Int16":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToInt16");
case "System.Int32":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToInt32");
case "System.Int64":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToInt64");
case "System.SByte":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToSByte");
case "System.Single":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToSingle");
case "System.String":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToString");
case "System.UInt16":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToUInt16");
case "System.UInt32":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToUInt32");
case "System.UInt64":
return ReplacePrimitiveCastWithConvertMethodCall(castExpression, "ToUInt64");
}
}
return null;
}
object ReplacePrimitiveCastWithConvertMethodCall(CastExpression castExpression, string methodName)
{
ReplaceCurrentNode(ExpressionBuilder.Identifier("Convert").Call(methodName, castExpression.Expression));
return null;
}
}
}

34
src/Libraries/NRefactory/Project/Src/Visitors/ToVBNetConvertVisitor.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.AstBuilder;
using Attribute = ICSharpCode.NRefactory.Ast.Attribute;
namespace ICSharpCode.NRefactory.Visitors
@ -27,6 +28,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -27,6 +28,7 @@ namespace ICSharpCode.NRefactory.Visitors
// Move Imports-statements out of namespaces
// Parenthesis around Cast expressions remove - these are syntax errors in VB.NET
// Decrease array creation size - VB specifies upper bound instead of array length
// Automatic properties are converted to explicit implementation
List<INode> nodesToMoveToCompilationUnit = new List<INode>();
@ -309,6 +311,25 @@ namespace ICSharpCode.NRefactory.Visitors @@ -309,6 +311,25 @@ namespace ICSharpCode.NRefactory.Visitors
ToVBNetRenameConflictingVariablesVisitor.RenameConflicting(propertyDeclaration);
if (!IsClassType(ClassType.Interface) && (propertyDeclaration.Modifier & Modifiers.Abstract) == 0) {
if (propertyDeclaration.HasGetRegion && propertyDeclaration.HasSetRegion) {
if (propertyDeclaration.GetRegion.Block.IsNull && propertyDeclaration.SetRegion.Block.IsNull) {
// automatically implemented property
string fieldName = "m_" + propertyDeclaration.Name;
Modifiers fieldModifier = propertyDeclaration.Modifier & ~(Modifiers.Visibility) | Modifiers.Private;
FieldDeclaration newField = new FieldDeclaration(null, propertyDeclaration.TypeReference, fieldModifier);
newField.Fields.Add(new VariableDeclaration(fieldName));
InsertAfterSibling(propertyDeclaration, newField);
propertyDeclaration.GetRegion.Block = new BlockStatement();
propertyDeclaration.GetRegion.Block.Return(ExpressionBuilder.Identifier(fieldName));
propertyDeclaration.SetRegion.Block = new BlockStatement();
propertyDeclaration.SetRegion.Block.Assign(ExpressionBuilder.Identifier(fieldName), ExpressionBuilder.Identifier("Value"));
}
}
}
return null;
}
@ -335,7 +356,9 @@ namespace ICSharpCode.NRefactory.Visitors @@ -335,7 +356,9 @@ namespace ICSharpCode.NRefactory.Visitors
{
base.VisitParenthesizedExpression(parenthesizedExpression, data);
if (parenthesizedExpression.Expression is CastExpression) {
ReplaceCurrentNode(parenthesizedExpression.Expression); // remove parenthesis
ReplaceCurrentNode(parenthesizedExpression.Expression); // remove parenthesis around casts
} else if (parenthesizedExpression.Parent is CastExpression) {
ReplaceCurrentNode(parenthesizedExpression.Expression); // remove parenthesis inside casts
}
return null;
}
@ -347,5 +370,14 @@ namespace ICSharpCode.NRefactory.Visitors @@ -347,5 +370,14 @@ namespace ICSharpCode.NRefactory.Visitors
}
return base.VisitArrayCreateExpression(arrayCreateExpression, data);
}
public override object VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data)
{
base.VisitDefaultValueExpression(defaultValueExpression, data);
Expression defaultValue = ExpressionBuilder.CreateDefaultValueForType(defaultValueExpression.TypeReference);
if (!(defaultValue is DefaultValueExpression))
ReplaceCurrentNode(defaultValue);
return null;
}
}
}

4
src/Libraries/NRefactory/Project/Src/Visitors/ToVBNetRenameConflictingVariables.cs

@ -20,7 +20,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -20,7 +20,7 @@ namespace ICSharpCode.NRefactory.Visitors
{
// variable name => case sensitive variable name
// value is null if there are multiple casings for the variable -> the variable is conflicting
Dictionary<string, string> caseInsensitive = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<string, string> caseInsensitive = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
LookupTableVisitor ltv = new LookupTableVisitor(SupportedLanguage.CSharp);
method.AcceptVisitor(ltv, null);
@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.Visitors
static void RenameVariable(INode method, string from, ref int index)
{
index += 1;
method.AcceptVisitor(new RenameLocalVariableVisitor(from, from + "__" + index, StringComparer.InvariantCulture), null);
method.AcceptVisitor(new RenameLocalVariableVisitor(from, from + "__" + index, StringComparer.Ordinal), null);
}
static void AddVariableToDict(Dictionary<string, string> caseInsensitive, string varName, bool hasDeclaration)

6
src/Libraries/NRefactory/Test/Output/CSharp/VBNetToCSharpConverterTest.cs

@ -697,5 +697,11 @@ static int static_Test2_j = 0;"); @@ -697,5 +697,11 @@ static int static_Test2_j = 0;");
" public const int C = 0;" + Environment.NewLine +
"}" + Environment.NewLine);
}
[Test]
public void CastToInteger()
{
TestStatement("Dim x As Integer = CInt(obj)", "int x = Convert.ToInt32(obj);");
}
}
}

36
src/Libraries/NRefactory/Test/Output/VBNet/CSharpToVBNetConverterTest.cs

@ -547,10 +547,40 @@ namespace ICSharpCode.NRefactory.Tests.PrettyPrinter @@ -547,10 +547,40 @@ namespace ICSharpCode.NRefactory.Tests.PrettyPrinter
}
[Test]
public void SizeOfInt32Pointer()
public void AutomaticProperty()
{
TestStatement(@"int i = sizeof(int*);",
@"Dim i As Integer = IntPtr.Size");
TestMember(@"public string Name { get; set; }",
@"Public Property Name() As String
Get
Return m_Name
End Get
Set
m_Name = Value
End Set
End Property
Private m_Name As String");
}
[Test]
public void AutomaticPropertyPrivateSetter()
{
TestMember(@"public string Name { get; private set; }",
@"Public Property Name() As String
Get
Return m_Name
End Get
Private Set
m_Name = Value
End Set
End Property
Private m_Name As String");
}
[Test]
public void DefaultValueExpression()
{
TestStatement(@"object x = default(int);",
@"Dim x As Object = 0");
}
}
}

25
src/Libraries/NRefactory/Test/Output/VBNet/VBNetOutputTest.cs

@ -380,6 +380,12 @@ End Using"); @@ -380,6 +380,12 @@ End Using");
TestTypeMember("Sub Main(ByRef one As Integer, ByRef two As Integer, <Out> ByRef three As Integer)\nEnd Sub");
}
[Test]
public void FieldWithoutType()
{
TestTypeMember("Dim X");
}
[Test]
public void UsingStatementForExistingVariable()
{
@ -431,6 +437,12 @@ End Using"); @@ -431,6 +437,12 @@ End Using");
TestExpression("16");
}
[Test]
public void Double()
{
TestExpression("1.0");
}
[Test]
public void HexadecimalInteger()
{
@ -442,5 +454,18 @@ End Using"); @@ -442,5 +454,18 @@ End Using");
{
TestExpression("&Hffffffff");
}
[Test]
public void TypeCharacters()
{
TestExpression("347S");
TestExpression("347L");
TestExpression("347D");
TestExpression("347F");
TestExpression("347US");
TestExpression("347UI");
TestExpression("347UL");
TestExpression("\".\"C");
}
}
}

42
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/CSharpToVBNetConvertVisitor.cs

@ -331,6 +331,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -331,6 +331,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return false;
}
bool IsFloatingPoint(IReturnType rt)
{
if (rt != null && rt.IsDefaultReturnType) {
switch (rt.FullyQualifiedName) {
case "System.Single":
case "System.Double":
case "System.Decimal":
return true;
}
}
return false;
}
bool NeedsExplicitConversionToString(IReturnType rt)
{
if (rt != null) {
@ -441,23 +454,30 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -441,23 +454,30 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
if (resolver.CompilationUnit == null)
return null;
IReturnType targetType = ResolveType(castExpression.CastTo);
IClass targetClass = targetType != null ? targetType.GetUnderlyingClass() : null;
if (castExpression.CastType != CastType.TryCast) {
IReturnType targetType = ResolveType(castExpression.CastTo);
if (targetType != null) {
IClass targetClass = targetType.GetUnderlyingClass();
if (targetClass != null && (targetClass.ClassType == ClassType.Struct || targetClass.ClassType == ClassType.Enum)) {
// cast to value type is a conversion
castExpression.CastType = CastType.Conversion;
}
if (targetClass != null && targetClass.FullyQualifiedName == "System.Char") {
// C# cast to char is done using ChrW function
if (targetClass != null && (targetClass.ClassType == ClassType.Struct || targetClass.ClassType == ClassType.Enum)) {
// cast to value type is a conversion
castExpression.CastType = CastType.Conversion;
if (IsInteger(targetType)) {
ResolveResult sourceRR = resolver.ResolveInternal(castExpression.Expression, ExpressionContext.Default);
IReturnType sourceType = sourceRR != null ? sourceRR.ResolvedType : null;
if (IsInteger(sourceType)) {
ReplaceCurrentNode(new IdentifierExpression("ChrW").Call(castExpression.Expression));
if (IsFloatingPoint(sourceType)) {
// casts from float to int in C# truncate, but VB rounds
// we'll have to introduce a call to Math.Truncate
castExpression.Expression = ExpressionBuilder.Identifier("Math").Call("Truncate", castExpression.Expression);
}
}
}
if (targetClass != null && targetClass.FullyQualifiedName == "System.Char") {
// C# cast to char is done using ChrW function
ResolveResult sourceRR = resolver.ResolveInternal(castExpression.Expression, ExpressionContext.Default);
IReturnType sourceType = sourceRR != null ? sourceRR.ResolvedType : null;
if (IsInteger(sourceType)) {
ReplaceCurrentNode(new IdentifierExpression("ChrW").Call(castExpression.Expression));
}
}
}
return null;
}

14
src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/CodeSnippetConverterTests.cs

@ -83,6 +83,20 @@ namespace ICSharpCode.SharpDevelop.Dom.Tests @@ -83,6 +83,20 @@ namespace ICSharpCode.SharpDevelop.Dom.Tests
out errors)));
}
[Test]
public void ConvertFloatingPointToInteger()
{
Assert.AreEqual("CInt(Math.Truncate(-3.5))", converter.CSharpToVB("(int)(-3.5)", out errors));
Assert.AreEqual("CInt(Math.Truncate(-3.5F))", converter.CSharpToVB("(int)(-3.5f)", out errors));
Assert.AreEqual("CInt(Math.Truncate(-3.5D))", converter.CSharpToVB("(int)(-3.5m)", out errors));
}
[Test]
public void ConvertLongToInteger()
{
Assert.AreEqual("CInt(-35L)", converter.CSharpToVB("(int)(-35L)", out errors));
}
string Normalize(string text)
{
return text.Replace("\t", " ").Replace("\r", "").Trim();

Loading…
Cancel
Save