diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs index 247c1fb394..2e94a1c589 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single(); LabelStatement label = (LabelStatement)block.Statements.ElementAt(1); - DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block); + DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block, CecilLoaderTests.Mscorlib); da.Analyze("i"); Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement)); @@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis TrueStatement = new BlockStatement(), FalseStatement = new BlockStatement() }; - DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt); + DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib); da.Analyze("i"); Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt)); @@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis TrueStatement = new BlockStatement(), FalseStatement = new BlockStatement() }; - DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt); + DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib); da.Analyze("i"); Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt)); @@ -128,5 +129,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement)); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt)); } + + [Test] + public void WhileTrue() + { + WhileStatement loop = new WhileStatement { + Condition = new PrimitiveExpression(true), + EmbeddedStatement = new BlockStatement { + new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0)), + new BreakStatement() + } + }; + DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(loop, CecilLoaderTests.Mscorlib); + da.Analyze("i"); + Assert.AreEqual(0, da.UnassignedVariableUses.Count); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop)); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.EmbeddedStatement)); + Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement)); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop)); + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index cfce622458..5092401ed3 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; [assembly: ICSharpCode.NRefactory.TypeSystem.TestCase.TypeTestAttribute( 42, typeof(System.Action<>), typeof(IDictionary>))] @@ -65,4 +66,31 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase { public MyStructWithCtor(int a) {} } + + [Serializable] + public class NonCustomAttributes + { + [NonSerialized] + public readonly int NonSerializedField; + + [DllImport("unmanaged.dll", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DllMethod([In, Out] ref int p); + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8)] + public struct ExplicitFieldLayoutStruct + { + [FieldOffset(0)] + public int Field0; + + [FieldOffset(100)] + public int Field100; + } + + public class ParameterTests + { + public void MethodWithOutParameter(out int x) { x = 0; } + public void MethodWithParamsArray(params object[] x) {} + } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index ba43e776d6..2959f00ed9 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.TestCase; using NUnit.Framework; @@ -261,5 +262,105 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(2, ctors.Count()); Assert.IsFalse(ctors.Any(c => c.IsStatic)); } + + [Test] + public void SerializableAttribute() + { + IAttribute attr = ctx.GetClass(typeof(NonCustomAttributes)).Attributes.Single(); + Assert.AreEqual("System.SerializableAttribute", attr.AttributeType.Resolve(ctx).FullName); + } + + [Test] + public void NonSerializedAttribute() + { + IField field = ctx.GetClass(typeof(NonCustomAttributes)).Fields.Single(f => f.Name == "NonSerializedField"); + Assert.AreEqual("System.NonSerializedAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName); + } + + [Test] + public void ExplicitStructLayoutAttribute() + { + IAttribute attr = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Attributes.Single(); + Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute", attr.AttributeType.Resolve(ctx).FullName); + IConstantValue arg1 = attr.PositionalArguments.Single(); + Assert.AreEqual("System.Runtime.InteropServices.LayoutKind", arg1.GetValueType(ctx).FullName); + Assert.AreEqual((int)LayoutKind.Explicit, arg1.GetValue(ctx)); + + var arg2 = attr.NamedArguments[0]; + Assert.AreEqual("CharSet", arg2.Key); + Assert.AreEqual("System.Runtime.InteropServices.CharSet", arg2.Value.GetValueType(ctx).FullName); + Assert.AreEqual((int)CharSet.Unicode, arg2.Value.GetValue(ctx)); + + var arg3 = attr.NamedArguments[1]; + Assert.AreEqual("Pack", arg3.Key); + Assert.AreEqual("System.Int32", arg3.Value.GetValueType(ctx).FullName); + Assert.AreEqual(8, arg3.Value.GetValue(ctx)); + } + + [Test] + public void FieldOffsetAttribute() + { + IField field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0"); + Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName); + IConstantValue arg = field.Attributes.Single().PositionalArguments.Single(); + Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName); + Assert.AreEqual(0, arg.GetValue(ctx)); + + field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field100"); + Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName); + arg = field.Attributes.Single().PositionalArguments.Single(); + Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName); + Assert.AreEqual(100, arg.GetValue(ctx)); + } + + [Test] + public void DllImportAttribute() + { + IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); + IAttribute dllImport = method.Attributes.Single(); + Assert.AreEqual("System.Runtime.InteropServices.DllImportAttribute", dllImport.AttributeType.Resolve(ctx).FullName); + Assert.AreEqual("unmanaged.dll", dllImport.PositionalArguments[0].GetValue(ctx)); + Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value.GetValue(ctx)); + } + + [Test] + public void InOutParametersOnRefMethod() + { + IParameter p = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single(); + Assert.IsTrue(p.IsRef); + Assert.IsFalse(p.IsOut); + Assert.AreEqual(2, p.Attributes.Count); + Assert.AreEqual("System.Runtime.InteropServices.InAttribute", p.Attributes[0].AttributeType.Resolve(ctx).FullName); + Assert.AreEqual("System.Runtime.InteropServices.OutAttribute", p.Attributes[1].AttributeType.Resolve(ctx).FullName); + } + + [Test] + public void MarshalAsAttributeOnMethod() + { + IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); + IAttribute marshalAs = method.ReturnTypeAttributes.Single(); + Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.PositionalArguments.Single().GetValue(ctx)); + } + + [Test] + public void MethodWithOutParameter() + { + IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single(); + Assert.IsFalse(p.IsRef); + Assert.IsTrue(p.IsOut); + Assert.AreEqual(0, p.Attributes.Count); + Assert.IsTrue(p.Type is ByReferenceTypeReference); + } + + [Test] + public void MethodWithParamsArray() + { + IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single(); + Assert.IsFalse(p.IsRef); + Assert.IsFalse(p.IsOut); + Assert.IsTrue(p.IsParams); + Assert.AreEqual(0, p.Attributes.Count); + Assert.IsTrue(p.Type is ArrayTypeReference); + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs index 2f1e2b47e3..9a2fa141d5 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs @@ -5,7 +5,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; + using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Analysis { @@ -135,12 +138,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis } Statement rootStatement; + ResolveVisitor resolveVisitor; List nodes; Dictionary labels; List gotoStatements; - public IList BuildControlFlowGraph(Statement statement) + public IList BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken)) { + return BuildControlFlowGraph(statement, new ResolveVisitor( + new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip)); + } + + public IList BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor) + { + if (statement == null) + throw new ArgumentNullException("statement"); + if (resolveVisitor == null) + throw new ArgumentNullException("resolveVisitor"); + NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); nodeCreationVisitor.builder = this; try { @@ -148,6 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis this.labels = new Dictionary(); this.gotoStatements = new List(); this.rootStatement = statement; + this.resolveVisitor = resolveVisitor; ControlFlowNode entryPoint = CreateStartNode(statement); statement.AcceptVisitor(nodeCreationVisitor, entryPoint); @@ -167,6 +183,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis this.labels = null; this.gotoStatements = null; this.rootStatement = null; + this.resolveVisitor = null; } } @@ -206,7 +223,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type) { - ControlFlowNode node = CreateNode(statement, null, type); + ControlFlowNode node = CreateNode(null, statement, type); nodes.Add(node); return node; } @@ -238,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis /// The constant value of the expression; or null if the expression is not a constant. ConstantResolveResult EvaluateConstant(Expression expr) { - return null; // TODO: implement this using the C# resolver + return resolveVisitor.Resolve(expr) as ConstantResolveResult; } /// @@ -256,7 +273,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2) { - return false; // TODO: implement this using the resolver's operator== + if (c1 == null || c2 == null) + return false; + CSharpResolver r = new CSharpResolver(resolveVisitor.TypeResolveContext, resolveVisitor.CancellationToken); + ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2); + return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true; } #endregion diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs index 534e423258..e377022398 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -46,6 +48,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis readonly List allNodes = new List(); readonly Dictionary beginNodeDict = new Dictionary(); readonly Dictionary endNodeDict = new Dictionary(); + readonly ResolveVisitor resolveVisitor; + readonly CancellationToken cancellationToken; Dictionary nodeStatus = new Dictionary(); Dictionary edgeStatus = new Dictionary(); @@ -54,19 +58,30 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Queue nodesWithModifiedInput = new Queue(); - public DefiniteAssignmentAnalysis(Statement rootStatement) + public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken)) + : this(rootStatement, new ResolveVisitor(new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip)) { + } + + public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolveVisitor) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (resolveVisitor == null) + throw new ArgumentNullException("resolveVisitor"); + this.resolveVisitor = resolveVisitor; + this.cancellationToken = resolveVisitor.CancellationToken; visitor.analysis = this; ControlFlowGraphBuilder b = new ControlFlowGraphBuilder(); - allNodes.AddRange(b.BuildControlFlowGraph(rootStatement)); + allNodes.AddRange(b.BuildControlFlowGraph(rootStatement, resolveVisitor)); foreach (AstNode descendant in rootStatement.Descendants) { // Anonymous methods have separate control flow graphs, but we also need to analyze those. AnonymousMethodExpression ame = descendant as AnonymousMethodExpression; if (ame != null) - allNodes.AddRange(b.BuildControlFlowGraph(ame.Body)); + allNodes.AddRange(b.BuildControlFlowGraph(ame.Body, resolveVisitor)); LambdaExpression lambda = descendant as LambdaExpression; if (lambda != null && lambda.Body is Statement) - allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body)); + allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor)); } // Verify that we created nodes for all statements: Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any()); @@ -289,7 +304,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis /// The constant value of the expression; or null if the expression is not a constant. ConstantResolveResult EvaluateConstant(Expression expr) { - return null; // TODO: implement this using the C# resolver + return resolveVisitor.Resolve(expr) as ConstantResolveResult; } /// diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 8264bdc9d2..bd5fbcc586 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp get { AstNode next; for (AstNode cur = firstChild; cur != null; cur = next) { + Debug.Assert(cur.parent == this); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.nextSibling; diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs index 07e0626721..a8c11cb993 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs @@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp { AstNode next; for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); // Remember next before yielding cur. // This allows removing/replacing nodes while iterating through the list. next = cur.NextSibling; diff --git a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs index 68ac7ee2f6..a056c8ba16 100644 --- a/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs @@ -374,9 +374,9 @@ namespace ICSharpCode.NRefactory.CSharp variable.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier); if (em.Initializer != null) { - var initializer = (VariableInitializer)em.Initializer.Accept (this); + var initializer = (Expression)em.Initializer.Accept (this); if (initializer != null) - variable.AddChild (initializer, AstNode.Roles.Variable); + variable.AddChild (initializer, EnumMemberDeclaration.InitializerRole); } newField.AddChild (variable, AstNode.Roles.Variable); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 904bbae995..fd43e18c4e 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.context = context; } - #if !DOTNET35 public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken) { if (context == null) @@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.context = context; this.cancellationToken = cancellationToken; } - #endif #endregion #region Properties diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs index 0ae7b02a28..be956b22fa 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs @@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// ResolveAll } + + sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator + { + ResolveVisitorNavigationMode mode; + + public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip }; + + ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) + { + return mode; + } + } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 060bdfa7fa..2c4002a416 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion + /// + /// Gets the TypeResolveContext used by this ResolveVisitor. + /// + public ITypeResolveContext TypeResolveContext { + get { return resolver.Context; } + } + + /// + /// Gets the CancellationToken used by this ResolveVisitor. + /// + public CancellationToken CancellationToken { + get { return resolver.cancellationToken; } + } + #region Scan / Resolve bool resolverEnabled { get { return mode != ResolveVisitorNavigationMode.Scan; } @@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver mode = ResolveVisitorNavigationMode.Resolve; ResolveResult result; if (!cache.TryGetValue(node, out result)) { + resolver.cancellationToken.ThrowIfCancellationRequested(); result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult; } if (wasScan) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 7564ed9959..74cbdb91ce 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -46,6 +46,10 @@ namespace ICSharpCode.NRefactory.TypeSystem #endregion #region Load From AssemblyDefinition + /// + /// Loads the assembly definition into a project content. + /// + /// IProjectContent that represents the assembly public IProjectContent LoadAssembly(AssemblyDefinition assemblyDefinition) { if (assemblyDefinition == null) @@ -144,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public void Dispose() { - // Disposibng the synchronization context has no effect + // Disposing the synchronization context has no effect } string IDocumentationProvider.GetDocumentation(IEntity entity) @@ -297,14 +301,13 @@ namespace ICSharpCode.NRefactory.TypeSystem } } - const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute"; - static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) { if (attributeProvider == null || !attributeProvider.HasCustomAttributes) return false; foreach (CustomAttribute a in attributeProvider.CustomAttributes) { - if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) { + TypeReference type = a.AttributeType; + if (type.Name == "DynamicAttribute" && type.Namespace == "System.Runtime.CompilerServices") { if (a.ConstructorArguments.Count == 1) { CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) @@ -325,21 +328,150 @@ namespace ICSharpCode.NRefactory.TypeSystem } } + static readonly IAttribute inAttribute = new DefaultAttribute(typeof(InAttribute).ToTypeReference(), null); + static readonly IAttribute outAttribute = new DefaultAttribute(typeof(OutAttribute).ToTypeReference(), null); + void AddAttributes(ParameterDefinition parameter, DefaultParameter targetParameter) { + if (!targetParameter.IsOut) { + if (parameter.IsIn) + targetParameter.Attributes.Add(inAttribute); + if (parameter.IsOut) + targetParameter.Attributes.Add(outAttribute); + } if (parameter.HasCustomAttributes) { AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes); } } - void AddAttributes(MethodDefinition accessorMethod, DefaultAccessor targetAccessor) + static readonly ITypeReference dllImportAttributeTypeRef = typeof(DllImportAttribute).ToTypeReference(); + static readonly SimpleConstantValue trueValue = new SimpleConstantValue(KnownTypeReference.Boolean, true); + static readonly SimpleConstantValue falseValue = new SimpleConstantValue(KnownTypeReference.Boolean, true); + static readonly ITypeReference callingConventionTypeRef = typeof(CallingConvention).ToTypeReference(); + static readonly IAttribute preserveSigAttribute = new DefaultAttribute(typeof(PreserveSigAttribute).ToTypeReference(), null); + static readonly ITypeReference methodImplAttributeTypeRef = typeof(MethodImplAttribute).ToTypeReference(); + static readonly ITypeReference methodImplOptionsTypeRef = typeof(MethodImplOptions).ToTypeReference(); + + bool HasAnyAttributes(MethodDefinition methodDefinition) { - if (accessorMethod.HasCustomAttributes) { - AddCustomAttributes(accessorMethod.CustomAttributes, targetAccessor.Attributes); + if (methodDefinition.HasPInvokeInfo) + return true; + if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0) + return true; + if (methodDefinition.MethodReturnType.HasFieldMarshal) + return true; + return methodDefinition.HasCustomAttributes || methodDefinition.MethodReturnType.HasCustomAttributes; + } + + void AddAttributes(MethodDefinition methodDefinition, IList attributes, IList returnTypeAttributes) + { + MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; + + #region DllImportAttribute + if (methodDefinition.HasPInvokeInfo) { + PInvokeInfo info = methodDefinition.PInvokeInfo; + DefaultAttribute dllImport = new DefaultAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String }); + dllImport.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.String, info.Module.Name)); + + if (info.IsBestFitDisabled) + AddNamedArgument(dllImport, "BestFitMapping", falseValue); + if (info.IsBestFitEnabled) + AddNamedArgument(dllImport, "BestFitMapping", trueValue); + + CallingConvention callingConvention; + switch (info.Attributes & PInvokeAttributes.CallConvMask) { + case PInvokeAttributes.CallConvCdecl: + callingConvention = CallingConvention.Cdecl; + break; + case PInvokeAttributes.CallConvFastcall: + callingConvention = CallingConvention.FastCall; + break; + case PInvokeAttributes.CallConvStdCall: + callingConvention = CallingConvention.StdCall; + break; + case PInvokeAttributes.CallConvThiscall: + callingConvention = CallingConvention.ThisCall; + break; + case PInvokeAttributes.CallConvWinapi: + callingConvention = CallingConvention.Winapi; + break; + default: + throw new NotSupportedException("unknown calling convention"); + } + if (callingConvention != CallingConvention.Winapi) + AddNamedArgument(dllImport, "CallingConvention", new SimpleConstantValue(callingConventionTypeRef, (int)callingConvention)); + + CharSet charSet = CharSet.None; + switch (info.Attributes & PInvokeAttributes.CharSetMask) { + case PInvokeAttributes.CharSetAnsi: + charSet = CharSet.Ansi; + break; + case PInvokeAttributes.CharSetAuto: + charSet = CharSet.Auto; + break; + case PInvokeAttributes.CharSetUnicode: + charSet = CharSet.Unicode; + break; + } + if (charSet != CharSet.None) + dllImport.NamedArguments.Add(new KeyValuePair( + "CharSet", new SimpleConstantValue(charSetTypeRef, (int)charSet))); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name) + AddNamedArgument(dllImport, "EntryPoint", new SimpleConstantValue(KnownTypeReference.String, info.EntryPoint)); + + if (info.IsNoMangle) + AddNamedArgument(dllImport, "ExactSpelling", trueValue); + + if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) + implAttributes &= ~MethodImplAttributes.PreserveSig; + else + AddNamedArgument(dllImport, "PreserveSig", falseValue); + + if (info.SupportsLastError) + AddNamedArgument(dllImport, "SetLastError", trueValue); + + if (info.IsThrowOnUnmappableCharDisabled) + AddNamedArgument(dllImport, "ThrowOnUnmappableChar", falseValue); + if (info.IsThrowOnUnmappableCharEnabled) + AddNamedArgument(dllImport, "ThrowOnUnmappableChar", trueValue); + + attributes.Add(dllImport); + } + #endregion + + #region PreserveSigAttribute + if (implAttributes == MethodImplAttributes.PreserveSig) { + attributes.Add(preserveSigAttribute); + implAttributes = 0; + } + #endregion + + #region MethodImplAttribute + if (implAttributes != 0) { + DefaultAttribute methodImpl = new DefaultAttribute(methodImplAttributeTypeRef, new[] { methodImplOptionsTypeRef }); + methodImpl.PositionalArguments.Add(new SimpleConstantValue(methodImplOptionsTypeRef, (int)implAttributes)); + attributes.Add(methodImpl); + } + #endregion + + if (methodDefinition.HasCustomAttributes) { + AddCustomAttributes(methodDefinition.CustomAttributes, attributes); + } + if (methodDefinition.MethodReturnType.HasMarshalInfo) { + returnTypeAttributes.Add(ConvertMarshalInfo(methodDefinition.MethodReturnType.MarshalInfo)); } + if (methodDefinition.MethodReturnType.HasCustomAttributes) { + AddCustomAttributes(methodDefinition.MethodReturnType.CustomAttributes, returnTypeAttributes); + } + } + + static void AddNamedArgument(DefaultAttribute attribute, string name, IConstantValue value) + { + attribute.NamedArguments.Add(new KeyValuePair(name, value)); } - static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference()); + static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference(), null); static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference(); static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference(); static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference(); @@ -373,8 +505,9 @@ namespace ICSharpCode.NRefactory.TypeSystem charSet = CharSet.Unicode; break; } - if (layoutKind != LayoutKind.Auto || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { - DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef); + LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto; + if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { + DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef, new[] { layoutKindTypeRef }); structLayout.PositionalArguments.Add(new SimpleConstantValue(layoutKindTypeRef, (int)layoutKind)); if (charSet != CharSet.Ansi) { structLayout.NamedArguments.Add(new KeyValuePair( @@ -400,12 +533,58 @@ namespace ICSharpCode.NRefactory.TypeSystem } } + static readonly ITypeReference fieldOffsetAttributeTypeRef = typeof(FieldOffsetAttribute).ToTypeReference(); + static readonly DefaultAttribute nonSerializedAttribute = new DefaultAttribute(typeof(NonSerializedAttribute).ToTypeReference(), null); + + void AddAttributes(FieldDefinition fieldDefinition, IEntity targetEntity) + { + #region FieldOffsetAttribute + if (fieldDefinition.HasLayoutInfo) { + DefaultAttribute fieldOffset = new DefaultAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 }); + fieldOffset.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.Int32, fieldDefinition.Offset)); + targetEntity.Attributes.Add(fieldOffset); + } + #endregion + + #region NonSerializedAttribute + if (fieldDefinition.IsNotSerialized) { + targetEntity.Attributes.Add(nonSerializedAttribute); + } + #endregion + + if (fieldDefinition.HasMarshalInfo) { + targetEntity.Attributes.Add(ConvertMarshalInfo(fieldDefinition.MarshalInfo)); + } + + if (fieldDefinition.HasCustomAttributes) { + AddCustomAttributes(fieldDefinition.CustomAttributes, targetEntity.Attributes); + } + } + + #region MarshalAsAttribute (ConvertMarshalInfo) + static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference(); + static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference(); + + static IAttribute ConvertMarshalInfo(MarshalInfo marshalInfo) + { + DefaultAttribute attr = new DefaultAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef }); + attr.PositionalArguments.Add(new SimpleConstantValue(unmanagedTypeTypeRef, (int)marshalInfo.NativeType)); + // TODO: handle classes derived from MarshalInfo + return attr; + } + #endregion + void AddCustomAttributes(Mono.Collections.Generic.Collection attributes, IList targetCollection) { foreach (var cecilAttribute in attributes) { - if (cecilAttribute.AttributeType.FullName != DynamicAttributeFullName) { - targetCollection.Add(ReadAttribute(cecilAttribute)); + TypeReference type = cecilAttribute.AttributeType; + if (type.Namespace == "System.Runtime.CompilerServices") { + if (type.Name == "DynamicAttribute" || type.Name == "ExtensionAttribute") + continue; + } else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") { + continue; } + targetCollection.Add(ReadAttribute(cecilAttribute)); } } @@ -413,7 +592,15 @@ namespace ICSharpCode.NRefactory.TypeSystem { if (attribute == null) throw new ArgumentNullException("attribute"); - DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType)); + MethodReference ctor = attribute.Constructor; + ITypeReference[] ctorParameters = null; + if (ctor.HasParameters) { + ctorParameters = new ITypeReference[ctor.Parameters.Count]; + for (int i = 0; i < ctorParameters.Length; i++) { + ctorParameters[i] = ReadTypeReference(ctor.Parameters[i].ParameterType); + } + } + DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType), ctorParameters); try { if (attribute.HasConstructorArguments) { foreach (var arg in attribute.ConstructorArguments) { @@ -442,8 +629,13 @@ namespace ICSharpCode.NRefactory.TypeSystem #region Read Constant Value public IConstantValue ReadConstantValue(CustomAttributeArgument arg) { - ITypeReference type = ReadTypeReference(arg.Type); object value = arg.Value; + if (value is CustomAttributeArgument) { + // Cecil uses this representation for boxed values + arg = (CustomAttributeArgument)value; + value = arg.Value; + } + ITypeReference type = ReadTypeReference(arg.Type); CustomAttributeArgument[] array = value as CustomAttributeArgument[]; if (array != null) { // TODO: write unit test for this @@ -500,9 +692,9 @@ namespace ICSharpCode.NRefactory.TypeSystem InitNestedTypes(loader); // nested types can be initialized only after generic parameters were created - if (typeDefinition.HasCustomAttributes) { - loader.AddAttributes(typeDefinition, this); - } + loader.AddAttributes(typeDefinition, this); + + this.HasExtensionMethods = HasExtensionAttribute(typeDefinition); // set base classes if (typeDefinition.IsEnum) { @@ -699,7 +891,8 @@ namespace ICSharpCode.NRefactory.TypeSystem else m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType, entity: m); - AddAttributes(method, m); + if (HasAnyAttributes(method)) + AddAttributes(method, m.Attributes, m.ReturnTypeAttributes); TranslateModifiers(method, m); if (method.HasParameters) { @@ -708,18 +901,26 @@ namespace ICSharpCode.NRefactory.TypeSystem } } - // 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; - } + // mark as extension method if the attribute is set + if (method.IsStatic && HasExtensionAttribute(method)) { + m.IsExtensionMethod = true; } FinishReadMember(m); return m; } + static bool HasExtensionAttribute(ICustomAttributeProvider provider) + { + if (provider.HasCustomAttributes) { + foreach (var attr in provider.CustomAttributes) { + if (attr.AttributeType.Name == "ExtensionAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices") + return true; + } + } + return false; + } + bool IsVisible(MethodAttributes att) { att &= MethodAttributes.MemberAccessMask; @@ -782,14 +983,13 @@ namespace ICSharpCode.NRefactory.TypeSystem var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, entity: parentMember); DefaultParameter p = new DefaultParameter(type, parameter.Name); - AddAttributes(parameter, p); - if (parameter.ParameterType is Mono.Cecil.ByReferenceType) { - if (parameter.IsOut) + if (!parameter.IsIn && parameter.IsOut) p.IsOut = true; else p.IsRef = true; } + AddAttributes(parameter, p); if (parameter.IsOptional) { p.DefaultValue = ReadConstantValue(new CustomAttributeArgument(parameter.ParameterType, parameter.Constant)); @@ -917,10 +1117,10 @@ namespace ICSharpCode.NRefactory.TypeSystem { if (accessorMethod != null && IsVisible(accessorMethod.Attributes)) { Accessibility accessibility = GetAccessibility(accessorMethod.Attributes); - if (accessorMethod.HasCustomAttributes) { + if (HasAnyAttributes(accessorMethod)) { DefaultAccessor a = new DefaultAccessor(); a.Accessibility = accessibility; - AddAttributes(accessorMethod, a); + AddAttributes(accessorMethod, a.Attributes, a.ReturnTypeAttributes); return a; } else { return DefaultAccessor.GetFromAccessibility(accessibility); diff --git a/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs b/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs index ed9278b255..9c33b586ec 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs @@ -21,6 +21,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// IList Attributes { get; } + /// + /// Gets the attributes defined on the return type of the accessor. (e.g. [return: MarshalAs(...)]) + /// + IList ReturnTypeAttributes { get; } + /// /// Gets the accessibility of this accessor. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs b/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs index b9bb2dd97d..42f913f45c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs @@ -35,6 +35,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the named arguments passed to the attribute. /// IList> NamedArguments { get; } + + /// + /// Resolves the constructor method used for this attribute invocation. + /// Returns null if the constructor cannot be found. + /// + IMethod ResolveConstructor(ITypeResolveContext context); } #if WITH_CONTRACTS diff --git a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs index e66ab07820..7f6241b3dd 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public interface IMethod : IParameterizedMember { /// - /// Gets the attributes associated with the return type. + /// Gets the attributes associated with the return type. (e.g. [return: MarshalAs(...)]) /// IList ReturnTypeAttributes { get; } diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs index 905a85fdca..ef6568eda1 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs @@ -44,6 +44,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets all members declared in this class. This is the union of Fields,Properties,Methods and Events. /// IEnumerable Members { get; } + + /// + /// Gets whether this type contains extension methods. + /// + /// This property is used to speed up the search for extension methods. + bool HasExtensionMethods { get; } } #if WITH_CONTRACTS diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs index 9a8580f0a8..917b1f4732 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs @@ -44,6 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation Accessibility accessibility; DomRegion region; IList attributes; + IList returnTypeAttributes; protected override void FreezeInternal() { @@ -75,20 +76,32 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public IList ReturnTypeAttributes { + get { + if (returnTypeAttributes == null) + returnTypeAttributes = new List(); + return returnTypeAttributes; + } + } + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) { attributes = provider.InternList(attributes); + returnTypeAttributes = provider.InternList(returnTypeAttributes); } int ISupportsInterning.GetHashCodeForInterning() { - return (attributes != null ? attributes.GetHashCode() : 0) ^ region.GetHashCode() ^ (int)accessibility; + return (attributes != null ? attributes.GetHashCode() : 0) + ^ (returnTypeAttributes != null ? returnTypeAttributes.GetHashCode() : 0) + ^ region.GetHashCode() ^ (int)accessibility; } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { DefaultAccessor a = other as DefaultAccessor; - return a != null && (attributes == a.attributes && accessibility == a.accessibility && region == a.region); + return a != null && (attributes == a.attributes && returnTypeAttributes == a.returnTypeAttributes + && accessibility == a.accessibility && region == a.region); } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs index cb085ca18e..8607ffca81 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Text; @@ -13,8 +14,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// public sealed class DefaultAttribute : AbstractFreezable, IAttribute, ISupportsInterning { - DomRegion region; ITypeReference attributeType; + readonly ITypeReference[] constructorParameterTypes; + DomRegion region; IList positionalArguments; IList> namedArguments; @@ -34,11 +36,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation base.FreezeInternal(); } - public DefaultAttribute(ITypeReference attributeType) + public DefaultAttribute(ITypeReference attributeType, IEnumerable constructorParameterTypes) { if (attributeType == null) throw new ArgumentNullException("attributeType"); this.attributeType = attributeType; + this.constructorParameterTypes = constructorParameterTypes != null ? constructorParameterTypes.ToArray() : null; + } + + public ITypeReference AttributeType { + get { return attributeType; } + } + + public ReadOnlyCollection ConstructorParameterTypes { + get { return Array.AsReadOnly(constructorParameterTypes); } } public DomRegion Region { @@ -49,14 +60,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - public ITypeReference AttributeType { - get { return attributeType; } - set { - CheckBeforeMutation(); - attributeType = value; - } - } - public IList PositionalArguments { get { if (positionalArguments == null) @@ -73,6 +76,38 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public IMethod ResolveConstructor(ITypeResolveContext context) + { + IType[] parameterTypes = null; + if (constructorParameterTypes != null && constructorParameterTypes.Length > 0) { + parameterTypes = new IType[constructorParameterTypes.Length]; + for (int i = 0; i < parameterTypes.Length; i++) { + parameterTypes[i] = constructorParameterTypes[i].Resolve(context); + } + } + IMethod bestMatch = null; + foreach (IMethod ctor in attributeType.Resolve(context).GetConstructors(context)) { + if (ctor.IsStatic) + continue; + if (parameterTypes == null) { + if (ctor.Parameters.Count == 0) + return ctor; + } else if (ctor.Parameters.Count == parameterTypes.Length) { + bestMatch = ctor; + bool ok = true; + for (int i = 0; i < parameterTypes.Length; i++) { + if (ctor.Parameters[i].Type != parameterTypes[i]) { + ok = false; + break; + } + } + if (ok) + return ctor; + } + } + return bestMatch; + } + public override string ToString() { StringBuilder b = new StringBuilder(); @@ -100,6 +135,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation void ISupportsInterning.PrepareForInterning(IInterningProvider provider) { attributeType = provider.Intern(attributeType); + if (constructorParameterTypes != null) { + for (int i = 0; i < constructorParameterTypes.Length; i++) { + constructorParameterTypes[i] = provider.Intern(constructorParameterTypes[i]); + } + } positionalArguments = provider.InternList(positionalArguments); } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs index 881d38585b..c707780c42 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation const ushort FlagShadowing = 0x0004; const ushort FlagSynthetic = 0x0008; const ushort FlagAddDefaultConstructorIfRequired = 0x0010; + const ushort FlagHasExtensionMethods = 0x0020; protected override void FreezeInternal() { @@ -312,6 +313,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public bool HasExtensionMethods { + get { return flags[FlagHasExtensionMethods]; } + set { + CheckBeforeMutation(); + flags[FlagHasExtensionMethods] = value; + } + } + public IProjectContent ProjectContent { get { return projectContent; } }