diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index e5c2a8b6fe..0d029b2b1d 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -2,6 +2,10 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; + +[assembly: ICSharpCode.NRefactory.TypeSystem.TestCase.TypeTestAttribute( + 42, typeof(System.Action<>), typeof(IDictionary>))] namespace ICSharpCode.NRefactory.TypeSystem.TestCase { @@ -9,4 +13,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase { public void Method() {} } + + public class TypeTestAttribute : Attribute + { + public TypeTestAttribute(int a1, Type a2, Type a3) {} + } + + public class DynamicTest + { + public List DynamicGenerics1(Action param) { return null; } + public void DynamicGenerics2(Action param) { } + public void DynamicGenerics3(Action param) { } + public void DynamicGenerics4(Action param) { } + public void DynamicGenerics5(Action param) { } + } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 3f75a9b334..c3c41869ba 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -41,7 +41,27 @@ namespace ICSharpCode.NRefactory.TypeSystem IMethod method = c.Methods.Single(m => m.Name == "Method"); Assert.AreEqual(typeof(SimplePublicClass).FullName + ".Method", method.FullName); Assert.AreSame(c, method.DeclaringType); - + Assert.AreEqual(Accessibility.Public, method.Accessibility); + Assert.AreEqual(EntityType.Method, method.EntityType); + Assert.IsFalse(method.IsVirtual); + Assert.IsFalse(method.IsStatic); + Assert.IsTrue(method.IsFrozen); + Assert.AreEqual(0, method.Parameters.Count); + Assert.AreEqual(0, method.Attributes.Count); + } + + [Test] + [Ignore] + public void DynamicTypeInGenerics() + { + ITypeDefinition testClass = testCasePC.GetClass(typeof(DynamicTest).FullName, 0, StringComparer.Ordinal); + /*CSharpAmbience a = new CSharpAmbience(); + a.ConversionFlags = ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList; + Assert.AreEqual("List DynamicGenerics1(Action)", a.Convert(testClass.Methods.Single(me => me.Name == "DynamicGenerics1"))); + Assert.AreEqual("void DynamicGenerics2(Action)", a.Convert(testClass.Methods.Single(me => me.Name == "DynamicGenerics2"))); + Assert.AreEqual("void DynamicGenerics3(Action)", a.Convert(testClass.Methods.Single(me => me.Name == "DynamicGenerics3"))); + Assert.AreEqual("void DynamicGenerics4(Action)", a.Convert(testClass.Methods.Single(me => me.Name == "DynamicGenerics4"))); + Assert.AreEqual("void DynamicGenerics5(Action)", a.Convert(testClass.Methods.Single(me => me.Name == "DynamicGenerics5")));*/ } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 5ebd511ba5..97ed9a7c61 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; - +using System.Runtime.CompilerServices; using ICSharpCode.NRefactory.TypeSystem.Implementation; using Mono.Cecil; @@ -40,7 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem ITypeResolveContext oldEarlyBindContext = this.EarlyBindContext; try { List assemblyAttributes = new List(); - ReadAttributes(assemblyDefinition, assemblyAttributes); + AddAttributes(assemblyDefinition, assemblyAttributes); TypeStorage typeStorage = new TypeStorage(); CecilProjectContent pc = new CecilProjectContent(typeStorage, assemblyDefinition.Name.FullName, assemblyAttributes.AsReadOnly()); @@ -181,26 +181,23 @@ namespace ICSharpCode.NRefactory.TypeSystem return PointerTypeReference.Create( CreateType( (type as Mono.Cecil.PointerType).ElementType, - entity, - typeAttributes, ref typeIndex)); + entity, typeAttributes, ref typeIndex)); } else if (type is Mono.Cecil.ArrayType) { typeIndex++; return ArrayTypeReference.Create( CreateType( (type as Mono.Cecil.ArrayType).ElementType, - entity, - typeAttributes, ref typeIndex), + entity, typeAttributes, ref typeIndex), (type as Mono.Cecil.ArrayType).Rank); } else if (type is GenericInstanceType) { GenericInstanceType gType = (GenericInstanceType)type; - /*IReturnType baseType = CreateType(pc, member, gType.ElementType, attributeProvider, ref typeIndex); - IReturnType[] para = new IReturnType[gType.GenericArguments.Count]; + ITypeReference baseType = CreateType(gType.ElementType, entity, typeAttributes, ref typeIndex); + ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count]; for (int i = 0; i < para.Length; ++i) { typeIndex++; - para[i] = CreateType(pc, member, gType.GenericArguments[i], attributeProvider, ref typeIndex); + para[i] = CreateType(gType.GenericArguments[i], entity, typeAttributes, ref typeIndex); } - return new ConstructedReturnType(baseType, para);*/ - throw new NotImplementedException(); + return ConstructedType.Create(baseType, para); } else if (type is GenericParameter) { GenericParameter typeGP = type as GenericParameter; if (typeGP.Owner is MethodDefinition) { @@ -303,7 +300,7 @@ namespace ICSharpCode.NRefactory.TypeSystem #endregion #region Read Attributes - void ReadAttributes(ICustomAttributeProvider attributeProvider, IList outputList) + void AddAttributes(ICustomAttributeProvider attributeProvider, IList outputList) { foreach (var cecilAttribute in attributeProvider.CustomAttributes) { outputList.Add(ReadAttribute(cecilAttribute)); @@ -422,7 +419,7 @@ namespace ICSharpCode.NRefactory.TypeSystem InitNestedTypes(loader); // nested types can be initialized only after generic parameters were created if (typeDefinition.HasCustomAttributes) { - loader.ReadAttributes(typeDefinition, this.Attributes); + loader.AddAttributes(typeDefinition, this.Attributes); } // set base classes @@ -538,19 +535,168 @@ namespace ICSharpCode.NRefactory.TypeSystem void InitMembers(CecilLoader loader) { + this.AddDefaultConstructorIfRequired = (this.ClassType == ClassType.Struct || this.ClassType == ClassType.Enum); if (typeDefinition.HasMethods) { foreach (MethodDefinition method in typeDefinition.Methods) { - this.Methods.Add(loader.ReadMethod(method, this)); + if (loader.IsVisible(method.Attributes)) { + EntityType type = EntityType.Method; + if (method.IsSpecialName) { + if (method.IsConstructor) + type = EntityType.Constructor; + else if (method.Name.StartsWith("op_", StringComparison.Ordinal)) + type = EntityType.Operator; + else + continue; + } + this.Methods.Add(loader.ReadMethod(method, this, type)); + } } } } } #endregion - IMethod ReadMethod(MethodDefinition method, ITypeDefinition parentType) + #region ReadMethod + IMethod ReadMethod(MethodDefinition method, ITypeDefinition parentType, EntityType methodType) { DefaultMethod m = new DefaultMethod(parentType, method.Name); + m.EntityType = methodType; + if (method.HasGenericParameters) { + throw new NotImplementedException(); + /*foreach (GenericParameter g in method.GenericParameters) { + m.TypeParameters.Add(new DefaultTypeParameter(this, g.Name, g.Position)); + } + int i = 0; + foreach (GenericParameter g in method.GenericParameters) { + AddConstraintsFromType(m.TypeParameters[i++], g); + }*/ + } + + if (method.IsConstructor) + m.ReturnType = parentType; + else + m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method, entity: m); + + if (method.HasCustomAttributes) { + AddAttributes(method, m.Attributes); + } + + if (parentType.ClassType == ClassType.Interface) { + // interface members don't have modifiers, but we want to handle them as "public abstract" + m.Accessibility = Accessibility.Public; + m.IsAbstract = true; + } else { + TranslateModifiers(method, m); + } + + if (method.HasParameters) { + foreach (ParameterDefinition p in method.Parameters) { + m.Parameters.Add(ReadParameter(p, parentMember: m)); + } + } + + AddExplicitInterfaceImplementations(method, m); + + // mark as extension method is the attribute is set + if (method.IsStatic && method.HasCustomAttributes) { + foreach (var attr in method.CustomAttributes) { + if (attr.AttributeType.FullName == typeof(ExtensionAttribute).FullName) + m.IsExtensionMethod = true; + } + } + return m; } + + bool IsVisible(MethodAttributes att) + { + att &= MethodAttributes.MemberAccessMask; + return IncludeInternalMembers + || att == MethodAttributes.Public + || att == MethodAttributes.Family + || att == MethodAttributes.FamORAssem; + } + + Accessibility GetAccessibility(MethodAttributes attr) + { + switch (attr & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Private: + return Accessibility.Private; + case MethodAttributes.FamANDAssem: + return Accessibility.ProtectedAndInternal; + case MethodAttributes.Assem: + return Accessibility.Internal; + case MethodAttributes.Family: + return Accessibility.Protected; + case MethodAttributes.FamORAssem: + return Accessibility.ProtectedOrInternal; + default: + return Accessibility.Public; + } + } + + void TranslateModifiers(MethodDefinition method, AbstractMember member) + { + member.Accessibility = GetAccessibility(method.Attributes); + if (method.IsAbstract) + member.IsAbstract = true; + else if (method.IsFinal) + member.IsSealed = true; + else if (method.IsVirtual) + member.IsVirtual = true; + } + #endregion + + bool IsVisible(FieldAttributes att) + { + att &= FieldAttributes.FieldAccessMask; + return IncludeInternalMembers + || att == FieldAttributes.Public + || att == FieldAttributes.Family + || att == FieldAttributes.FamORAssem; + } + + #region ReadParameter + public IParameter ReadParameter(ParameterDefinition parameter, IParameterizedMember parentMember = null) + { + if (parameter == null) + throw new ArgumentNullException("parameter"); + DefaultParameter p = new DefaultParameter(); + p.Name = parameter.Name; + p.Type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, entity: parentMember); + + if (parameter.HasCustomAttributes) + AddAttributes(parameter, p.Attributes); + + if (parameter.ParameterType is ByReferenceType) { + if (parameter.IsOut) + p.IsOut = true; + else + p.IsRef = true; + } + + if (parameter.IsOptional) { + p.DefaultValue = ReadConstantValue(new CustomAttributeArgument(parameter.ParameterType, parameter.Constant)); + } + + if (parameter.ParameterType is Mono.Cecil.ArrayType) { + foreach (CustomAttribute att in parameter.CustomAttributes) { + if (att.AttributeType.FullName == typeof(ParamArrayAttribute).FullName) { + p.IsParams = true; + break; + } + } + } + + return p; + } + #endregion + + void AddExplicitInterfaceImplementations(MethodDefinition method, AbstractMember targetMember) + { + if (method.HasOverrides) { + throw new NotImplementedException(); + } + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs index 8da5cca030..b6439a094c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractMember.cs @@ -244,5 +244,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public virtual string DotNetName { get { return this.DeclaringType.DotNetName + "." + this.Name; } } + + public override string ToString() + { + return "[" + EntityType + " " + DotNetName + ":" + ReturnType + "]"; + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/ConstructedType.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/ConstructedType.cs index 4e02302229..8cd4bb0ff9 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/ConstructedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/ConstructedType.cs @@ -174,6 +174,25 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } return hashCode; } + + public static ITypeReference Create(ITypeReference genericType, IEnumerable typeArguments) + { + if (genericType == null) + throw new ArgumentNullException("genericType"); + if (typeArguments == null) + throw new ArgumentNullException("typeArguments"); + + ITypeReference[] typeArgs = typeArguments.ToArray(); + if (genericType is ITypeDefinition && Array.TrueForAll(typeArgs, t => t is IType)) { + IType[] ta = new IType[typeArgs.Length]; + for (int i = 0; i < ta.Length; i++) { + ta[i] = (IType)typeArgs[i]; + } + return new ConstructedType((ITypeDefinition)genericType, ta); + } else { + return new ConstructedTypeReference(genericType, typeArgs); + } + } } /// @@ -226,5 +245,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } return new ConstructedType(baseTypeDef, resolvedTypes); } + + public override string ToString() + { + StringBuilder b = new StringBuilder(genericType.ToString()); + b.Append('['); + for (int i = 0; i < typeArguments.Length; i++) { + if (i > 0) + b.Append(','); + b.Append('['); + b.Append(typeArguments[i].ToString()); + b.Append(']'); + } + b.Append(']'); + return b.ToString(); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMethod.cs index 903b968721..17243d0756 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMethod.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Text; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { @@ -73,5 +74,25 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return parameters; } } + + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(EntityType.ToString()); + b.Append(' '); + b.Append(DeclaringType.Name); + b.Append('.'); + b.Append(Name); + b.Append('('); + var p = this.Parameters; + for (int i = 0; i < p.Count; i++) { + if (i > 0) b.Append(", "); + b.Append(p[i].ToString()); + } + b.Append("):"); + b.Append(ReturnType.ToString()); + b.Append(']'); + return b.ToString(); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs index 532a00db9c..e9304a80fe 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Text; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { @@ -133,5 +134,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return p != null && type == p.type && attributes == p.attributes && defaultValue == p.defaultValue && region == p.region && flags == p.flags; } + + public override string ToString() + { + StringBuilder b = new StringBuilder(); + if (IsRef) + b.Append("ref "); + if (IsOut) + b.Append("out "); + if (IsParams) + b.Append("params "); + b.Append(name); + b.Append(':'); + b.Append(type.ToString()); + if (defaultValue != null) { + b.Append(" = "); + b.Append(defaultValue.ToString()); + } + return b.ToString(); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs index b739dacb82..d63d0be5b5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs @@ -37,6 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation const ushort FlagAbstract = 0x0002; const ushort FlagShadowing = 0x0004; const ushort FlagSynthetic = 0x0008; + const ushort FlagAddDefaultConstructorIfRequired = 0x0010; protected override void FreezeInternal() { @@ -398,5 +399,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { return DotNetName; } + + /// + /// Gets whether a default constructor should be added to this class if it is required. + /// Such automatic default constructors will not appear in ITypeDefinition.Methods, but will be present + /// in IType.GetMethods(). + /// + /// This way of creating the default constructor is necessary because + /// we cannot create it directly in the IClass - we need to consider partial classes. + public bool AddDefaultConstructorIfRequired { + get { return flags[FlagAddDefaultConstructorIfRequired]; } + set { + CheckBeforeMutation(); + flags[FlagAddDefaultConstructorIfRequired] = value; + } + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleConstantValue.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleConstantValue.cs index 420f904674..392952fac5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleConstantValue.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleConstantValue.cs @@ -33,5 +33,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation else return value; } + + public override string ToString() + { + if (value == null) + return "null"; + else + return value.ToString(); + } } }