diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj index a0c7024ec6..66b2770618 100644 --- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj +++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -169,6 +169,7 @@ + diff --git a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs new file mode 100644 index 0000000000..d64ea6a15f --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs @@ -0,0 +1,243 @@ +// Copyright (c) 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.IO; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# ambience. + /// + public class CSharpAmbience : IAmbience + { + public ConversionFlags ConversionFlags { get; set; } + + #region ConvertEntity + public string ConvertEntity(IEntity e, ITypeResolveContext context) + { + using (var ctx = context.Synchronize()) { + StringWriter writer = new StringWriter(); + + if (e.EntityType == EntityType.TypeDefinition) { + ConvertTypeDeclaration((ITypeDefinition)e, ctx, writer); + } else { + ConvertMember((IMember)e, ctx, writer); + } + + return writer.ToString().TrimEnd(); + } + } + + void ConvertMember(IMember member, ISynchronizedTypeResolveContext ctx, StringWriter writer) + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + + AttributedNode node = (AttributedNode)astBuilder.ConvertEntity(member); + PrintModifiers(node.Modifiers, writer); + + if ((ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { + var rt = node.GetChildByRole(AstNode.Roles.Type); + if (rt != AstNode.Roles.Type.NullObject) { + writer.Write(rt.AcceptVisitor(CreatePrinter(writer), null)); + writer.Write(' '); + } + } + + WriteMemberDeclarationName(member, ctx, writer); + + if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList + && member is IParameterizedMember && member.EntityType != EntityType.Property) { + writer.Write((node is IndexerDeclaration) ? '[' : '('); + bool first = true; + foreach (var param in node.GetChildrenByRole(AstNode.Roles.Parameter)) { + if (first) + first = false; + else + writer.Write(", "); + param.AcceptVisitor(CreatePrinter(writer), null); + } + writer.Write((node is IndexerDeclaration) ? ']' : ')'); + } + } + + void ConvertTypeDeclaration(ITypeDefinition typeDef, ITypeResolveContext ctx, StringWriter writer) + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + TypeDeclaration typeDeclaration = (TypeDeclaration)astBuilder.ConvertEntity(typeDef); + PrintModifiers(typeDeclaration.Modifiers, writer); + if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyWord) == ConversionFlags.ShowDefinitionKeyWord) { + switch (typeDeclaration.ClassType) { + case ClassType.Class: + writer.Write("class"); + break; + case ClassType.Struct: + writer.Write("struct"); + break; + case ClassType.Interface: + writer.Write("interface"); + break; + case ClassType.Enum: + writer.Write("enum"); + break; + default: + throw new Exception("Invalid value for ClassType"); + } + writer.Write(' '); + } + WriteTypeDeclarationName(typeDef, ctx, writer); + } + + void WriteTypeDeclarationName(ITypeDefinition typeDef, ITypeResolveContext ctx, StringWriter writer) + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + if (typeDef.DeclaringTypeDefinition != null) { + WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, ctx, writer); + writer.Write('.'); + } else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedMemberNames) == ConversionFlags.UseFullyQualifiedMemberNames) { + writer.Write(typeDef.Namespace); + writer.Write('.'); + } + writer.Write(typeDef.Name); + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) { + CreatePrinter(writer).WriteTypeParameters(((TypeDeclaration)astBuilder.ConvertEntity(typeDef)).TypeParameters); + } + } + + void WriteMemberDeclarationName(IMember member, ITypeResolveContext ctx, StringWriter writer) + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + if ((ConversionFlags & ConversionFlags.UseFullyQualifiedMemberNames) == ConversionFlags.UseFullyQualifiedMemberNames) { + writer.Write(ConvertType(member.DeclaringType)); + writer.Write('.'); + } + switch (member.EntityType) { + case EntityType.Indexer: + writer.Write("this"); + break; + case EntityType.Constructor: + writer.Write(member.DeclaringType.Name); + break; + case EntityType.Destructor: + writer.Write('~'); + writer.Write(member.DeclaringType.Name); + break; + case EntityType.Operator: + switch (member.Name) { + case "op_Implicit": + writer.Write("implicit operator "); + writer.Write(ConvertType(member.ReturnType, ctx)); + break; + case "op_Explicit": + writer.Write("explicit operator "); + writer.Write(ConvertType(member.ReturnType, ctx)); + break; + default: + writer.Write("operator "); + var operatorType = OperatorDeclaration.GetOperatorType(member.Name); + if (operatorType.HasValue) + writer.Write(OperatorDeclaration.GetToken(operatorType.Value)); + else + writer.Write(member.Name); + break; + } + break; + default: + writer.Write(member.Name); + break; + } + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList && member.EntityType == EntityType.Method) { + CreatePrinter(writer).WriteTypeParameters(astBuilder.ConvertEntity(member).GetChildrenByRole(AstNode.Roles.TypeParameter)); + } + } + + OutputVisitor CreatePrinter(StringWriter writer) + { + return new OutputVisitor(writer, new CSharpFormattingOptions()); + } + + void PrintModifiers(Modifiers modifiers, StringWriter writer) + { + foreach (var m in CSharpModifierToken.AllModifiers) { + if ((modifiers & m) == m) { + writer.Write(CSharpModifierToken.GetModifierName(m)); + writer.Write(' '); + } + } + } + #endregion + + public string ConvertVariable(IVariable v, ITypeResolveContext context) + { + using (var ctx = context.Synchronize()) { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + AstNode astNode = astBuilder.ConvertVariable(v); + CSharpFormattingOptions formatting = new CSharpFormattingOptions(); + StringWriter writer = new StringWriter(); + astNode.AcceptVisitor(new OutputVisitor(writer, formatting), null); + return writer.ToString().TrimEnd(';', '\r', '\n'); + } + } + + public string ConvertType(IType type) + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(MinimalResolveContext.Instance); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + AstType astType = astBuilder.ConvertType(type); + CSharpFormattingOptions formatting = new CSharpFormattingOptions(); + StringWriter writer = new StringWriter(); + astType.AcceptVisitor(new OutputVisitor(writer, formatting), null); + return writer.ToString(); + } + + public string ConvertType(ITypeReference type, ITypeResolveContext context) + { + using (var ctx = context.Synchronize()) { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(ctx); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + AstType astType = astBuilder.ConvertTypeReference(type); + CSharpFormattingOptions formatting = new CSharpFormattingOptions(); + StringWriter writer = new StringWriter(); + astType.AcceptVisitor(new OutputVisitor(writer, formatting), null); + return writer.ToString(); + } + } + + public string WrapAttribute(string attribute) + { + return "[" + attribute + "]"; + } + + public string WrapComment(string comment) + { + return "// " + comment; + } + } +} diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs new file mode 100644 index 0000000000..d2ac4c698a --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs @@ -0,0 +1,235 @@ +// Copyright (c) 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.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using NUnit.Framework; + +namespace ICSharpCode.NRefactory.CSharp +{ + [TestFixture] + public class CSharpAmbienceTests + { + IProjectContent mscorlib; + IProjectContent myLib; + CompositeTypeResolveContext compositeContext; + CSharpAmbience ambience; + + public CSharpAmbienceTests() + { + ambience = new CSharpAmbience(); + mscorlib = CecilLoaderTests.Mscorlib; + var loader = new CecilLoader(); + loader.IncludeInternalMembers = true; + myLib = loader.LoadAssemblyFile(typeof(CSharpAmbienceTests).Assembly.Location); + compositeContext = new CompositeTypeResolveContext(new[] { mscorlib, myLib }); + } + + #region ITypeDefinition tests + [Test] + public void GenericType() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Dictionary<,>)); + ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedMemberNames | ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("System.Collections.Generic.Dictionary", result); + } + + [Test] + public void GenericTypeShortName() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Dictionary<,>)); + ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("Dictionary", result); + } + + [Test] + public void SimpleType() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Object)); + ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedMemberNames | ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("System.Object", result); + } + + [Test] + public void SimpleTypeDefinition() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Object)); + ambience.ConversionFlags = ConversionFlags.All & ~(ConversionFlags.UseFullyQualifiedMemberNames); + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("public class Object", result); + } + + [Test] + public void SimpleTypeDefinitionWithoutModifiers() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Object)); + ambience.ConversionFlags = ConversionFlags.All & ~(ConversionFlags.UseFullyQualifiedMemberNames | ConversionFlags.ShowModifiers | ConversionFlags.ShowAccessibility); + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("class Object", result); + } + + [Test] + public void GenericTypeDefinitionFull() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(List<>)); + ambience.ConversionFlags = ConversionFlags.All; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("public class System.Collections.Generic.List", result); + } + + [Test] + public void SimpleTypeShortName() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(Object)); + ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("Object", result); + } + + [Test] + public void GenericTypeWithNested() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(List<>.Enumerator)); + ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedMemberNames | ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("System.Collections.Generic.List.Enumerator", result); + } + + [Test] + public void GenericTypeWithNestedShortName() + { + var typeDef = mscorlib.GetTypeDefinition(typeof(List<>.Enumerator)); + ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; + string result = ambience.ConvertEntity(typeDef, mscorlib); + + Assert.AreEqual("List.Enumerator", result); + } + #endregion + + #region IField tests + [Test] + public void SimpleField() + { + var field = typeof(CSharpAmbienceTests.Program).ToTypeReference().Resolve(myLib) + .GetDefinition().Fields.Single(f => f.Name == "test"); + ambience.ConversionFlags = ConversionFlags.All; + string result = ambience.ConvertEntity(field, compositeContext); + + Assert.AreEqual("private int ICSharpCode.SharpDevelop.Tests.CSharpAmbienceTests.Program.test", result); + } + + [Test] + public void SimpleConstField() + { + var field = typeof(CSharpAmbienceTests.Program).ToTypeReference().Resolve(myLib) + .GetDefinition().Fields.Single(f => f.Name == "TEST2"); + ambience.ConversionFlags = ConversionFlags.All; + string result = ambience.ConvertEntity(field, compositeContext); + + Assert.AreEqual("private const int ICSharpCode.SharpDevelop.Tests.CSharpAmbienceTests.Program.TEST2", result); + } + + [Test] + public void SimpleFieldWithoutModifiers() + { + var field = typeof(CSharpAmbienceTests.Program).ToTypeReference().Resolve(myLib) + .GetDefinition().Fields.Single(f => f.Name == "test"); + ambience.ConversionFlags = ConversionFlags.All & ~(ConversionFlags.UseFullyQualifiedMemberNames | ConversionFlags.ShowModifiers | ConversionFlags.ShowAccessibility); + string result = ambience.ConvertEntity(field, compositeContext); + + Assert.AreEqual("int test", result); + } + #endregion + + #region Test types + #pragma warning disable 169 + + class Test {} + + class Program + { + int test; + const int TEST2 = 2; + + public int Test { get; set; } + + public int this[int index] { + get { + return index; + } + } + + public event EventHandler ProgramChanged; + + public event EventHandler SomeEvent { + add {} + remove {} + } + + public static bool operator +(Program lhs, Program rhs) + { + throw new NotImplementedException(); + } + + public static implicit operator Test(Program lhs) + { + throw new NotImplementedException(); + } + + public static explicit operator int(Program lhs) + { + throw new NotImplementedException(); + } + + public Program(int x) + { + + } + + ~Program() + { + + } + + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + + // TODO: Implement Functionality Here + + Console.Write("Press any key to continue . . . "); + Console.ReadKey(true); + } + } + #endregion + } +} diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 9e86061bc0..8eff5fbb2f 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -78,6 +78,7 @@ + diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index bcd9a9b4d7..e614362884 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -103,6 +103,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/IAmbience.cs b/ICSharpCode.NRefactory/TypeSystem/IAmbience.cs new file mode 100644 index 0000000000..44ac8fc5ad --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/IAmbience.cs @@ -0,0 +1,94 @@ +// Copyright (c) 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; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + [Flags] + public enum ConversionFlags + { + /// + /// Convert only the name. + /// + None = 0, + /// + /// Show the parameter list + /// + ShowParameterList = 1, + /// + /// Show names for parameters + /// + ShowParameterNames = 2, + /// + /// Show the accessibility (private, public, etc.) + /// + ShowAccessibility = 4, + /// + /// Show the definition key word (class, struct, Sub, Function, etc.) + /// + ShowDefinitionKeyWord = 8, + /// + /// Show the fully qualified name for the member + /// + UseFullyQualifiedMemberNames = 0x10, + /// + /// Show modifiers (virtual, override, etc.) + /// + ShowModifiers = 0x20, + /// + /// Show the return type + /// + ShowReturnType = 0x100, + /// + /// Use fully qualified names for return type and parameters. + /// + UseFullyQualifiedTypeNames = 0x200, + /// + /// Show the list of type parameters on method and class declarations. + /// Type arguments for parameter/return types are always shown. + /// + ShowTypeParameterList = 0x800, + + StandardConversionFlags = ShowParameterNames | + ShowAccessibility | + ShowParameterList | + ShowReturnType | + ShowModifiers | + ShowTypeParameterList | + ShowDefinitionKeyWord, + + All = 0xfff, + } + + public interface IAmbience + { + ConversionFlags ConversionFlags { + get; + set; + } + + string ConvertEntity(IEntity e, ITypeResolveContext context); + string ConvertType(IType type); + string ConvertType(ITypeReference type, ITypeResolveContext context); + string ConvertVariable(IVariable variable, ITypeResolveContext context); + + string WrapAttribute(string attribute); + string WrapComment(string comment); + } +}