#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
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.
 
 
 
 
 
 

486 lines
15 KiB

// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
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 Microsoft.CSharp;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp
{
#if !__MonoCS__
[TestFixture]
public class CodeDomConvertVisitorTests : ResolverTestBase
{
CodeDomConvertVisitor convertVisitor;
CSharpUnresolvedFile unresolvedFile;
public override void SetUp()
{
base.SetUp();
unresolvedFile = new CSharpUnresolvedFile();
unresolvedFile.RootUsingScope.Usings.Add(MakeReference("System"));
unresolvedFile.RootUsingScope.Usings.Add(MakeReference("System.Collections.Generic"));
unresolvedFile.RootUsingScope.Usings.Add(MakeReference("System.Linq"));
convertVisitor = new CodeDomConvertVisitor();
convertVisitor.AllowSnippetNodes = false;
convertVisitor.UseFullyQualifiedTypeNames = true;
}
#region Helper methods
CodeObject ConvertInContext(string program)
{
var p = PrepareResolver(program);
return convertVisitor.Convert(p.Item2, p.Item1);
}
string ConvertHelper(AstNode node, Action<CSharpCodeProvider, CodeObject, TextWriter, CodeGeneratorOptions> action)
{
CSharpResolver resolver = new CSharpResolver(compilation);
resolver = resolver.WithCurrentUsingScope(unresolvedFile.RootUsingScope.Resolve(compilation));
resolver = resolver.WithCurrentTypeDefinition(compilation.FindType(KnownTypeCode.Object).GetDefinition());
var codeObj = convertVisitor.Convert(node, new CSharpAstResolver(resolver, node));
StringWriter writer = new StringWriter();
writer.NewLine = " ";
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(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(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(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 syntaxTree = parser.Parse(code, "program.cs");
Assert.IsFalse(parser.HasErrors);
return ConvertTypeDeclaration((EntityDeclaration)syntaxTree.Children.Single());
}
string ConvertSyntaxTree(SyntaxTree syntaxTree)
{
return ConvertHelper(syntaxTree, (p,obj,w,opt) => p.GenerateCodeFromCompileUnit((CodeCompileUnit)obj, w, opt));
}
string ConvertSyntaxTree(string code)
{
CSharpParser parser = new CSharpParser();
var syntaxTree = parser.Parse(code, "program.cs");
Assert.IsFalse(parser.HasErrors);
var result = ConvertSyntaxTree(syntaxTree);
var idx = result.IndexOf("namespace", StringComparison.Ordinal);
if (idx > 0)
result = result.Substring (idx);
return result;
}
#endregion
#region Type References
[Test]
public void MultidimensionalArrayTypeReference()
{
Assert.AreEqual("default(int[,][])", ConvertExpression("default(int[,][])"));
}
[Test]
public void NestedTypeInGenericType()
{
Assert.AreEqual("default(System.Collections.Generic.List<string>.Enumerator)",
ConvertExpression("default(List<string>.Enumerator)"));
convertVisitor.UseFullyQualifiedTypeNames = false;
Assert.AreEqual("default(List<string>.Enumerator)",
ConvertExpression("default(List<string>.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()
{
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}", ConvertExpression("new [] { 1, 2, 3 }"));
Assert.AreEqual("new System.Collections.Generic.List<string>[] { new System.Collections.Generic.List<string>()}",
ConvertExpression("new [] { new List<string>() }"));
}
[Test, ExpectedException(typeof(NotSupportedException))]
public void Create2DImplicitlyTypedArray()
{
ConvertExpression("new [,] { { 1, 2 }, { 3, 4 }}");
}
#endregion
#region Operators
[Test]
public void ArithmeticOperators()
{
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)", ConvertExpression("0 == 1"));
Assert.AreEqual("(default(object) == null)", ConvertExpression("default(object) == null"));
}
[Test]
public void InEqualityOperator()
{
Assert.AreEqual("((0 == 1) == false)", ConvertExpression("0 != 1"));
Assert.AreEqual("(default(object) != null)", ConvertExpression("default(object) != null"));
}
[Test]
public void UnaryOperators()
{
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", ConvertExpression("Environment.TickCount"));
}
[Test]
public void FullyQualifiedEnumFieldAccess()
{
string program = "class A { object x = $System.StringComparison.Ordinal$; }";
var cfre = (CodeFieldReferenceExpression)ConvertInContext(program);
Assert.AreEqual("Ordinal", cfre.FieldName);
var ctre = ((CodeTypeReferenceExpression)cfre.TargetObject);
Assert.AreEqual("System.StringComparison", ctre.Type.BaseType);
}
[Test]
public void EnumFieldAccess()
{
string program = "using System; class A { object x = $StringComparison.Ordinal$; }";
var cfre = (CodeFieldReferenceExpression)ConvertInContext(program);
Assert.AreEqual("Ordinal", cfre.FieldName);
var ctre = ((CodeTypeReferenceExpression)cfre.TargetObject);
Assert.AreEqual("System.StringComparison", ctre.Type.BaseType);
}
[Test]
public void InstanceMethodInvocation()
{
Assert.AreEqual("this.Equals(null)", ConvertExpression("Equals(null)"));
}
[Test]
public void StaticMethodInvocation()
{
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<string>", ConvertExpression("this.Stuff<string>"));
Assert.AreEqual("this.Stuff<string>", ConvertExpression("Stuff<string>"));
}
[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 Event()
{
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
#region SyntaxTrees
[Test]
public void TestGlobalNamespaceFix ()
{
Assert.AreEqual("namespace A { using System; public class AClass { } } namespace B { using System.IO; using System; public class AClass { } }",
ConvertSyntaxTree("using System; namespace A { public class AClass {} } namespace B { using System.IO; public class AClass {} }"));
}
#endregion
}
#endif
}