mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
21 KiB
546 lines
21 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Linq; |
|
using System.Collections.Generic; |
|
using ICSharpCode.NRefactory; |
|
using ICSharpCode.NRefactory.Ast; |
|
using ICSharpCode.NRefactory.AstBuilder; |
|
using ICSharpCode.NRefactory.Visitors; |
|
using System.Runtime.InteropServices; |
|
|
|
namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver |
|
{ |
|
/// <summary> |
|
/// This class converts C# constructs to their VB.NET equivalents. |
|
/// </summary> |
|
public class CSharpToVBNetConvertVisitor : CSharpConstructsConvertVisitor |
|
{ |
|
NRefactoryResolver resolver; |
|
ParseInformation parseInformation; |
|
IProjectContent projectContent; |
|
public string RootNamespaceToRemove { get; set; } |
|
public string StartupObjectToMakePublic { get; set; } |
|
public IList<string> DefaultImportsToRemove { get; set; } |
|
|
|
public CSharpToVBNetConvertVisitor(IProjectContent pc, ParseInformation parseInfo) |
|
{ |
|
this.resolver = new NRefactoryResolver(LanguageProperties.CSharp); |
|
this.projectContent = pc; |
|
this.parseInformation = parseInfo; |
|
} |
|
|
|
public override object VisitCompilationUnit(CompilationUnit compilationUnit, object data) |
|
{ |
|
base.VisitCompilationUnit(compilationUnit, data); |
|
ToVBNetConvertVisitor v = new ToVBNetConvertVisitor(); |
|
compilationUnit.AcceptVisitor(v, data); |
|
return null; |
|
} |
|
|
|
IReturnType ResolveType(TypeReference typeRef) |
|
{ |
|
return TypeVisitor.CreateReturnType(typeRef, resolver); |
|
} |
|
|
|
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) |
|
{ |
|
if (RootNamespaceToRemove != null) { |
|
if (namespaceDeclaration.Name == RootNamespaceToRemove) { |
|
// remove namespace declaration |
|
INode insertAfter = namespaceDeclaration; |
|
foreach (INode child in namespaceDeclaration.Children) { |
|
InsertAfterSibling(insertAfter, child); |
|
insertAfter = child; |
|
} |
|
namespaceDeclaration.Children.Clear(); |
|
RemoveCurrentNode(); |
|
} else if (namespaceDeclaration.Name.StartsWith(RootNamespaceToRemove + ".")) { |
|
namespaceDeclaration.Name = namespaceDeclaration.Name.Substring(RootNamespaceToRemove.Length + 1); |
|
} |
|
} |
|
base.VisitNamespaceDeclaration(namespaceDeclaration, data); |
|
return null; |
|
} |
|
|
|
public override object VisitUsing(Using @using, object data) |
|
{ |
|
base.VisitUsing(@using, data); |
|
if (DefaultImportsToRemove != null && !@using.IsAlias) { |
|
if (DefaultImportsToRemove.Contains(@using.Name)) { |
|
RemoveCurrentNode(); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public override object VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) |
|
{ |
|
base.VisitUsingDeclaration(usingDeclaration, data); |
|
if (usingDeclaration.Usings.Count == 0) { |
|
RemoveCurrentNode(); |
|
} |
|
return null; |
|
} |
|
|
|
struct BaseType |
|
{ |
|
internal readonly TypeReference TypeReference; |
|
internal readonly IReturnType ReturnType; |
|
internal readonly IClass UnderlyingClass; |
|
|
|
public BaseType(TypeReference typeReference, IReturnType returnType) |
|
{ |
|
this.TypeReference = typeReference; |
|
this.ReturnType = returnType; |
|
this.UnderlyingClass = returnType.GetUnderlyingClass(); |
|
} |
|
} |
|
|
|
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) |
|
{ |
|
// Initialize resolver for method: |
|
if (!methodDeclaration.Body.IsNull) { |
|
if (resolver.Initialize(parseInformation, methodDeclaration.Body.StartLocation.Y, methodDeclaration.Body.StartLocation.X)) { |
|
resolver.RunLookupTableVisitor(methodDeclaration); |
|
} |
|
} |
|
IMethod currentMethod = resolver.CallingMember as IMethod; |
|
CreateInterfaceImplementations(currentMethod, methodDeclaration, methodDeclaration.InterfaceImplementations); |
|
// Make "Main" public |
|
if (currentMethod != null && currentMethod.Name == "Main") { |
|
if (currentMethod.DeclaringType.FullyQualifiedName == StartupObjectToMakePublic) { |
|
if (currentMethod.IsStatic && currentMethod.IsPrivate) { |
|
methodDeclaration.Modifier &= ~Modifiers.Private; |
|
methodDeclaration.Modifier |= Modifiers.Internal; |
|
} |
|
} |
|
} |
|
if (resolver.CallingClass != null && resolver.CallingClass.BaseType != null) { |
|
// methods with the same name as a method in a base class must have 'Overloads' |
|
if ((methodDeclaration.Modifier & (Modifiers.Override | Modifiers.New)) == Modifiers.None) { |
|
if (resolver.CallingClass.BaseType.GetMethods() |
|
.Any(m => string.Equals(m.Name, methodDeclaration.Name, StringComparison.OrdinalIgnoreCase))) { |
|
methodDeclaration.Modifier |= Modifiers.Overloads; |
|
} |
|
} |
|
} |
|
return base.VisitMethodDeclaration(methodDeclaration, data); |
|
} |
|
|
|
ClassFinder CreateContext() |
|
{ |
|
return new ClassFinder(resolver.CallingClass, resolver.CallingMember, resolver.CaretLine, resolver.CaretColumn); |
|
} |
|
|
|
void CreateInterfaceImplementations(IMember currentMember, ParametrizedNode memberDecl, List<InterfaceImplementation> interfaceImplementations) |
|
{ |
|
if (currentMember != null |
|
&& (memberDecl.Modifier & Modifiers.Visibility) == Modifiers.None |
|
&& interfaceImplementations.Count == 1) |
|
{ |
|
// member is explicitly implementing an interface member |
|
// to convert explicit interface implementations to VB, make the member private |
|
// and ensure its name does not collide with another member |
|
memberDecl.Modifier |= Modifiers.Private; |
|
memberDecl.Name = interfaceImplementations[0].InterfaceType.Type.Replace('.', '_') + "_" + memberDecl.Name; |
|
} |
|
|
|
if (currentMember != null && currentMember.IsPublic |
|
&& currentMember.DeclaringType.ClassType != ClassType.Interface) |
|
{ |
|
// member could be implicitly implementing an interface member, |
|
// search for interfaces containing the member |
|
foreach (IReturnType directBaseType in currentMember.DeclaringType.GetCompoundClass().BaseTypes) { |
|
IClass directBaseClass = directBaseType.GetUnderlyingClass(); |
|
if (directBaseClass != null && directBaseClass.ClassType == ClassType.Interface) { |
|
// include members inherited from other interfaces in the search: |
|
foreach (IReturnType baseType in MemberLookupHelper.GetTypeInheritanceTree(directBaseType)) { |
|
IClass baseClass = baseType.GetUnderlyingClass(); |
|
if (baseClass != null && baseClass.ClassType == ClassType.Interface) { |
|
IMember similarMember = MemberLookupHelper.FindSimilarMember(baseClass, currentMember); |
|
// add an interface implementation for similarMember |
|
// only when similarMember is not explicitly implemented by another member in this class |
|
if (similarMember != null && !HasExplicitImplementationFor(similarMember, baseType, memberDecl.Parent)) { |
|
interfaceImplementations.Add(new InterfaceImplementation( |
|
Refactoring.CodeGenerator.ConvertType(baseType, CreateContext()), |
|
currentMember.Name)); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
bool HasExplicitImplementationFor(IMember interfaceMember, IReturnType interfaceReference, INode typeDecl) |
|
{ |
|
if (typeDecl == null) |
|
return false; |
|
foreach (INode node in typeDecl.Children) { |
|
MemberNode memberNode = node as MemberNode; |
|
if (memberNode != null && memberNode.InterfaceImplementations.Count > 0) { |
|
foreach (InterfaceImplementation impl in memberNode.InterfaceImplementations) { |
|
if (impl.MemberName == interfaceMember.Name |
|
&& object.Equals(ResolveType(impl.InterfaceType), interfaceReference)) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) |
|
{ |
|
if (!constructorDeclaration.Body.IsNull) { |
|
if (resolver.Initialize(parseInformation, constructorDeclaration.Body.StartLocation.Y, constructorDeclaration.Body.StartLocation.X)) { |
|
resolver.RunLookupTableVisitor(constructorDeclaration); |
|
} |
|
} |
|
return base.VisitConstructorDeclaration(constructorDeclaration, data); |
|
} |
|
|
|
public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) |
|
{ |
|
if (resolver.Initialize(parseInformation, propertyDeclaration.BodyStart.Y, propertyDeclaration.BodyStart.X)) { |
|
resolver.RunLookupTableVisitor(propertyDeclaration); |
|
} |
|
IProperty currentProperty = resolver.CallingMember as IProperty; |
|
CreateInterfaceImplementations(currentProperty, propertyDeclaration, propertyDeclaration.InterfaceImplementations); |
|
return base.VisitPropertyDeclaration(propertyDeclaration, data); |
|
} |
|
|
|
public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) |
|
{ |
|
if (resolver.CompilationUnit == null) |
|
return base.VisitExpressionStatement(expressionStatement, data); |
|
|
|
// Transform event invocations that aren't already transformed by a parent IfStatement to RaiseEvent statement |
|
InvocationExpression eventInvocation = expressionStatement.Expression as InvocationExpression; |
|
if (eventInvocation != null && eventInvocation.TargetObject is IdentifierExpression) { |
|
MemberResolveResult mrr = resolver.ResolveInternal(eventInvocation.TargetObject, ExpressionContext.Default) as MemberResolveResult; |
|
if (mrr != null && mrr.ResolvedMember is IEvent) { |
|
ReplaceCurrentNode(new RaiseEventStatement( |
|
((IdentifierExpression)eventInvocation.TargetObject).Identifier, |
|
eventInvocation.Arguments)); |
|
} |
|
} |
|
base.VisitExpressionStatement(expressionStatement, data); |
|
|
|
HandleAssignmentStatement(expressionStatement.Expression as AssignmentExpression); |
|
return null; |
|
} |
|
|
|
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) |
|
{ |
|
base.VisitBinaryOperatorExpression(binaryOperatorExpression, data); |
|
|
|
if (resolver.CompilationUnit == null) |
|
return null; |
|
|
|
switch (binaryOperatorExpression.Op) { |
|
case BinaryOperatorType.Equality: |
|
case BinaryOperatorType.InEquality: |
|
ConvertEqualityToReferenceEqualityIfRequired(binaryOperatorExpression); |
|
break; |
|
case BinaryOperatorType.Add: |
|
ConvertArgumentsForStringConcatenationIfRequired(binaryOperatorExpression); |
|
break; |
|
case BinaryOperatorType.Divide: |
|
ConvertDivisionToIntegerDivisionIfRequired(binaryOperatorExpression); |
|
break; |
|
} |
|
return null; |
|
} |
|
|
|
void ConvertEqualityToReferenceEqualityIfRequired(BinaryOperatorExpression binaryOperatorExpression) |
|
{ |
|
// maybe we have to convert Equality operator to ReferenceEquality |
|
ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); |
|
ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); |
|
if (left != null && right != null && left.ResolvedType != null && right.ResolvedType != null) { |
|
IClass cLeft = left.ResolvedType.GetUnderlyingClass(); |
|
IClass cRight = right.ResolvedType.GetUnderlyingClass(); |
|
if (cLeft != null && cRight != null) { |
|
if ((cLeft.ClassType != ClassType.Struct && cLeft.ClassType != ClassType.Enum) |
|
|| (cRight.ClassType != ClassType.Struct && cRight.ClassType != ClassType.Enum)) |
|
{ |
|
// this is a reference comparison |
|
if (cLeft.FullyQualifiedName != "System.String") { |
|
// and it's not a string comparison, so we'll use reference equality |
|
if (binaryOperatorExpression.Op == BinaryOperatorType.Equality) { |
|
binaryOperatorExpression.Op = BinaryOperatorType.ReferenceEquality; |
|
} else { |
|
binaryOperatorExpression.Op = BinaryOperatorType.ReferenceInequality; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void ConvertArgumentsForStringConcatenationIfRequired(BinaryOperatorExpression binaryOperatorExpression) |
|
{ |
|
ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); |
|
ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); |
|
|
|
if (left != null && right != null) { |
|
if (IsString(left.ResolvedType)) { |
|
binaryOperatorExpression.Op = BinaryOperatorType.Concat; |
|
if (NeedsExplicitConversionToString(right.ResolvedType)) { |
|
binaryOperatorExpression.Right = CreateExplicitConversionToString(binaryOperatorExpression.Right); |
|
} |
|
} else if (IsString(right.ResolvedType)) { |
|
binaryOperatorExpression.Op = BinaryOperatorType.Concat; |
|
if (NeedsExplicitConversionToString(left.ResolvedType)) { |
|
binaryOperatorExpression.Left = CreateExplicitConversionToString(binaryOperatorExpression.Left); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void ConvertDivisionToIntegerDivisionIfRequired(BinaryOperatorExpression binaryOperatorExpression) |
|
{ |
|
ResolveResult left = resolver.ResolveInternal(binaryOperatorExpression.Left, ExpressionContext.Default); |
|
ResolveResult right = resolver.ResolveInternal(binaryOperatorExpression.Right, ExpressionContext.Default); |
|
|
|
if (left != null && right != null) { |
|
if (IsInteger(left.ResolvedType) && IsInteger(right.ResolvedType)) { |
|
binaryOperatorExpression.Op = BinaryOperatorType.DivideInteger; |
|
} |
|
} |
|
} |
|
|
|
bool IsString(IReturnType rt) |
|
{ |
|
return rt != null && rt.IsDefaultReturnType && rt.FullyQualifiedName == "System.String"; |
|
} |
|
|
|
bool IsInteger(IReturnType rt) |
|
{ |
|
if (rt != null && rt.IsDefaultReturnType) { |
|
switch (rt.FullyQualifiedName) { |
|
case "System.Byte": |
|
case "System.SByte": |
|
case "System.Int16": |
|
case "System.UInt16": |
|
case "System.Int32": |
|
case "System.UInt32": |
|
case "System.Int64": |
|
case "System.UInt64": |
|
return true; |
|
} |
|
} |
|
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) { |
|
if (rt.IsDefaultReturnType) { |
|
if (rt.FullyQualifiedName == "System.Object" |
|
|| !TypeReference.PrimitiveTypesVBReverse.ContainsKey(rt.FullyQualifiedName)) |
|
{ |
|
// object and non-primitive types need explicit conversion |
|
return true; |
|
} else { |
|
// primitive types except object don't need explicit conversion |
|
return false; |
|
} |
|
} else { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
Expression CreateExplicitConversionToString(Expression expr) |
|
{ |
|
return new IdentifierExpression("Convert").Call("ToString", expr); |
|
} |
|
|
|
public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) |
|
{ |
|
base.VisitIdentifierExpression(identifierExpression, data); |
|
if (resolver.CompilationUnit == null) |
|
return null; |
|
|
|
InvocationExpression parentIE = identifierExpression.Parent as InvocationExpression; |
|
if (!(identifierExpression.Parent is AddressOfExpression) |
|
&& (parentIE == null || parentIE.TargetObject != identifierExpression)) |
|
{ |
|
ResolveResult rr = resolver.ResolveInternal(identifierExpression, ExpressionContext.Default); |
|
if (IsMethodGroup(rr)) { |
|
ReplaceCurrentNode(new AddressOfExpression(identifierExpression)); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public override object VisitMemberReferenceExpression(MemberReferenceExpression fieldReferenceExpression, object data) |
|
{ |
|
base.VisitMemberReferenceExpression(fieldReferenceExpression, data); |
|
|
|
if (resolver.CompilationUnit == null) |
|
return null; |
|
|
|
InvocationExpression parentIE = fieldReferenceExpression.Parent as InvocationExpression; |
|
if (!(fieldReferenceExpression.Parent is AddressOfExpression) |
|
&& (parentIE == null || parentIE.TargetObject != fieldReferenceExpression)) |
|
{ |
|
ResolveResult rr = resolver.ResolveInternal(fieldReferenceExpression, ExpressionContext.Default); |
|
if (IsMethodGroup(rr)) { |
|
ReplaceCurrentNode(new AddressOfExpression(fieldReferenceExpression)); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
static bool IsMethodGroup(ResolveResult rr) |
|
{ |
|
MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; |
|
if (mgrr != null) { |
|
return mgrr.Methods.Any(g=>g.Count > 0); |
|
} |
|
return false; |
|
} |
|
|
|
void HandleAssignmentStatement(AssignmentExpression assignmentExpression) |
|
{ |
|
if (resolver.CompilationUnit == null || assignmentExpression == null) |
|
return; |
|
|
|
if (assignmentExpression.Op == AssignmentOperatorType.Add || assignmentExpression.Op == AssignmentOperatorType.Subtract) { |
|
ResolveResult rr = resolver.ResolveInternal(assignmentExpression.Left, ExpressionContext.Default); |
|
if (rr is MemberResolveResult && (rr as MemberResolveResult).ResolvedMember is IEvent) { |
|
if (assignmentExpression.Op == AssignmentOperatorType.Add) { |
|
ReplaceCurrentNode(new AddHandlerStatement(assignmentExpression.Left, assignmentExpression.Right)); |
|
} else { |
|
ReplaceCurrentNode(new RemoveHandlerStatement(assignmentExpression.Left, assignmentExpression.Right)); |
|
} |
|
} else if (rr != null && rr.ResolvedType != null) { |
|
IClass c = rr.ResolvedType.GetUnderlyingClass(); |
|
if (c != null && c.ClassType == ClassType.Delegate) { |
|
InvocationExpression invocation = |
|
new IdentifierExpression("Delegate").Call( |
|
assignmentExpression.Op == AssignmentOperatorType.Add ? "Combine" : "Remove", |
|
assignmentExpression.Left); |
|
invocation.Arguments.Add(assignmentExpression.Right); |
|
|
|
assignmentExpression.Op = AssignmentOperatorType.Assign; |
|
assignmentExpression.Right = new CastExpression( |
|
Refactoring.CodeGenerator.ConvertType(rr.ResolvedType, CreateContext()), |
|
invocation, CastType.Cast); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public override object VisitCastExpression(CastExpression castExpression, object data) |
|
{ |
|
base.VisitCastExpression(castExpression, data); |
|
|
|
if (resolver.CompilationUnit == null) |
|
return null; |
|
|
|
IReturnType targetType = ResolveType(castExpression.CastTo); |
|
IClass targetClass = targetType != null ? targetType.GetUnderlyingClass() : null; |
|
if (castExpression.CastType != CastType.TryCast) { |
|
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 (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); |
|
} else if (sourceType != null && sourceType.FullyQualifiedName == "System.Char") { |
|
// casts from char to int are valid in C#, but need to use AscW in VB |
|
castExpression.Expression = ExpressionBuilder.Identifier("AscW").Call(castExpression.Expression); |
|
if (targetType != null && targetType.FullyQualifiedName == "System.Int32") { |
|
// AscW already returns int, so skip the cast |
|
ReplaceCurrentNode(castExpression.Expression); |
|
return null; |
|
} |
|
} |
|
} |
|
} |
|
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; |
|
} |
|
|
|
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) |
|
{ |
|
base.VisitUnaryOperatorExpression(unaryOperatorExpression, data); |
|
switch (unaryOperatorExpression.Op) { |
|
case UnaryOperatorType.Dereference: |
|
ReplaceCurrentNode(unaryOperatorExpression.Expression.Member("Target")); |
|
break; |
|
case UnaryOperatorType.AddressOf: |
|
ResolveResult rr = resolver.ResolveInternal(unaryOperatorExpression.Expression, ExpressionContext.Default); |
|
if (rr != null && rr.ResolvedType != null) { |
|
TypeReference targetType = Refactoring.CodeGenerator.ConvertType(rr.ResolvedType, CreateContext()); |
|
TypeReference pointerType = new TypeReference("Pointer", new List<TypeReference> { targetType }); |
|
ReplaceCurrentNode(pointerType.New(unaryOperatorExpression.Expression)); |
|
} |
|
break; |
|
} |
|
return null; |
|
} |
|
|
|
public override object VisitTypeReference(TypeReference typeReference, object data) |
|
{ |
|
while (typeReference.PointerNestingLevel > 0) { |
|
TypeReference tr = new TypeReference(typeReference.Type) { |
|
IsKeyword = typeReference.IsKeyword, |
|
IsGlobal = typeReference.IsGlobal, |
|
}; |
|
tr.GenericTypes.AddRange(typeReference.GenericTypes); |
|
|
|
typeReference = new TypeReference("Pointer") { |
|
StartLocation = typeReference.StartLocation, |
|
EndLocation = typeReference.EndLocation, |
|
PointerNestingLevel = typeReference.PointerNestingLevel - 1, |
|
GenericTypes = { tr }, |
|
RankSpecifier = typeReference.RankSpecifier |
|
}; |
|
} |
|
ReplaceCurrentNode(typeReference); |
|
return base.VisitTypeReference(typeReference, data); |
|
} |
|
|
|
public override object VisitUnsafeStatement(UnsafeStatement unsafeStatement, object data) |
|
{ |
|
base.VisitUnsafeStatement(unsafeStatement, data); |
|
ReplaceCurrentNode(unsafeStatement.Block); |
|
return null; |
|
} |
|
} |
|
}
|
|
|