From bf6217d038acde9535e315a65837163665a7959a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 10 Jun 2012 17:06:26 +0200 Subject: [PATCH] Improved CodeDomConvertVisitor. --- .../OutputVisitor/CodeDomConvertVisitor.cs | 121 ++++-- .../Parser/CSharpParser.cs | 4 +- .../CSharp/CodeDomConvertVisitorTests.cs | 376 +++++++++++++++--- 3 files changed, 413 insertions(+), 88 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs index 53584eef08..b8e94dcfb5 100644 --- a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs @@ -21,12 +21,12 @@ using System.CodeDom; using System.Collections.Generic; using System.IO; using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp { @@ -40,14 +40,23 @@ namespace ICSharpCode.NRefactory.CSharp { //ICompilation compilation = MinimalResolveContext.Instance; CSharpAstResolver resolver; - bool useFullyQualifiedTypeNames; /// /// Gets/Sets whether the visitor should use fully-qualified type references. /// - public bool UseFullyQualifiedTypeNames { - get { return useFullyQualifiedTypeNames; } - set { useFullyQualifiedTypeNames = value; } + public bool UseFullyQualifiedTypeNames { get; set; } + + /// + /// Gets whether the visitor is allowed to produce snippet nodes for + /// code that cannot be converted. + /// The default is true. If this property is set to false, + /// unconvertible code will throw a NotSupportedException. + /// + public bool AllowSnippetNodes { get; set; } + + public CodeDomConvertVisitor() + { + this.AllowSnippetNodes = true; } /// @@ -136,7 +145,15 @@ namespace ICSharpCode.NRefactory.CSharp CodeTypeReference Convert(IType type) { - return new CodeTypeReference(type.ReflectionName); + if (type.Kind == TypeKind.Array) { + ArrayType a = (ArrayType)type; + return new CodeTypeReference(Convert(a.ElementType), a.Dimensions); + } else if (type is ParameterizedType) { + var pt = (ParameterizedType)type; + return new CodeTypeReference(pt.GetDefinition().ReflectionName, pt.TypeArguments.Select(Convert).ToArray()); + } else { + return new CodeTypeReference(type.ReflectionName); + } } CodeStatement Convert(Statement stmt) @@ -148,6 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp { List result = new List(); foreach (Statement stmt in block.Statements) { + if (stmt is EmptyStatement) + continue; CodeStatement s = Convert(stmt); if (s != null) result.Add(s); @@ -160,6 +179,8 @@ namespace ICSharpCode.NRefactory.CSharp BlockStatement block = embeddedStatement as BlockStatement; if (block != null) { return ConvertBlock(block); + } else if (embeddedStatement is EmptyStatement) { + return new CodeStatement[0]; } CodeStatement s = Convert(embeddedStatement); if (s != null) @@ -170,6 +191,8 @@ namespace ICSharpCode.NRefactory.CSharp string MakeSnippet(AstNode node) { + if (!AllowSnippetNodes) + throw new NotSupportedException(); StringWriter w = new StringWriter(); CSharpOutputVisitor v = new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono ()); node.AcceptVisitor(v); @@ -371,7 +394,7 @@ namespace ICSharpCode.NRefactory.CSharp TypeResolveResult trr = rr as TypeResolveResult; if (trr != null) { CodeTypeReference typeRef; - if (useFullyQualifiedTypeNames) { + if (UseFullyQualifiedTypeNames) { typeRef = Convert(trr.Type); } else { typeRef = new CodeTypeReference(identifierExpression.Identifier); @@ -380,7 +403,7 @@ namespace ICSharpCode.NRefactory.CSharp return new CodeTypeReferenceExpression(typeRef); } MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; - if (mgrr != null) { + if (mgrr != null || identifierExpression.TypeArguments.Any()) { return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), identifierExpression.Identifier, Convert(identifierExpression.TypeArguments)); } return new CodeVariableReferenceExpression(identifierExpression.Identifier); @@ -658,7 +681,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { CodeTypeDelegate d = new CodeTypeDelegate(delegateDeclaration.Name); - d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers); + d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers, EntityType.TypeDefinition); d.CustomAttributes.AddRange(Convert(delegateDeclaration.Attributes)); d.ReturnType = Convert(delegateDeclaration.ReturnType); d.Parameters.AddRange(Convert(delegateDeclaration.Parameters)); @@ -666,13 +689,15 @@ namespace ICSharpCode.NRefactory.CSharp return d; } - static MemberAttributes ConvertMemberAttributes(Modifiers modifiers) + MemberAttributes ConvertMemberAttributes(Modifiers modifiers, EntityType entityType) { MemberAttributes a = 0; if ((modifiers & Modifiers.Abstract) != 0) a |= MemberAttributes.Abstract; if ((modifiers & Modifiers.Sealed) != 0) a |= MemberAttributes.Final; + if (entityType != EntityType.TypeDefinition && (modifiers & (Modifiers.Abstract | Modifiers.Override | Modifiers.Virtual)) == 0) + a |= MemberAttributes.Final; if ((modifiers & Modifiers.Static) != 0) a |= MemberAttributes.Static; if ((modifiers & Modifiers.Override) != 0) @@ -719,7 +744,7 @@ namespace ICSharpCode.NRefactory.CSharp { //bool isNestedType = typeStack.Count > 0; CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeDeclaration.Name); - typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers); + typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers, EntityType.TypeDefinition); typeDecl.CustomAttributes.AddRange(Convert(typeDeclaration.Attributes)); switch (typeDeclaration.ClassType) { @@ -809,7 +834,12 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) { - return null; + return EmptyStatement(); + } + + CodeStatement EmptyStatement() + { + return new CodeExpressionStatement(new CodeObjectCreateExpression(new CodeTypeReference(typeof(object)))); } CodeObject IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) @@ -817,10 +847,55 @@ namespace ICSharpCode.NRefactory.CSharp AssignmentExpression assignment = expressionStatement.Expression as AssignmentExpression; if (assignment != null && assignment.Operator == AssignmentOperatorType.Assign) { return new CodeAssignStatement(Convert(assignment.Left), Convert(assignment.Right)); + } else if (assignment != null && CanBeDuplicatedForCompoundAssignment(assignment.Left)) { + CodeBinaryOperatorType op; + switch (assignment.Operator) { + case AssignmentOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case AssignmentOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case AssignmentOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case AssignmentOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case AssignmentOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case AssignmentOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case AssignmentOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + default: + return MakeSnippetStatement(expressionStatement); + } + var cboe = new CodeBinaryOperatorExpression(Convert(assignment.Left), op, Convert(assignment.Right)); + return new CodeAssignStatement(Convert(assignment.Left), cboe); + } + UnaryOperatorExpression unary = expressionStatement.Expression as UnaryOperatorExpression; + if (unary != null && CanBeDuplicatedForCompoundAssignment(unary.Expression)) { + var op = unary.Operator; + if (op == UnaryOperatorType.Increment || op == UnaryOperatorType.PostIncrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } else if (op == UnaryOperatorType.Decrement || op == UnaryOperatorType.PostDecrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } } return new CodeExpressionStatement(Convert(expressionStatement.Expression)); } + bool CanBeDuplicatedForCompoundAssignment(Expression expr) + { + return expr is IdentifierExpression; + } + CodeObject IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) { return MakeSnippetStatement(fixedStatement); @@ -956,7 +1031,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) { - return new CodeIterationStatement(null, Convert(whileStatement.Condition), null, ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); + return new CodeIterationStatement(EmptyStatement(), Convert(whileStatement.Condition), EmptyStatement(), ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); } CodeObject IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) @@ -977,7 +1052,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { CodeConstructor ctor = new CodeConstructor(); - ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers); + ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers, EntityType.Constructor); ctor.CustomAttributes.AddRange(Convert(constructorDeclaration.Attributes)); if (constructorDeclaration.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { ctor.ChainedConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); @@ -1019,7 +1094,7 @@ namespace ICSharpCode.NRefactory.CSharp } CodeMemberEvent e = new CodeMemberEvent(); - e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers); + e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers, EntityType.Event); e.CustomAttributes.AddRange(Convert(eventDeclaration.Attributes)); e.Name = vi.Name; e.Type = Convert(eventDeclaration.ReturnType); @@ -1037,7 +1112,7 @@ namespace ICSharpCode.NRefactory.CSharp { foreach (VariableInitializer vi in fieldDeclaration.Variables) { CodeMemberField f = new CodeMemberField(Convert(fieldDeclaration.ReturnType), vi.Name); - f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers); + f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers, EntityType.Field); f.CustomAttributes.AddRange(Convert(fieldDeclaration.Attributes)); f.InitExpression = ConvertVariableInitializer(vi.Initializer, fieldDeclaration.ReturnType); AddTypeMember(f); @@ -1048,7 +1123,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { CodeMemberProperty p = new CodeMemberProperty(); - p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers); + p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers, EntityType.Indexer); p.CustomAttributes.AddRange(Convert(indexerDeclaration.Attributes)); p.Name = "Items"; p.PrivateImplementationType = Convert(indexerDeclaration.PrivateImplementationType); @@ -1069,7 +1144,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) { CodeMemberMethod m = new CodeMemberMethod(); - m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers); + m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers, EntityType.Method); m.CustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); m.ReturnTypeCustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); @@ -1087,7 +1162,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { CodeMemberMethod m = new CodeMemberMethod(); - m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers); + m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers, EntityType.Method); m.CustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); m.ReturnTypeCustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); @@ -1129,7 +1204,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { CodeMemberProperty p = new CodeMemberProperty(); - p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers); + p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers, EntityType.Property); p.CustomAttributes.AddRange(Convert(propertyDeclaration.Attributes)); p.Name = propertyDeclaration.Name; p.PrivateImplementationType = Convert(propertyDeclaration.PrivateImplementationType); @@ -1181,7 +1256,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitSimpleType(SimpleType simpleType) { - if (useFullyQualifiedTypeNames) { + if (UseFullyQualifiedTypeNames) { IType type = Resolve(simpleType).Type; if (type.Kind != TypeKind.Unknown) return Convert(type); @@ -1198,7 +1273,7 @@ namespace ICSharpCode.NRefactory.CSharp tr.TypeArguments.AddRange(Convert(memberType.TypeArguments)); return tr; } - if (useFullyQualifiedTypeNames || memberType.IsDoubleColon) { + if (UseFullyQualifiedTypeNames || memberType.IsDoubleColon) { IType type = Resolve(memberType).Type; if (type.Kind != TypeKind.Unknown) return Convert(type); @@ -1309,7 +1384,7 @@ namespace ICSharpCode.NRefactory.CSharp return null; } - CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, ICSharpCode.NRefactory.PatternMatching.Pattern pattern) + CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) { return null; } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index 8d750b714c..3f2c274438 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -3750,7 +3750,7 @@ namespace ICSharpCode.NRefactory.CSharp return AstType.Null; } - public AstNode ParseExpression (TextReader reader) + public Expression ParseExpression (TextReader reader) { var es = ParseStatements (new StringReader ("tmp = " + Environment.NewLine + reader.ReadToEnd () + ";"), -1).FirstOrDefault () as ExpressionStatement; if (es != null) { @@ -3758,7 +3758,7 @@ namespace ICSharpCode.NRefactory.CSharp if (ae != null) return ae.Right; } - return null; + return Expression.Null; } /// diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs index 92efb0f156..52d55315bd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs @@ -20,11 +20,11 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using Microsoft.CSharp; using NUnit.Framework; @@ -45,138 +45,388 @@ namespace ICSharpCode.NRefactory.CSharp parsedFile.RootUsingScope.Usings.Add(MakeReference("System.Linq")); convertVisitor = new CodeDomConvertVisitor(); + convertVisitor.AllowSnippetNodes = false; convertVisitor.UseFullyQualifiedTypeNames = true; } - string Convert(Expression expr) + #region Helper methods + string ConvertHelper(AstNode node, Action action) { CSharpResolver resolver = new CSharpResolver(compilation); resolver = resolver.WithCurrentUsingScope(parsedFile.RootUsingScope.Resolve(compilation)); resolver = resolver.WithCurrentTypeDefinition(compilation.FindType(KnownTypeCode.Object).GetDefinition()); - var codeExpr = (CodeExpression)convertVisitor.Convert(expr, new CSharpAstResolver(resolver, expr, parsedFile)); + var codeObj = convertVisitor.Convert(node, new CSharpAstResolver(resolver, node)); StringWriter writer = new StringWriter(); writer.NewLine = " "; - new CSharpCodeProvider().GenerateCodeFromExpression(codeExpr, writer, new CodeGeneratorOptions { IndentString = " " }); - return Regex.Replace(writer.ToString(), @"\s+", " "); + action(new CSharpCodeProvider(), codeObj, writer, new CodeGeneratorOptions { IndentString = " " }); + return Regex.Replace(writer.ToString(), @"\s+", " ").Trim(); } + string ConvertExpression(Expression expr) + { + return ConvertHelper(expr, (p,obj,w,opt) => p.GenerateCodeFromExpression((CodeExpression)obj, w, opt)); + } + + string ConvertExpression(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseExpression(new StringReader(code)); + Assert.IsFalse(parser.HasErrors); + return ConvertExpression(expr); + } + + string ConvertStatement(Statement statement) + { + return ConvertHelper(statement, (p,obj,w,opt) => p.GenerateCodeFromStatement((CodeStatement)obj, w, opt)); + } + + string ConvertStatement(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseStatements(new StringReader(code)).Single(); + Assert.IsFalse(parser.HasErrors); + return ConvertStatement(expr); + } + + string ConvertMember(EntityDeclaration entity) + { + return ConvertHelper(entity, (p,obj,w,opt) => p.GenerateCodeFromMember((CodeTypeMember)obj, w, opt)); + } + + string ConvertMember(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseTypeMembers(new StringReader(code)).Single(); + Assert.IsFalse(parser.HasErrors); + return ConvertMember(expr); + } + + string ConvertTypeDeclaration(EntityDeclaration decl) + { + return ConvertHelper(decl, (p,obj,w,opt) => p.GenerateCodeFromType((CodeTypeDeclaration)obj, w, opt)); + } + + string ConvertTypeDeclaration(string code) + { + CSharpParser parser = new CSharpParser(); + var cu = parser.Parse(new StringReader(code), "program.cs"); + Assert.IsFalse(parser.HasErrors); + return ConvertTypeDeclaration((EntityDeclaration)cu.Children.Single()); + } + #endregion + + #region Type References [Test] - public void CreateArray() + public void MultidimensionalArrayTypeReference() { - Assert.AreEqual("new int[10]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10) } - })); + Assert.AreEqual("default(int[,][])", ConvertExpression("default(int[,][])")); } [Test] - public void CreateJaggedArray() + public void NestedTypeInGenericType() { - Assert.AreEqual("new int[10][]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10) }, - AdditionalArraySpecifiers = { new ArraySpecifier() } - })); + Assert.AreEqual("default(System.Collections.Generic.List.Enumerator)", + ConvertExpression("default(List.Enumerator)")); + convertVisitor.UseFullyQualifiedTypeNames = false; + Assert.AreEqual("default(List.Enumerator)", + ConvertExpression("default(List.Enumerator)")); } + #endregion + #region Arrays [Test] + public void CreateArray() + { + Assert.AreEqual("new int[10]", ConvertExpression("new int[10]")); + } + + [Test, ExpectedException(typeof(NotSupportedException))] + public void CreateJaggedArray() + { + ConvertExpression("new int[10][]"); + } + + [Test, ExpectedException(typeof(NotSupportedException))] public void Create2DArray() { - Assert.AreEqual("new int[10, 20]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10), new PrimitiveExpression(20) } - })); + ConvertExpression("new int[10, 20]"); } [Test] public void CreateImplicitlyTypedArray() { // implicitly-typed array not supported in CodeDom, so the conversion should infer the type - Assert.AreEqual("new int[] { 1, 2, 3}", Convert( - new ArrayCreateExpression { - AdditionalArraySpecifiers = { new ArraySpecifier() }, - Initializer = new ArrayInitializerExpression { - Elements = { - new PrimitiveExpression(1), - new PrimitiveExpression(2), - new PrimitiveExpression(3) - } - } - })); + Assert.AreEqual("new int[] { 1, 2, 3}", ConvertExpression("new [] { 1, 2, 3 }")); + Assert.AreEqual("new System.Collections.Generic.List[] { new System.Collections.Generic.List()}", + ConvertExpression("new [] { new List() }")); } - [Test] + [Test, ExpectedException(typeof(NotSupportedException))] public void Create2DImplicitlyTypedArray() { - Assert.AreEqual("new int[,] { { 1, 2 }, { 3, 4 }}", Convert( - new ArrayCreateExpression { - AdditionalArraySpecifiers = { new ArraySpecifier(2) }, - Initializer = new ArrayInitializerExpression { - Elements = { - new ArrayInitializerExpression(new PrimitiveExpression(1), new PrimitiveExpression(2)), - new ArrayInitializerExpression(new PrimitiveExpression(3), new PrimitiveExpression(4)) - } - } - })); + ConvertExpression("new [,] { { 1, 2 }, { 3, 4 }}"); } + #endregion + #region Operators [Test] - public void AdditionOperator() + public void ArithmeticOperators() { - Assert.AreEqual("(0 + 1)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.Add, new PrimitiveExpression(1)))); + Assert.AreEqual("(0 + 1)", ConvertExpression("0 + 1")); + Assert.AreEqual("(0 - 1)", ConvertExpression("0 - 1")); + Assert.AreEqual("(0 * 1)", ConvertExpression("0 * 1")); + Assert.AreEqual("(0 / 1)", ConvertExpression("0 / 1")); + Assert.AreEqual("(0 % 1)", ConvertExpression("0 % 1")); + Assert.AreEqual("(0 & 1)", ConvertExpression("0 & 1")); + Assert.AreEqual("(0 | 1)", ConvertExpression("0 | 1")); + Assert.AreEqual("(0 < 1)", ConvertExpression("0 < 1")); + Assert.AreEqual("(0 > 1)", ConvertExpression("0 > 1")); + Assert.AreEqual("(0 <= 1)", ConvertExpression("0 <= 1")); + Assert.AreEqual("(0 >= 1)", ConvertExpression("0 >= 1")); + Assert.AreEqual("(true && false)", ConvertExpression("true && false")); + Assert.AreEqual("(true || false)", ConvertExpression("true || false")); } [Test] public void EqualityOperator() { - Assert.AreEqual("(0 == 1)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.Equality, new PrimitiveExpression(1)))); + Assert.AreEqual("(0 == 1)", ConvertExpression("0 == 1")); + Assert.AreEqual("(default(object) == null)", ConvertExpression("default(object) == null")); } [Test] public void InEqualityOperator() { - Assert.AreEqual("((0 == 1) == false)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.InEquality, new PrimitiveExpression(1)))); + Assert.AreEqual("((0 == 1) == false)", ConvertExpression("0 != 1")); + Assert.AreEqual("(default(object) != null)", ConvertExpression("default(object) != null")); } [Test] - public void ReferenceInEqualityOperator() + public void UnaryOperators() { - Assert.AreEqual("(default(object) != null)", Convert( - new BinaryOperatorExpression(new DefaultValueExpression(new PrimitiveType("object")), BinaryOperatorType.InEquality, new NullReferenceExpression()))); + Assert.AreEqual("(a == false)", ConvertExpression("!a")); + Assert.AreEqual("(0 - a)", ConvertExpression("-a")); + Assert.AreEqual("a", ConvertExpression("+a")); } + [Test] + public void Cast() + { + Assert.AreEqual("((double)(0))", ConvertExpression("(double)0")); + } + #endregion + + #region Member Access [Test] public void StaticProperty() { - Assert.AreEqual("System.Environment.TickCount", Convert( - new IdentifierExpression("Environment").Member("TickCount"))); + Assert.AreEqual("System.Environment.TickCount", ConvertExpression("Environment.TickCount")); } [Test] public void InstanceMethodInvocation() { - Assert.AreEqual("this.Equals(null)", - Convert(new IdentifierExpression("Equals").Invoke(new NullReferenceExpression()))); + Assert.AreEqual("this.Equals(null)", ConvertExpression("Equals(null)")); } [Test] public void StaticMethodInvocation() { - Assert.AreEqual("object.Equals(null, null)", - Convert(new IdentifierExpression("Equals").Invoke(new NullReferenceExpression(), new NullReferenceExpression()))); + Assert.AreEqual("object.Equals(null, null)", ConvertExpression("Equals(null, null)")); + } + + [Test] + public void BaseMemberAccess() + { + Assert.AreEqual("base.X", ConvertExpression("base.X")); + Assert.AreEqual("base[i]", ConvertExpression("base[i]")); + } + + [Test] + public void GenericMethodReference() + { + Assert.AreEqual("this.Stuff", ConvertExpression("this.Stuff")); + Assert.AreEqual("this.Stuff", ConvertExpression("Stuff")); + } + + [Test] + public void ByReferenceCall() + { + Assert.AreEqual("a.Call(ref x, out y, z)", ConvertExpression("a.Call(ref x, out y, z)")); + } + + [Test] + public void MemberAccessOnType() + { + Assert.AreEqual("string.Empty", ConvertExpression("string.Empty")); + } + #endregion + + #region Statements + [Test] + public void MethodInvocationStatement() + { + Assert.AreEqual("a.SomeMethod();", ConvertStatement("a.SomeMethod();")); + } + + [Test] + public void Assignment() + { + Assert.AreEqual("a = 1;", ConvertStatement("a = 1;")); + } + + [Test, ExpectedException(typeof(NotSupportedException))] + public void AssignmentNotSupportedInExpression() + { + ConvertStatement("a = b = 1;"); + } + + [Test] + public void BlockStatement() + { + Assert.AreEqual("if (true) { a = 1; b = 2; }", + ConvertStatement("{ a = 1; b = 2; }")); + } + + [Test] + public void CompoundAssign() + { + Assert.AreEqual("a = (a + 1);", ConvertStatement("a += 1;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("a -= 1;")); + Assert.AreEqual("a = (a * 1);", ConvertStatement("a *= 1;")); + Assert.AreEqual("a = (a / 1);", ConvertStatement("a /= 1;")); + Assert.AreEqual("a = (a % 1);", ConvertStatement("a %= 1;")); + Assert.AreEqual("a = (a & 1);", ConvertStatement("a &= 1;")); + Assert.AreEqual("a = (a | 1);", ConvertStatement("a |= 1;")); + } + + [Test] + public void Increment() + { + Assert.AreEqual("a = (a + 1);", ConvertStatement("a++;")); + Assert.AreEqual("a = (a + 1);", ConvertStatement("++a;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("a--;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("--a;")); + } + + [Test] + public void ForLoop() + { + Assert.AreEqual("for (int i = 0; (i < 10); i = (i + 1)) { }", + ConvertStatement("for (int i = 0; i < 10; i++) {}")); + } + + [Test] + public void WhileLoop() + { + Assert.AreEqual("for (new object(); (i < 10); new object()) { }", + ConvertStatement("while (i < 10);")); + } + + [Test] + public void VariableDeclarationWithArrayInitializer() + { + Assert.AreEqual("int[] nums = new int[] { 1, 2};", + ConvertStatement("int[] nums = { 1, 2 };")); + } + + [Test] + public void TryCatch() + { + Assert.AreEqual("try { a = 1; } catch (System.Exception ex) { ex.ToString(); }", + ConvertStatement("try { a = 1; } catch (Exception ex) { ex.ToString(); }")); + } + + [Test] + public void TryEmptyCatch() + { + Assert.AreEqual("try { a = 1; } catch (System.Exception ) { }", + ConvertStatement("try { a = 1; } catch (Exception) { }")); + } + + [Test] + public void TryFinally() + { + Assert.AreEqual("try { a = 1; } finally { a = 0; }", + ConvertStatement("try { a = 1; } finally { a = 0; }")); + } + #endregion + + #region Type Members + [Test] + public void MethodParameterNamedValue() + { + Assert.AreEqual("void M(string value) { System.Console.WriteLine(value); }", + ConvertMember("void M(string value) { Console.WriteLine(value); }")); + } + + [Test] + public void ValueInProperty() + { + Assert.AreEqual("string P { set { System.Console.WriteLine(value); } }", + ConvertMember("string P { set { Console.WriteLine(value); } }")); + } + + [Test] + public void MethodWithAttribute() + { + Assert.AreEqual("[Test()] void MethodWithAttribute() { }", + ConvertMember("[Test] void MethodWithAttribute() { }")); + } + + [Test] + public void PublicNonVirtualMethod() + { + Assert.AreEqual("public void Method() { }", + ConvertMember("public void Method() { }")); + } + + [Test] + public void PublicVirtualMethod() + { + Assert.AreEqual("public virtual void Method() { }", + ConvertMember("public virtual void Method() { }")); + } + + [Test] + public void NestedClass() + { + Assert.AreEqual("public class Outer { public class Inner { } }", + ConvertTypeDeclaration("class Outer { class Inner { } }")); + } + + [Test] + public void Constructor() + { + string code = "public class Test : Base { public Test(string x) : base(x) { } }"; + Assert.AreEqual(code, ConvertTypeDeclaration(code)); + } + + [Test] + public void Enum() + { + string code = "public enum E { [Description(\"Text\")] None, B = 2, }"; + Assert.AreEqual(code, ConvertTypeDeclaration(code)); + } + + [Test] + public void Field() + { + Assert.AreEqual("public class X {" + + " int A;" + + " int B; }", + ConvertMember("public class X { int A, B; }")); } [Test] - public void NotOperator() + public void Event() { - Assert.AreEqual("(a == false)", Convert(new UnaryOperatorExpression(UnaryOperatorType.Not, new IdentifierExpression("a")))); + Assert.AreEqual("public class X {" + + " protected event System.EventHandler A;" + + " protected event System.EventHandler B; }", + ConvertMember("public class X { protected event EventHandler A, B; }")); } + #endregion } }