From c9c204439f76afa50629b0602c22ea6f9ab98247 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 10 Aug 2011 18:15:25 +0200 Subject: [PATCH] Fix type inference and overload resolution when a class type parameter gets substituted by a method type parameter. --- .../CSharp/Resolver/InvocationTests.cs | 15 + .../CSharp/Resolver/LambdaTests.cs | 21 ++ .../CSharp/Resolver/NameLookupTests.cs | 27 +- .../CSharp/Resolver/TypeInferenceTests.cs | 28 +- .../StringBuilderOutputFormatter.cs | 158 ---------- .../CSharp/Resolver/CSharpResolver.cs | 66 +++- .../CSharp/Resolver/Conversions.cs | 22 ++ .../Resolver/InvocationResolveResult.cs | 26 +- .../CSharp/Resolver/LocalResolveResult.cs | 2 +- .../CSharp/Resolver/MemberLookup.cs | 63 ++-- .../CSharp/Resolver/MemberResolveResult.cs | 3 +- .../MemberTypeOrNamespaceReference.cs | 2 +- .../Resolver/MethodGroupResolveResult.cs | 16 +- .../CSharp/Resolver/OverloadResolution.cs | 63 ++-- .../CSharp/Resolver/ResolveVisitor.cs | 2 +- .../CSharp/Resolver/TypeInference.cs | 27 +- .../ICSharpCode.NRefactory.csproj | 4 +- .../TypeSystem/ExtensionMethods.cs | 6 +- ICSharpCode.NRefactory/TypeSystem/IType.cs | 39 ++- .../TypeSystem/Implementation/AbstractType.cs | 10 + .../Implementation/DefaultTypeDefinition.cs | 12 +- .../Implementation/DefaultTypeParameter.cs | 81 ++--- .../MethodTypeParameterSubstitution.cs | 65 ---- .../Implementation/SpecializedEvent.cs | 57 ++-- .../Implementation/SpecializedField.cs | 53 ++-- .../Implementation/SpecializedMember.cs | 282 ++++++++++++++++++ .../Implementation/SpecializedMethod.cs | 103 ++++--- .../Implementation/SpecializedProperty.cs | 66 ++-- .../TypeParameterSubstitution.cs | 101 +++++++ .../Implementation/VoidTypeDefinition.cs | 5 + .../TypeSystem/ParameterizedType.cs | 183 ++++++------ 31 files changed, 942 insertions(+), 666 deletions(-) delete mode 100644 ICSharpCode.NRefactory/CSharp/Refactoring/StringBuilderOutputFormatter.cs delete mode 100644 ICSharpCode.NRefactory/TypeSystem/Implementation/MethodTypeParameterSubstitution.cs create mode 100644 ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs create mode 100644 ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs index 7d6b0cd87e..be269b878a 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs @@ -17,7 +17,9 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -279,5 +281,18 @@ class DerivedClass : MiddleClass { InvocationResolveResult mrr = Resolve(program); Assert.AreEqual("MiddleClass.Test", mrr.Member.FullName); } + + [Test] + public void SubstituteClassAndMethodTypeParametersAtOnce() + { + string program = @"class C { static void M(X a, T b) { $C.M(b, a)$; } }"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + + var m = (SpecializedMethod)rr.Member; + Assert.AreEqual("X", m.TypeArguments.Single().Name); + Assert.AreEqual("T", m.Parameters[0].Type.Resolve(context).Name); + Assert.AreEqual("X", m.Parameters[1].Type.Resolve(context).Name); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs index ba5317b973..4a91bcb3a6 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -290,6 +291,26 @@ class TestClass { Assert.AreEqual("System.Int32", lrr.Type.ReflectionName); } + [Test] + public void ConvertAllInGenericMethod() + { + string program = @"using System; +class TestClass { + static void Method(System.Collections.Generic.List list) { + $list.ConvertAll(x => (int)x)$; + } +}"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + SpecializedMethod m = (SpecializedMethod)rr.Member; + Assert.AreEqual("System.Int32", m.TypeArguments[0].ReflectionName); + Assert.AreEqual("System.Converter`2[[``0],[System.Int32]]", m.Parameters[0].Type.Resolve(context).ReflectionName); + + var crr = (ConversionResolveResult)rr.Arguments[0]; + Assert.IsTrue(crr.Conversion.IsAnonymousFunctionConversion); + Assert.AreEqual("System.Converter`2[[``0],[System.Int32]]", crr.Type.ReflectionName); + } + /* TODO write test for this class A { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs index ecb88bd939..f80adf59af 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs @@ -19,8 +19,8 @@ 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.Resolver @@ -496,7 +496,7 @@ namespace A.B { [Test] - public void InnerTypeResolve () + public void InnerTypeResolve1 () { string program = @"public class C { public class Inner { } } class TestClass { @@ -507,15 +507,19 @@ class TestClass { "; TypeResolveResult trr = Resolve(program); Assert.AreEqual("C.Inner", trr.Type.FullName); - - program = @"public class C { public class D { public class Inner { } }} + } + + [Test] + public void InnerTypeResolve2 () + { + string program = @"public class C { public class D { public class Inner { } }} class TestClass { void Test() { $C.D.Inner$ a; } } "; - trr = Resolve(program); + TypeResolveResult trr = Resolve(program); Assert.AreEqual("C.D.Inner", trr.Type.FullName); } @@ -838,5 +842,18 @@ class B var mrr = Resolve(program); Assert.AreEqual("B.x", mrr.Member.FullName); } + + [Test] + public void SubstituteClassAndMethodTypeParametersAtOnce() + { + string program = @"class C { static void M(X a, T b) { $C.M$(b, a); } }"; + var rr = Resolve(program); + Assert.AreEqual("X", rr.TypeArguments.Single().Name); + + var m = (SpecializedMethod)rr.Methods.Single(); + Assert.AreSame(rr.TypeArguments.Single(), m.TypeArguments.Single()); + Assert.AreEqual("T", m.Parameters[0].Type.Resolve(context).Name); + Assert.AreEqual("X", m.Parameters[1].Type.Resolve(context).Name); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs index 5777797703..8a80fdabb5 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs @@ -175,7 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public override IType GetInferredReturnType(IType[] parameterTypes) { - Assert.AreEqual(expectedParameterTypes, parameterTypes); + Assert.AreEqual(expectedParameterTypes, parameterTypes, "Parameters types passed to " + this); return inferredReturnType; } @@ -216,6 +216,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ti.InferTypeArguments(typeParameters, arguments, parameterTypes, out success)); Assert.IsTrue(success); } + + [Test] + public void ConvertAllLambdaInference() + { + ITypeParameter[] classTypeParameters = { new DefaultTypeParameter(EntityType.TypeDefinition, 0, "T") }; + ITypeParameter[] methodTypeParameters = { new DefaultTypeParameter(EntityType.Method, 0, "R") }; + + IType[] parameterTypes = { + new ParameterizedType(ctx.GetTypeDefinition(typeof(Converter<,>)), + new[] { classTypeParameters[0], methodTypeParameters[0] }) + }; + + // Signature: List.ConvertAll(Converter converter); + // Invocation: listOfString.ConvertAll(s => default(int)); + ResolveResult[] arguments = { + new MockImplicitLambda(new[] { KnownTypeReference.String.Resolve(ctx) }, KnownTypeReference.Int32.Resolve(ctx)) + }; + IType[] classTypeArguments = { + KnownTypeReference.String.Resolve(ctx) + }; + + bool success; + Assert.AreEqual( + new [] { KnownTypeReference.Int32.Resolve(ctx) }, + ti.InferTypeArguments(methodTypeParameters, arguments, parameterTypes, out success, classTypeArguments)); + } #endregion #region FindTypeInBounds diff --git a/ICSharpCode.NRefactory/CSharp/Refactoring/StringBuilderOutputFormatter.cs b/ICSharpCode.NRefactory/CSharp/Refactoring/StringBuilderOutputFormatter.cs deleted file mode 100644 index 63830b47a0..0000000000 --- a/ICSharpCode.NRefactory/CSharp/Refactoring/StringBuilderOutputFormatter.cs +++ /dev/null @@ -1,158 +0,0 @@ -// -// StringBuilderOutputFormatter.cs -// -// Author: -// Mike Krüger -// -// Copyright (c) 2011 Mike Krüger -// -// 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.Text; - -namespace ICSharpCode.NRefactory.CSharp.Refactoring -{ - public class StringBuilderOutputFormatter : IOutputFormatter - { - readonly StringBuilder sb = new StringBuilder (); - int indentation; - bool needsIndent = true; - - public int Length { - get { - WriteIndentation (); - return sb.Length; - } - } - - public int Indentation { - get { - return this.indentation; - } - set { - indentation = value; - } - } - - public string EolMarker { - get; - set; - } - - public override string ToString () - { - return sb.ToString (); - } - - public void WriteIdentifier (string ident) - { - WriteIndentation (); - sb.Append (ident); - } - - public void WriteKeyword (string keyword) - { - WriteIndentation (); - sb.Append (keyword); - } - - public void WriteToken (string token) - { - WriteIndentation (); - sb.Append (token); - } - - public void Space () - { - WriteIndentation (); - sb.Append (' '); - } - - public void OpenBrace (BraceStyle style) - { - WriteIndentation (); - sb.Append (' '); - sb.Append ('{'); - Indent (); - NewLine (); - } - - public void CloseBrace (BraceStyle style) - { - Unindent (); - WriteIndentation (); - sb.Append ('}'); - } - - void WriteIndentation () - { - if (needsIndent) { - needsIndent = false; - for (int i = 0; i < indentation; i++) { - sb.Append ('\t'); - } - } - } - - public void NewLine () - { - sb.Append (EolMarker); - needsIndent = true; - } - - public void Indent () - { - indentation++; - } - - public void Unindent () - { - indentation--; - } - - public void WriteComment (CommentType commentType, string content) - { - WriteIndentation (); - switch (commentType) { - case CommentType.SingleLine: - sb.Append ("//"); - sb.AppendLine (content); - break; - case CommentType.MultiLine: - sb.Append ("/*"); - sb.Append (content); - sb.Append ("*/"); - break; - case CommentType.Documentation: - sb.Append ("///"); - sb.AppendLine (content); - break; - } - } - - public virtual void StartNode (AstNode node) - { - } - - public virtual void EndNode (AstNode node) - { - } - } -} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index ac9db2e431..1215f14cfc 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -344,7 +344,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } IMember IMember.MemberDefinition { - get { return null; } + get { return this; } } IList IMember.InterfaceImplementations { @@ -1723,7 +1723,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new InvocationResolveResult( null, lifted.nonLiftedOperator, lifted.ReturnType.Resolve(context), r.GetArgumentsWithConversions(), r.BestCandidateErrors, - typeArguments: r.InferredTypeArguments, isLiftedOperatorInvocation: true, argumentToParameterMap: r.GetArgumentToParameterMap() ); @@ -2004,15 +2003,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver NamespaceResolveResult nrr = target as NamespaceResolveResult; if (nrr != null) { - if (typeArguments.Count == 0) { - string fullName = NamespaceDeclaration.BuildQualifiedName(nrr.NamespaceName, identifier); - if (context.GetNamespace(fullName, StringComparer.Ordinal) != null) - return new NamespaceResolveResult(fullName); - } - ITypeDefinition def = context.GetTypeDefinition(nrr.NamespaceName, identifier, typeArguments.Count, StringComparer.Ordinal); - if (def != null) - return new TypeResolveResult(def); - return ErrorResult; + return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments); } if (SharedTypes.Dynamic.Equals(target.Type)) @@ -2021,7 +2012,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver MemberLookup lookup = CreateMemberLookup(); ResolveResult result = lookup.Lookup(target, identifier, typeArguments, isInvocationTarget); if (result is UnknownMemberResolveResult) { - var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments.Count); + var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments); if (extensionMethods.Count > 0) { return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { extensionMethods = extensionMethods @@ -2039,6 +2030,36 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return result; } + public ResolveResult ResolveMemberType(ResolveResult target, string identifier, IList typeArguments) + { + cancellationToken.ThrowIfCancellationRequested(); + + NamespaceResolveResult nrr = target as NamespaceResolveResult; + if (nrr != null) { + return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments); + } + + MemberLookup lookup = CreateMemberLookup(); + return lookup.LookupType(target.Type, identifier, typeArguments); + } + + ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IList typeArguments) + { + if (typeArguments.Count == 0) { + string fullName = NamespaceDeclaration.BuildQualifiedName(nrr.NamespaceName, identifier); + if (context.GetNamespace(fullName, StringComparer.Ordinal) != null) + return new NamespaceResolveResult(fullName); + } + ITypeDefinition def = context.GetTypeDefinition(nrr.NamespaceName, identifier, typeArguments.Count, StringComparer.Ordinal); + if (def != null) { + if (typeArguments.Count > 0) + return new TypeResolveResult(new ParameterizedType(def, typeArguments)); + else + return new TypeResolveResult(def); + } + return ErrorResult; + } + MemberLookup CreateMemberLookup() { return new MemberLookup(context, this.CurrentTypeDefinition, this.UsingScope != null ? this.UsingScope.ProjectContent : null); @@ -2047,9 +2068,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region GetExtensionMethods /// - /// Gets the extension methods that are called 'name', and can be called with 'typeArgumentCount' explicit type arguments; + /// Gets the extension methods that are called 'name' /// and are applicable with a first argument type of 'targetType'. /// + /// Type of the 'this' argument + /// Name of the extension method + /// Explicitly provided type arguments. + /// An empty list will return all matching extension method definitions; + /// a non-empty list will return s for all extension methods + /// with the matching number of type parameters. /// /// The results are stored in nested lists because they are grouped by using scope. /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", @@ -2059,13 +2086,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// new List { all extensions from SomeExtensions } /// } /// - public List> GetExtensionMethods(IType targetType, string name, int typeArgumentCount) + public List> GetExtensionMethods(IType targetType, string name, IList typeArguments = null) { List> extensionMethodGroups = new List>(); foreach (var inputGroup in GetAllExtensionMethods()) { List outputGroup = new List(); foreach (var method in inputGroup) { - if (method.Name == name && (typeArgumentCount == 0 || method.TypeParameters.Count == typeArgumentCount)) { + if (method.Name != name) + continue; + + if (typeArguments != null && typeArguments.Count > 0) { + if (method.TypeParameters.Count != typeArguments.Count) + continue; + SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments); + // TODO: verify targetType + outputGroup.Add(sm); + } else { // TODO: verify targetType outputGroup.Add(method); } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs index 1c78cd33d4..daefe18edb 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs @@ -310,6 +310,28 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Conversion.ImplicitPointerConversion; return Conversion.None; } + + /// + /// Gets whether the type 'fromType' is convertible to 'toType' + /// using one of the conversions allowed when satisying constraints (§4.4.4) + /// + public bool IsConstraintConvertible(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + + if (IdentityConversion(fromType, toType)) + return true; + if (ImplicitReferenceConversion(fromType, toType)) + return true; + if (BoxingConversion(fromType, toType) && !NullableType.IsNullable(fromType)) + return true; + if (ImplicitTypeParameterConversion(fromType, toType)) + return true; + return false; + } #endregion #region ExplicitConversion diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs index 8864c4f609..e0f5ea7aa6 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs @@ -31,7 +31,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public class InvocationResolveResult : MemberResolveResult { public readonly OverloadResolutionErrors OverloadResolutionErrors; - public readonly IList TypeArguments; public readonly IList Arguments; @@ -55,11 +54,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public InvocationResolveResult(ResolveResult targetResult, OverloadResolution or, ITypeResolveContext context) : base( or.IsExtensionMethodInvocation ? null : targetResult, - or.BestCandidate, - GetReturnType(or, context)) + or.GetBestCandidateWithSubstitutedTypeArguments(), + context) { this.OverloadResolutionErrors = or.BestCandidateErrors; - this.TypeArguments = or.InferredTypeArguments; this.argumentToParameterMap = or.GetArgumentToParameterMap(); this.Arguments = or.GetArgumentsWithConversions(); @@ -72,14 +70,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult targetResult, IParameterizedMember member, IType returnType, IList arguments, OverloadResolutionErrors overloadResolutionErrors = OverloadResolutionErrors.None, - IList typeArguments = null, bool isExtensionMethodInvocation = false, bool isExpandedForm = false, bool isLiftedOperatorInvocation = false, IList argumentToParameterMap = null) : base(targetResult, member, returnType) { this.OverloadResolutionErrors = overloadResolutionErrors; - this.TypeArguments = typeArguments ?? EmptyList.Instance; this.Arguments = arguments ?? EmptyList.Instance; this.IsExtensionMethodInvocation = isExtensionMethodInvocation; this.IsExpandedForm = isExpandedForm; @@ -87,24 +83,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.argumentToParameterMap = argumentToParameterMap; } - static IType GetReturnType(OverloadResolution or, ITypeResolveContext context) - { - if (context == null) - throw new ArgumentNullException("context"); - - IType returnType; - if (or.BestCandidate.EntityType == EntityType.Constructor) - returnType = or.BestCandidate.DeclaringType; - else - returnType = or.BestCandidate.ReturnType.Resolve(context); - - var typeArguments = or.InferredTypeArguments; - if (typeArguments.Count > 0) - return returnType.AcceptVisitor(new MethodTypeParameterSubstitution(typeArguments)); - else - return returnType; - } - public override bool IsError { get { return this.OverloadResolutionErrors != OverloadResolutionErrors.None; } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/LocalResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/LocalResolveResult.cs index 6400a068b3..ef056230f9 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/LocalResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/LocalResolveResult.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public override string ToString() { - return string.Format("[VariableResolveResult {0}]", variable); + return string.Format("[LocalResolveResult {0}]", variable); } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs index ffa0019a69..85e9016597 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs @@ -126,12 +126,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { int typeArgumentCount = typeArguments.Count; Predicate typeFilter = delegate (ITypeDefinition d) { - return d.TypeParameterCount == typeArgumentCount && d.Name == name && IsAccessible(d, true); + // inner types contain the type parameters of outer types. therefore this count has to been adjusted. + int correctedCount = d.TypeParameterCount - (d.DeclaringType != null ? d.DeclaringType.TypeParameterCount : 0); + return correctedCount == typeArgumentCount && d.Name == name && IsAccessible(d, true); }; - List types = declaringType.GetNestedTypes(context, typeFilter).ToList(); + List types; + if (parameterizeResultType) + types = declaringType.GetNestedTypes(typeArguments, context, typeFilter).ToList(); + else + types = declaringType.GetNestedTypes(context, typeFilter).ToList(); + RemoveTypesHiddenByOtherTypes(types); - if (types.Count > 0) - return CreateTypeResolveResult(types[0], types.Count > 1, typeArguments, parameterizeResultType); + if (types.Count == 1) + return new TypeResolveResult(types[0]); + else if (types.Count > 1) + return new AmbiguousTypeResolveResult(types[0]); else return new UnknownMemberResolveResult(declaringType, name, typeArguments); } @@ -156,24 +165,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - ResolveResult CreateTypeResolveResult(IType returnedType, bool isAmbiguous, IList typeArguments, bool parameterizeResultType) - { - if (parameterizeResultType && typeArguments.Count > 0) { - // Complete the partial parameterization - ParameterizedType pt = returnedType as ParameterizedType; - if (pt != null) { - IType[] newTypeArguments = new IType[pt.TypeParameterCount]; - pt.TypeArguments.CopyTo(newTypeArguments, 0); - typeArguments.CopyTo(newTypeArguments, newTypeArguments.Length - typeArguments.Count); - returnedType = new ParameterizedType(pt.GetDefinition(), newTypeArguments); - } - } - if (isAmbiguous) - return new AmbiguousTypeResolveResult(returnedType); - else - return new TypeResolveResult(returnedType); - } - /// /// Performs a member lookup. /// @@ -185,32 +176,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver List types = new List(); List members = new List(); if (!isInvocation) { - // Consider nested types only if it's not an invocation. The type parameter count must match in this case. + // Consider nested types only if it's not an invocation. + // type.GetNestedTypes() is checking the type parameter count for an exact match. Predicate typeFilter = delegate (ITypeDefinition d) { - // inner types contain the type parameters of outer types. therefore this count has to been adjusted. - int correctedCount = d.TypeParameterCount - (d.DeclaringType != null ? d.DeclaringType.TypeParameterCount : 0); - return correctedCount == typeArgumentCount && d.Name == name && IsAccessible(d, true); + return d.Name == name && IsAccessible(d, true); }; - types.AddRange(type.GetNestedTypes(context, typeFilter)); + types.AddRange(type.GetNestedTypes(typeArguments, context, typeFilter)); } bool allowProtectedAccess = IsProtectedAccessAllowed(type); + Predicate memberFilter = delegate(IMember member) { + return !member.IsOverride && member.Name == name && IsAccessible(member, allowProtectedAccess); + }; if (typeArgumentCount == 0) { - Predicate memberFilter = delegate(IMember member) { - return !member.IsOverride && member.Name == name && IsAccessible(member, allowProtectedAccess); - }; members.AddRange(type.GetMembers(context, memberFilter)); + // Note: IsInvocable-checking cannot be done as part of the memberFilter; + // because it must be done after type substitution. if (isInvocation) members.RemoveAll(m => !IsInvocable(m, context)); } else { // No need to check for isInvocation/isInvocable here: // we filter out all non-methods - Predicate memberFilter = delegate(IMethod method) { - return method.TypeParameters.Count == typeArgumentCount - && !method.IsOverride && method.Name == name && IsAccessible(method, allowProtectedAccess); - }; - members.AddRange(type.GetMethods(context, memberFilter).SafeCast()); + members.AddRange(type.GetMethods(typeArguments, context, memberFilter)); } // TODO: can't members also hide types? @@ -269,7 +257,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (types.Count > 0) { bool isAmbiguous = !(types.Count == 1 && members.Count == 0); - return CreateTypeResolveResult(types[0], isAmbiguous, typeArguments, true); + if (isAmbiguous) + return new AmbiguousTypeResolveResult(types[0]); + else + return new TypeResolveResult(types[0]); } if (members.Count == 0) return new UnknownMemberResolveResult(type, name, typeArguments); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs index 1a1cbbff80..baaf504d7d 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs @@ -51,7 +51,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.constantValue = constantValue; } - public MemberResolveResult(ResolveResult targetResult, IMember member, ITypeResolveContext context) : base(member.ReturnType.Resolve(context)) + public MemberResolveResult(ResolveResult targetResult, IMember member, ITypeResolveContext context) + : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType.Resolve(context)) { this.targetResult = targetResult; this.member = member; diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs index fa91ded5f3..b8cb7b7d3d 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver for (int i = 0; i < typeArgs.Length; i++) { typeArgs[i] = typeArguments[i].Resolve(context); } - return r.ResolveMemberAccess(targetRR, identifier, typeArgs, false); + return r.ResolveMemberType(targetRR, identifier, typeArgs); } public NamespaceResolveResult ResolveNamespace(ITypeResolveContext context) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs index e4d18c41c9..abfcadd6c4 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs @@ -31,8 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public class MethodGroupResolveResult : ResolveResult { - readonly ReadOnlyCollection methods; - readonly ReadOnlyCollection typeArguments; + readonly IList methods; + readonly IList typeArguments; readonly ResolveResult targetResult; readonly string methodName; @@ -44,8 +44,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new ArgumentNullException("methods"); this.targetResult = targetResult; this.methodName = methodName; - this.methods = new ReadOnlyCollection(methods); - this.typeArguments = typeArguments != null ? new ReadOnlyCollection(typeArguments) : EmptyList.Instance; + this.methods = methods; + this.typeArguments = typeArguments ?? EmptyList.Instance; } /// @@ -73,14 +73,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets the methods that were found. /// This list does not include extension methods. /// - public ReadOnlyCollection Methods { + public IList Methods { get { return methods; } } /// /// Gets the type arguments that were explicitly provided. /// - public ReadOnlyCollection TypeArguments { + public IList TypeArguments { get { return typeArguments; } } @@ -104,7 +104,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver UsingScope oldUsingScope = resolver.UsingScope; try { resolver.UsingScope = usingScope; - extensionMethods = resolver.GetExtensionMethods(this.TargetType, methodName, typeArguments.Count); + extensionMethods = resolver.GetExtensionMethods(this.TargetType, methodName, typeArguments); } finally { resolver.UsingScope = oldUsingScope; resolver = null; @@ -173,7 +173,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } } - Log.WriteLine("Overload resolution finished, best candidate is {0}.", or.BestCandidate); + Log.WriteLine("Overload resolution finished, best candidate is {0}.", or.GetBestCandidateWithSubstitutedTypeArguments()); return or; } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs index b614118f36..6372704d99 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs @@ -53,7 +53,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public IType[] InferredTypes; - public IList Parameters { get { return Member.Parameters; } } + /// + /// Gets the original member parameters (before any substitution!) + /// + public readonly IList Parameters; /// /// Conversions applied to the arguments. @@ -86,7 +89,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { this.Member = member; this.IsExpandedForm = isExpanded; - this.ParameterTypes = new IType[member.Parameters.Count]; + this.Parameters = ((IParameterizedMember)member.MemberDefinition).Parameters; + this.ParameterTypes = new IType[this.Parameters.Count]; } public void AddError(OverloadResolutionErrors newError) @@ -227,8 +231,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } else { // named argument - for (int j = 0; j < candidate.Member.Parameters.Count; j++) { - if (argumentNames[i] == candidate.Member.Parameters[j].Name) { + for (int j = 0; j < candidate.Parameters.Count; j++) { + if (argumentNames[i] == candidate.Parameters[j].Name) { candidate.ArgumentToParameterMap[i] = j; } } @@ -250,6 +254,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return; } + ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType; + IList classTypeArguments; + if (parameterizedDeclaringType != null) { + classTypeArguments = parameterizedDeclaringType.TypeArguments; + } else { + classTypeArguments = null; + } // The method is generic: if (explicitlyGivenTypeArguments != null) { if (explicitlyGivenTypeArguments.Length == method.TypeParameters.Count) { @@ -268,12 +279,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } else { TypeInference ti = new TypeInference(context, conversions); bool success; - candidate.InferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, candidate.ParameterTypes, out success); + candidate.InferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments); if (!success) candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed); } // Now substitute in the formal parameters: - var substitution = new ConstraintValidatingSubstitution(candidate.InferredTypes, this); + var substitution = new ConstraintValidatingSubstitution(classTypeArguments, candidate.InferredTypes, this); for (int i = 0; i < candidate.ParameterTypes.Length; i++) { candidate.ParameterTypes[i] = candidate.ParameterTypes[i].AcceptVisitor(substitution); } @@ -281,15 +292,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint); } - sealed class ConstraintValidatingSubstitution : MethodTypeParameterSubstitution + sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution { - readonly OverloadResolution overloadResolution; + readonly Conversions conversions; + readonly ITypeResolveContext context; public bool ConstraintsValid = true; - public ConstraintValidatingSubstitution(IType[] typeArguments, OverloadResolution overloadResolution) - : base(typeArguments) + public ConstraintValidatingSubstitution(IList classTypeArguments, IList methodTypeArguments, OverloadResolution overloadResolution) + : base(classTypeArguments, methodTypeArguments) { - this.overloadResolution = overloadResolution; + this.context = overloadResolution.context; + this.conversions = overloadResolution.conversions; } public override IType VisitParameterizedType(ParameterizedType type) @@ -312,11 +325,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver break; } if (tp.HasReferenceTypeConstraint) { - if (typeArg.IsReferenceType(overloadResolution.context) != true) + if (typeArg.IsReferenceType(context) != true) ConstraintsValid = false; } if (tp.HasValueTypeConstraint) { - if (!NullableType.IsNonNullableValueType(typeArg, overloadResolution.context)) + if (!NullableType.IsNonNullableValueType(typeArg, context)) ConstraintsValid = false; } if (tp.HasDefaultConstructorConstraint) { @@ -324,13 +337,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (def != null && def.IsAbstract) ConstraintsValid = false; ConstraintsValid &= typeArg.GetConstructors( - overloadResolution.context, + context, m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public ).Any(); } foreach (IType constraintType in tp.Constraints) { IType c = newParameterizedType.SubstituteInType(constraintType); - ConstraintsValid &= overloadResolution.IsConstraintConvertible(typeArg, c); + ConstraintsValid &= conversions.IsConstraintConvertible(typeArg, c); } } } @@ -338,12 +351,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return newType; } } - - bool IsConstraintConvertible(IType typeArg, IType constraintType) - { - // TODO: this isn't exactly correct; not all kinds of implicit conversions are allowed here - return conversions.ImplicitConversion(typeArg, constraintType); - } #endregion #region CheckApplicability @@ -604,7 +611,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public IList InferredTypeArguments { get { if (bestCandidate != null && bestCandidate.InferredTypes != null) - return Array.AsReadOnly(bestCandidate.InferredTypes); + return bestCandidate.InferredTypes; else return EmptyList.Instance; } @@ -657,5 +664,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return args; } + + public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments() + { + if (bestCandidate == null) + return null; + IMethod method = bestCandidate.Member as IMethod; + if (method != null && method.TypeParameters.Count > 0) { + return new SpecializedMethod(method.DeclaringType, (IMethod)method.MemberDefinition, bestCandidate.InferredTypes); + } else { + return bestCandidate.Member; + } + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 27c7409e13..63eec8a36d 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -2231,7 +2231,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } var typeArguments = GetTypeArguments(memberType.TypeArguments); - return resolver.ResolveMemberAccess(target, memberType.MemberName, typeArguments); + return resolver.ResolveMemberType(target, memberType.MemberName, typeArguments); } public override ResolveResult VisitComposedType(ComposedType composedType, object data) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs b/ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs index 7f30b2e167..6f76b4ffe4 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs @@ -87,9 +87,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IType[] parameterTypes; ResolveResult[] arguments; bool[,] dependencyMatrix; + IList classTypeArguments; #region InferTypeArguments (main function) - public IType[] InferTypeArguments(IList typeParameters, IList arguments, IList parameterTypes, out bool success) + /// + /// Performs type inference. + /// + /// The method type parameters that should be inferred. + /// The arguments passed to the method. + /// The parameter types of the method. + /// Out: whether type inference was successful + /// + /// Class type arguments. These are substituted for class type parameters in the formal parameter types + /// when inferring a method group or lambda. + /// + /// The inferred type arguments. + public IType[] InferTypeArguments(IList typeParameters, IList arguments, IList parameterTypes, out bool success, IList classTypeArguments = null) { if (typeParameters == null) throw new ArgumentNullException("typeParameters"); @@ -102,6 +115,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver for (int i = 0; i < this.typeParameters.Length; i++) { if (i != typeParameters[i].Index) throw new ArgumentException("Type parameter has wrong index"); + if (typeParameters[i].OwnerType != EntityType.Method) + throw new ArgumentException("Type parameter must be owned by a method"); this.typeParameters[i] = new TP(typeParameters[i]); } this.parameterTypes = new IType[Math.Min(arguments.Count, parameterTypes.Count)]; @@ -112,6 +127,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.arguments[i] = arguments[i]; this.parameterTypes[i] = parameterTypes[i]; } + this.classTypeArguments = classTypeArguments; Log.WriteLine("Type Inference"); Log.WriteLine(" Signature: M<" + string.Join(", ", this.typeParameters) + ">" + "(" + string.Join(", ", this.parameterTypes) + ")"); @@ -137,6 +153,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.parameterTypes = null; this.arguments = null; this.dependencyMatrix = null; + this.classTypeArguments = null; } /// @@ -443,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (lrr.IsImplicitlyTyped) { if (m.Parameters.Count != lrr.Parameters.Count) return; // cannot infer due to mismatched parameter lists - MethodTypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); + TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); IType[] inferredParameterTypes = new IType[m.Parameters.Count]; for (int i = 0; i < inferredParameterTypes.Length; i++) { IType parameterType = m.Parameters[i].Type.Resolve(context); @@ -466,7 +483,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { ResolveResult[] args = new ResolveResult[m.Parameters.Count]; - MethodTypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); + TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); for (int i = 0; i < args.Length; i++) { IParameter param = m.Parameters[i]; IType parameterType = param.Type.Resolve(context); @@ -494,13 +511,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - MethodTypeParameterSubstitution GetSubstitutionForFixedTPs() + TypeParameterSubstitution GetSubstitutionForFixedTPs() { IType[] fixedTypes = new IType[typeParameters.Length]; for (int i = 0; i < fixedTypes.Length; i++) { fixedTypes[i] = typeParameters[i].FixedTo ?? SharedTypes.UnknownType; } - return new MethodTypeParameterSubstitution(fixedTypes); + return new TypeParameterSubstitution(classTypeArguments, fixedTypes); } #endregion diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 17fb06ff5f..ab55e516e7 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -246,7 +246,7 @@ - + @@ -255,6 +255,7 @@ + @@ -380,7 +381,6 @@ - diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index 35203db026..26ec436d76 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -209,11 +209,7 @@ namespace ICSharpCode.NRefactory.TypeSystem if (method.Name == "Invoke") { ParameterizedType pt = type as ParameterizedType; if (pt != null) { - SpecializedMethod m = new SpecializedMethod(method); - m.SetDeclaringType(pt); - var substitution = pt.GetSubstitution(); - m.SubstituteTypes(t => SubstitutionTypeReference.Create(t, substitution)); - return m; + return new SpecializedMethod(pt, method); } return method; } diff --git a/ICSharpCode.NRefactory/TypeSystem/IType.cs b/ICSharpCode.NRefactory/TypeSystem/IType.cs index ac19606721..a581c0d4c2 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IType.cs @@ -125,6 +125,22 @@ namespace ICSharpCode.NRefactory.TypeSystem // Derived.GetNestedTypes() = Base+Nested<`1, > // Here `1 refers to B, and there's no way to return X as it would collide with B. + /// + /// Gets inner classes (including inherited inner classes) + /// that have typeArguments.Count additional type parameters. + /// + /// The type arguments passed to the inner class + /// The context used for resolving type references + /// The filter used to select which types to return. + /// The filter is tested on the original type definitions (before parameterization). + /// + /// Type parameters belonging to the outer class will have the value copied from the outer type + /// if it is a parameterized type. Otherwise, those existing type parameters will be self-parameterized, + /// and thus 'leaked' to the caller in the same way the GetMembers() method does not specialize members + /// from an and 'leaks' type parameters in member signatures. + /// + IEnumerable GetNestedTypes(IList typeArguments, ITypeResolveContext context, Predicate filter = null); + /// /// Gets all instance constructors for this type. /// @@ -134,7 +150,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// This list does not include constructors in base classes or static constructors. /// For methods on parameterized types, type substitution will be performed on the method signature, - /// and the appriopriate will be returned. + /// and the appropriate will be returned. /// IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null); @@ -147,10 +163,23 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// The list does not include constructors. /// For methods on parameterized types, type substitution will be performed on the method signature, - /// and the appriopriate will be returned. + /// and the appropriate will be returned. /// IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null); + /// + /// Gets all generic methods that can be called on this type with the specified type arguments. + /// + /// The type arguments used for the call. + /// The context used for resolving type references + /// The filter used to select which methods to return. + /// The filter is tested on the original method definitions (before specialization). + /// + /// Type substitution will be performed on the method signature, creating a + /// with the specified type arguments. + /// + IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter = null); + /// /// Gets all properties that can be called on this type. /// @@ -159,7 +188,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// The filter is tested on the original property definitions (before specialization). /// /// For properties on parameterized types, type substitution will be performed on the property signature, - /// and the appriopriate will be returned. + /// and the appropriate will be returned. /// IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null); @@ -171,7 +200,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// The filter is tested on the original field definitions (before specialization). /// /// For fields on parameterized types, type substitution will be performed on the field's return type, - /// and the appriopriate will be returned. + /// and the appropriate will be returned. /// IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null); @@ -183,7 +212,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// The filter is tested on the original event definitions (before specialization). /// /// For fields on parameterized types, type substitution will be performed on the event's return type, - /// and the appriopriate will be returned. + /// and the appropriate will be returned. /// IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null); diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs index 4d3394aa67..060082cbeb 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs @@ -82,11 +82,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return EmptyList.Instance; } + public virtual IEnumerable GetNestedTypes(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return EmptyList.Instance; + } + public virtual IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } + public virtual IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter) + { + return EmptyList.Instance; + } + public virtual IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs index 8b85ac58cf..98d9bb0c9c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs @@ -426,16 +426,26 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return this; } - public virtual IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null) + public IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null) { return ParameterizedType.GetNestedTypes(this, context, filter); } + public IEnumerable GetNestedTypes(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return ParameterizedType.GetNestedTypes(this, typeArguments, context, filter); + } + public virtual IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { return ParameterizedType.GetMethods(this, context, filter); } + public virtual IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return ParameterizedType.GetMethods(this, typeArguments, context, filter); + } + public virtual IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs index b15bec1697..cc37c190aa 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -261,86 +261,40 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { - foreach (var baseType in GetNonCircularBaseTypes(context)) { - foreach (var m in baseType.GetMethods(context, filter)) { - if (!m.IsStatic) - yield return m; - } - } + return ParameterizedType.GetMethods(this, context, FilterNonStatic(filter)); + } + + public IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return ParameterizedType.GetMethods(this, typeArguments, context, FilterNonStatic(filter)); } public IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) { - foreach (var baseType in GetNonCircularBaseTypes(context)) { - foreach (var m in baseType.GetProperties(context, filter)) { - if (!m.IsStatic) - yield return m; - } - } + return ParameterizedType.GetProperties(this, context, FilterNonStatic(filter)); } public IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null) { - foreach (var baseType in GetNonCircularBaseTypes(context)) { - foreach (var m in baseType.GetFields(context, filter)) { - if (!m.IsStatic) - yield return m; - } - } + return ParameterizedType.GetFields(this, context, FilterNonStatic(filter)); } public IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null) { - foreach (var baseType in GetNonCircularBaseTypes(context)) { - foreach (var m in baseType.GetEvents(context, filter)) { - if (!m.IsStatic) - yield return m; - } - } + return ParameterizedType.GetEvents(this, context, FilterNonStatic(filter)); } public IEnumerable GetMembers(ITypeResolveContext context, Predicate filter = null) { - foreach (var baseType in GetNonCircularBaseTypes(context)) { - foreach (var m in baseType.GetMembers(context, filter)) { - if (!m.IsStatic) - yield return m; - } - } + return ParameterizedType.GetMembers(this, context, FilterNonStatic(filter)); } - // Problem with type parameter resolving - circular declarations - // void Example (S s, T t) where S : T where T : S - IEnumerable GetNonCircularBaseTypes(ITypeResolveContext context) - { - var result = this.GetBaseTypes(context).Where(bt => !IsCircular (context, bt)); - if (result.Any ()) - return result; - - // result may be empty, GetBaseTypes doesn't return object/struct when there are only constraints (even circular) as base types are available, - // but even when there are only circular references the default base type should be included. - IType defaultBaseType = context.GetTypeDefinition("System", HasValueTypeConstraint ? "ValueType" : "Object", 0, StringComparer.Ordinal); - if (defaultBaseType != null) - return new [] { defaultBaseType }; - return Enumerable.Empty (); - } - - bool IsCircular(ITypeResolveContext context, IType baseType) + static Predicate FilterNonStatic(Predicate filter) where T : class, IMember { - var parameter = baseType as DefaultTypeParameter; - if (parameter == null) - return false; - var stack = new Stack(); - while (true) { - if (parameter == this) - return true; - foreach (DefaultTypeParameter parameterBaseType in parameter.GetNonCircularBaseTypes(context).Where(t => t is DefaultTypeParameter)) { - stack.Push(parameterBaseType); - } - if (stack.Count == 0) - return false; - parameter = stack.Pop(); - } + if (filter == null) + return member => !member.IsStatic; + else + return member => !member.IsStatic && filter(member); } IEnumerable IType.GetNestedTypes(ITypeResolveContext context, Predicate filter) @@ -348,6 +302,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return EmptyList.Instance; } + IEnumerable IType.GetNestedTypes(IList typeArguments, ITypeResolveContext context, Predicate filter) + { + return EmptyList.Instance; + } + public IEnumerable GetBaseTypes(ITypeResolveContext context) { bool hasNonInterfaceConstraint = false; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/MethodTypeParameterSubstitution.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/MethodTypeParameterSubstitution.cs deleted file mode 100644 index 9ee63a7b70..0000000000 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/MethodTypeParameterSubstitution.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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.Text; - -namespace ICSharpCode.NRefactory.TypeSystem.Implementation -{ - /// - /// Substitutes method type parameters with type arguments. Does not modify class type parameters. - /// - public class MethodTypeParameterSubstitution : TypeVisitor - { - readonly IList typeArguments; - - public MethodTypeParameterSubstitution(IList typeArguments) - { - this.typeArguments = typeArguments; - } - - public override IType VisitTypeParameter(ITypeParameter type) - { - int index = type.Index; - if (type.OwnerType == EntityType.Method) { - if (index >= 0 && index < typeArguments.Count) - return typeArguments[index]; - else - return SharedTypes.UnknownType; - } else { - return base.VisitTypeParameter(type); - } - } - - public override string ToString() - { - StringBuilder b = new StringBuilder(); - b.Append('['); - for (int i = 0; i < typeArguments.Count; i++) { - if (i > 0) b.Append(", "); - b.Append("``"); - b.Append(i); - b.Append(" -> "); - b.Append(typeArguments[i]); - } - b.Append(']'); - return b.ToString(); - } - } -} diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs index 31c387d487..09a15d95f7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs @@ -21,55 +21,46 @@ using System; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// - /// Represents a specialized IEvent (e.g. after type substitution). + /// Represents a specialized IEvent (event after type substitution). /// - public class SpecializedEvent : DefaultEvent + public class SpecializedEvent : SpecializedMember, IEvent { - readonly IMember memberDefinition; - IType declaringType; + readonly IEvent eventDefinition; - public SpecializedEvent(IEvent e) : base(e) + public SpecializedEvent(IType declaringType, IEvent eventDefinition) + : base(declaringType, eventDefinition) { - this.memberDefinition = e.MemberDefinition; - this.declaringType = e.DeclaringType; + this.eventDefinition = eventDefinition; } - public override IType DeclaringType { - get { return declaringType; } + internal SpecializedEvent(IType declaringType, IEvent eventDefinition, TypeVisitor substitution, ITypeResolveContext context) + : base(declaringType, eventDefinition, substitution, context) + { + this.eventDefinition = eventDefinition; } - public void SetDeclaringType(IType declaringType) - { - CheckBeforeMutation(); - this.declaringType = declaringType; + public bool CanAdd { + get { return eventDefinition.CanAdd; } } - public override IMember MemberDefinition { - get { return memberDefinition; } + public bool CanRemove { + get { return eventDefinition.CanRemove; } } - public override string Documentation { - get { return memberDefinition.Documentation; } + public bool CanInvoke { + get { return eventDefinition.CanInvoke; } } - public override int GetHashCode() - { - int hashCode = 0; - unchecked { - if (memberDefinition != null) - hashCode += 1000000007 * memberDefinition.GetHashCode(); - if (declaringType != null) - hashCode += 1000000009 * declaringType.GetHashCode(); - } - return hashCode; + public IAccessor AddAccessor { + get { return eventDefinition.AddAccessor; } } - public override bool Equals(object obj) - { - SpecializedEvent other = obj as SpecializedEvent; - if (other == null) - return false; - return object.Equals(this.memberDefinition, other.memberDefinition) && object.Equals(this.declaringType, other.declaringType); + public IAccessor RemoveAccessor { + get { return eventDefinition.RemoveAccessor; } + } + + public IAccessor InvokeAccessor { + get { return eventDefinition.InvokeAccessor; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs index a11d68b009..fc5a0e39a7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs @@ -21,55 +21,42 @@ using System; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// - /// Represents a specialized IField (e.g. after type substitution). + /// Represents a specialized IField (field after type substitution). /// - public class SpecializedField : DefaultField + public class SpecializedField : SpecializedMember, IField { - readonly IMember memberDefinition; - IType declaringType; + readonly IField fieldDefinition; - public SpecializedField(IField f) : base(f) + public SpecializedField(IType declaringType, IField fieldDefinition) + : base(declaringType, fieldDefinition) { - this.memberDefinition = f.MemberDefinition; - this.declaringType = f.DeclaringType; + this.fieldDefinition = fieldDefinition; } - public override IType DeclaringType { - get { return declaringType; } + internal SpecializedField(IType declaringType, IField fieldDefinition, TypeVisitor substitution, ITypeResolveContext context) + : base(declaringType, fieldDefinition, substitution, context) + { + this.fieldDefinition = fieldDefinition; } - public void SetDeclaringType(IType declaringType) - { - CheckBeforeMutation(); - this.declaringType = declaringType; + public bool IsReadOnly { + get { return fieldDefinition.IsReadOnly; } } - public override IMember MemberDefinition { - get { return memberDefinition; } + public bool IsVolatile { + get { return fieldDefinition.IsVolatile; } } - public override string Documentation { - get { return memberDefinition.Documentation; } + ITypeReference IVariable.Type { + get { return this.ReturnType; } } - public override int GetHashCode() - { - int hashCode = 0; - unchecked { - if (memberDefinition != null) - hashCode += 1000000007 * memberDefinition.GetHashCode(); - if (declaringType != null) - hashCode += 1000000009 * declaringType.GetHashCode(); - } - return hashCode; + public bool IsConst { + get { return fieldDefinition.IsConst; } } - public override bool Equals(object obj) - { - SpecializedField other = obj as SpecializedField; - if (other == null) - return false; - return object.Equals(this.memberDefinition, other.memberDefinition) && object.Equals(this.declaringType, other.declaringType); + public IConstantValue ConstantValue { + get { return fieldDefinition.ConstantValue; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs new file mode 100644 index 0000000000..19eff7493f --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs @@ -0,0 +1,282 @@ +// 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.Text; + +namespace ICSharpCode.NRefactory.TypeSystem.Implementation +{ + /// + /// Represents a SpecializedMember (a member on which type substitution has been performed). + /// + public abstract class SpecializedMember : IMember + { + readonly IType declaringType; + readonly IMember memberDefinition; + readonly ITypeReference returnType; + + protected SpecializedMember(IType declaringType, IMember memberDefinition) + : this(declaringType, memberDefinition, GetSubstitution(declaringType), null) + { + } + + internal SpecializedMember(IType declaringType, IMember memberDefinition, TypeVisitor substitution, ITypeResolveContext context) + { + if (declaringType == null) + throw new ArgumentNullException("declaringType"); + if (memberDefinition == null) + throw new ArgumentNullException("memberDefinition"); + + this.declaringType = declaringType; + this.memberDefinition = memberDefinition; + this.returnType = Substitute(memberDefinition.ReturnType, substitution, context); + } + + internal static TypeVisitor GetSubstitution(IType declaringType) + { + ParameterizedType pt = declaringType as ParameterizedType; + if (pt != null) + return pt.GetSubstitution(); + else + return null; + } + + internal static ITypeReference Substitute(ITypeReference type, TypeVisitor substitution, ITypeResolveContext context) + { + if (substitution == null) + return type; + if (context != null) + return type.Resolve(context).AcceptVisitor(substitution); + else + return SubstitutionTypeReference.Create(type, substitution); + } + + public IType DeclaringType { + get { return declaringType; } + } + + public IMember MemberDefinition { + get { return memberDefinition; } + } + + public ITypeReference ReturnType { + get { return returnType; } + } + + public IList InterfaceImplementations { + get { return memberDefinition.InterfaceImplementations; } + } + + public bool IsVirtual { + get { return memberDefinition.IsVirtual; } + } + + public bool IsOverride { + get { return memberDefinition.IsOverride; } + } + + public bool IsOverridable { + get { return memberDefinition.IsOverridable; } + } + + public EntityType EntityType { + get { return memberDefinition.EntityType; } + } + + public DomRegion Region { + get { return memberDefinition.Region; } + } + + public DomRegion BodyRegion { + get { return memberDefinition.BodyRegion; } + } + + public ITypeDefinition DeclaringTypeDefinition { + get { return memberDefinition.DeclaringTypeDefinition; } + } + + public IList Attributes { + get { return memberDefinition.Attributes; } + } + + public string Documentation { + get { return memberDefinition.Documentation; } + } + + public Accessibility Accessibility { + get { return memberDefinition.Accessibility; } + } + + public bool IsStatic { + get { return memberDefinition.IsStatic; } + } + + public bool IsAbstract { + get { return memberDefinition.IsAbstract; } + } + + public bool IsSealed { + get { return memberDefinition.IsSealed; } + } + + public bool IsShadowing { + get { return memberDefinition.IsShadowing; } + } + + public bool IsSynthetic { + get { return memberDefinition.IsSynthetic; } + } + + public bool IsPrivate { + get { return memberDefinition.IsPrivate; } + } + + public bool IsPublic { + get { return memberDefinition.IsPublic; } + } + + public bool IsProtected { + get { return memberDefinition.IsProtected; } + } + + public bool IsInternal { + get { return memberDefinition.IsInternal; } + } + + public bool IsProtectedOrInternal { + get { return memberDefinition.IsProtectedOrInternal; } + } + + public bool IsProtectedAndInternal { + get { return memberDefinition.IsProtectedAndInternal; } + } + + public IProjectContent ProjectContent { + get { return memberDefinition.ProjectContent; } + } + + public string FullName { + get { return memberDefinition.FullName; } + } + + public string Name { + get { return memberDefinition.Name; } + } + + public string Namespace { + get { return memberDefinition.Namespace; } + } + + public string ReflectionName { + get { return memberDefinition.ReflectionName; } + } + + public bool IsFrozen { + get { return memberDefinition.IsFrozen; } + } + + void IFreezable.Freeze() + { + if (!memberDefinition.IsFrozen) + throw new NotSupportedException(); + } + + public override bool Equals(object obj) + { + SpecializedMember other = obj as SpecializedMember; + if (other == null) + return false; + return this.declaringType.Equals(other.declaringType) && this.memberDefinition.Equals(other.memberDefinition); + } + + public override int GetHashCode() + { + unchecked { + return 1000000007 * declaringType.GetHashCode() + 1000000009 * memberDefinition.GetHashCode(); + } + } + + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(GetType().Name); + b.Append(' '); + b.Append(declaringType.ToString()); + b.Append('.'); + b.Append(this.Name); + b.Append(':'); + b.Append(returnType.ToString()); + b.Append(']'); + return b.ToString(); + } + } + + public abstract class SpecializedParameterizedMember : SpecializedMember, IParameterizedMember + { + readonly IList parameters; + + protected SpecializedParameterizedMember(IType declaringType, IParameterizedMember memberDefinition) + : this(declaringType, memberDefinition, GetSubstitution(declaringType), null) + { + } + + internal SpecializedParameterizedMember(IType declaringType, IParameterizedMember memberDefinition, TypeVisitor substitution, ITypeResolveContext context) + : base(declaringType, memberDefinition, substitution, context) + { + var paramDefs = memberDefinition.Parameters; + if (paramDefs.Count == 0) { + this.parameters = EmptyList.Instance; + } else { + var parameters = new IParameter[paramDefs.Count]; + for (int i = 0; i < parameters.Length; i++) { + ITypeReference newType = Substitute(paramDefs[i].Type, substitution, context); + if (newType != paramDefs[i].Type) { + parameters[i] = new DefaultParameter(paramDefs[i]) { Type = newType }; + } else { + parameters[i] = paramDefs[i]; + } + } + this.parameters = Array.AsReadOnly(parameters); + } + } + + public IList Parameters { + get { return parameters; } + } + + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(GetType().Name); + b.Append(' '); + b.Append(this.DeclaringType.ToString()); + b.Append('.'); + b.Append(this.Name); + b.Append('('); + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) b.Append(", "); + b.Append(parameters[i].ToString()); + } + b.Append("):"); + b.Append(this.ReturnType.ToString()); + b.Append(']'); + return b.ToString(); + } + } +} diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index f9c0ae96c2..44ceae2548 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -17,49 +17,62 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// /// Represents a specialized IMethod (e.g. after type substitution). /// - public class SpecializedMethod : DefaultMethod + public class SpecializedMethod : SpecializedParameterizedMember, IMethod { - readonly IMember memberDefinition; - IType declaringType; + readonly IMethod methodDefinition; + readonly IList typeArguments; - public SpecializedMethod(IMethod m) : base(m) + public SpecializedMethod(IType declaringType, IMethod methodDefinition, IList typeArguments = null) + : this(declaringType, methodDefinition, typeArguments, GetSubstitution(declaringType, typeArguments), null) { - this.memberDefinition = m.MemberDefinition; - this.declaringType = m.DeclaringType; } - public override IType DeclaringType { - get { return declaringType; } - } - - public void SetDeclaringType(IType declaringType) + internal SpecializedMethod(IType declaringType, IMethod methodDefinition, IList typeArguments, TypeVisitor substitution, ITypeResolveContext context) : base(declaringType, methodDefinition, substitution, context) { - CheckBeforeMutation(); - this.declaringType = declaringType; + this.methodDefinition = methodDefinition; + + if (typeArguments != null) { + if (typeArguments.Count != methodDefinition.TypeParameters.Count) + throw new ArgumentException("Number of type arguments does not match number of type parameters"); + this.typeArguments = typeArguments; + } else { + this.typeArguments = EmptyList.Instance; + } } - public override IMember MemberDefinition { - get { return memberDefinition; } + internal static TypeVisitor GetSubstitution(IType declaringType, IList typeArguments) + { + ParameterizedType pt = declaringType as ParameterizedType; + if (pt != null) + return pt.GetSubstitution(typeArguments); + else if (typeArguments != null) + return new TypeParameterSubstitution(null, typeArguments); + else + return null; } - public override string Documentation { - get { return memberDefinition.Documentation; } + /// + /// Gets the type arguments passed to this method. + /// + public IList TypeArguments { + get { return typeArguments; } } public override int GetHashCode() { - int hashCode = 0; + int hashCode = base.GetHashCode(); unchecked { - if (memberDefinition != null) - hashCode += 1000000007 * memberDefinition.GetHashCode(); - if (declaringType != null) - hashCode += 1000000009 * declaringType.GetHashCode(); + for (int i = 0; i < typeArguments.Count; i++) { + hashCode *= 362631391; + hashCode += typeArguments[i].GetHashCode(); + } } return hashCode; } @@ -67,25 +80,39 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override bool Equals(object obj) { SpecializedMethod other = obj as SpecializedMethod; - if (other == null) + if (!base.Equals(other)) + return false; + if (typeArguments.Count != other.typeArguments.Count) return false; - return object.Equals(this.memberDefinition, other.memberDefinition) && object.Equals(this.declaringType, other.declaringType); + for (int i = 0; i < typeArguments.Count; i++) { + if (!typeArguments[i].Equals(other.typeArguments[i])) + return false; + } + return true; } - /// - /// Performs type substitution in parameter types and in the return type. - /// - public void SubstituteTypes(Func substitution) - { - this.ReturnType = substitution(this.ReturnType); - var p = this.Parameters; - for (int i = 0; i < p.Count; i++) { - ITypeReference newType = substitution(p[i].Type); - if (newType != p[i].Type) { - p[i] = new DefaultParameter(p[i]) { Type = newType }; - } - } - // TODO: we might also have to perform substitution within the method's constraints + public IList ReturnTypeAttributes { + get { return methodDefinition.ReturnTypeAttributes; } + } + + public IList TypeParameters { + get { return methodDefinition.TypeParameters; } + } + + public bool IsExtensionMethod { + get { return methodDefinition.IsExtensionMethod; } + } + + public bool IsConstructor { + get { return methodDefinition.IsConstructor; } + } + + public bool IsDestructor { + get { return methodDefinition.IsDestructor; } + } + + public bool IsOperator { + get { return methodDefinition.IsOperator; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs index ed5f3f2007..5647ec872a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs @@ -21,70 +21,42 @@ using System; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { /// - /// Represents a specialized IProperty (e.g. after type substitution). + /// Represents a specialized IProperty (property after type substitution). /// - public class SpecializedProperty : DefaultProperty + public class SpecializedProperty : SpecializedParameterizedMember, IProperty { - readonly IMember memberDefinition; - IType declaringType; + readonly IProperty propertyDefinition; - public SpecializedProperty(IProperty p) : base(p) + public SpecializedProperty(IType declaringType, IProperty propertyDefinition) + : base(declaringType, propertyDefinition) { - this.memberDefinition = p.MemberDefinition; - this.declaringType = p.DeclaringType; + this.propertyDefinition = propertyDefinition; } - public override IType DeclaringType { - get { return declaringType; } - } - - public void SetDeclaringType(IType declaringType) + internal SpecializedProperty(IType declaringType, IProperty propertyDefinition, TypeVisitor substitution, ITypeResolveContext context) + : base(declaringType, propertyDefinition, substitution, context) { - CheckBeforeMutation(); - this.declaringType = declaringType; + this.propertyDefinition = propertyDefinition; } - public override IMember MemberDefinition { - get { return memberDefinition; } + public bool CanGet { + get { return propertyDefinition.CanGet; } } - public override string Documentation { - get { return memberDefinition.Documentation; } + public bool CanSet { + get { return propertyDefinition.CanSet; } } - public override int GetHashCode() - { - int hashCode = 0; - unchecked { - if (memberDefinition != null) - hashCode += 1000000007 * memberDefinition.GetHashCode(); - if (declaringType != null) - hashCode += 1000000009 * declaringType.GetHashCode(); - } - return hashCode; + public IAccessor Getter { + get { return propertyDefinition.Getter; } } - public override bool Equals(object obj) - { - SpecializedProperty other = obj as SpecializedProperty; - if (other == null) - return false; - return object.Equals(this.memberDefinition, other.memberDefinition) && object.Equals(this.declaringType, other.declaringType); + public IAccessor Setter { + get { return propertyDefinition.Setter; } } - /// - /// Performs type substitution in parameter types and in the return type. - /// - public void SubstituteTypes(Func substitution) - { - this.ReturnType = substitution(this.ReturnType); - var p = this.Parameters; - for (int i = 0; i < p.Count; i++) { - ITypeReference newType = substitution(p[i].Type); - if (newType != p[i].Type) { - p[i] = new DefaultParameter(p[i]) { Type = newType }; - } - } + public bool IsIndexer { + get { return propertyDefinition.IsIndexer; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs new file mode 100644 index 0000000000..bdd1571f34 --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs @@ -0,0 +1,101 @@ +// 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.Text; + +namespace ICSharpCode.NRefactory.TypeSystem.Implementation +{ + /// + /// Substitues class and method type parameters. + /// + public class TypeParameterSubstitution : TypeVisitor + { + readonly IList classTypeArguments; + readonly IList methodTypeArguments; + + /// + /// Creates a new type parameter substitution. + /// + /// + /// The type arguments to substitute for class type parameters. + /// Pass null to keep class type parameters unmodified. + /// + /// + /// The type arguments to substitute for method type parameters. + /// Pass null to keep method type parameters unmodified. + /// + public TypeParameterSubstitution(IList classTypeArguments, IList methodTypeArguments) + { + this.classTypeArguments = classTypeArguments; + this.methodTypeArguments = methodTypeArguments; + } + + public override IType VisitTypeParameter(ITypeParameter type) + { + int index = type.Index; + if (classTypeArguments != null && type.OwnerType == EntityType.TypeDefinition) { + if (index >= 0 && index < classTypeArguments.Count) + return classTypeArguments[index]; + else + return SharedTypes.UnknownType; + } else if (methodTypeArguments != null && type.OwnerType == EntityType.Method) { + if (index >= 0 && index < methodTypeArguments.Count) + return methodTypeArguments[index]; + else + return SharedTypes.UnknownType; + } else { + return base.VisitTypeParameter(type); + } + } + + public override string ToString() + { + StringBuilder b = new StringBuilder(); + b.Append('['); + bool first = true; + if (classTypeArguments != null) { + for (int i = 0; i < classTypeArguments.Count; i++) { + if (first) { + first = false; + b.Append(", "); + } + b.Append('`'); + b.Append(i); + b.Append(" -> "); + b.Append(classTypeArguments[i]); + } + } + if (methodTypeArguments != null) { + for (int i = 0; i < methodTypeArguments.Count; i++) { + if (first) { + first = false; + b.Append(", "); + } + b.Append("``"); + b.Append(i); + b.Append(" -> "); + b.Append(methodTypeArguments[i]); + } + } + b.Append(']'); + return b.ToString(); + } + } +} diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs index e5426cfa3e..3e9c2c9f21 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs @@ -54,6 +54,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return EmptyList.Instance; } + public override IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter) + { + return EmptyList.Instance; + } + public override IEnumerable GetProperties(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs index 9ef7a86a6f..faa88eb60f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs @@ -38,44 +38,6 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public sealed class ParameterizedType : Immutable, IType { - sealed class Substitution : TypeVisitor - { - readonly IType[] typeArguments; - - public Substitution(IType[] typeArguments) - { - this.typeArguments = typeArguments; - } - - public override IType VisitTypeParameter(ITypeParameter type) - { - int index = type.Index; - if (type.OwnerType == EntityType.TypeDefinition) { - if (index >= 0 && index < typeArguments.Length) - return typeArguments[index]; - else - return SharedTypes.UnknownType; - } else { - return base.VisitTypeParameter(type); - } - } - - public override string ToString() - { - StringBuilder b = new StringBuilder(); - b.Append('['); - for (int i = 0; i < typeArguments.Length; i++) { - if (i > 0) b.Append(", "); - b.Append('`'); - b.Append(i); - b.Append(" -> "); - b.Append(typeArguments[i]); - } - b.Append(']'); - return b.ToString(); - } - } - readonly ITypeDefinition genericType; readonly IType[] typeArguments; @@ -190,21 +152,31 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public IType SubstituteInType(IType type) { - return type.AcceptVisitor(new Substitution(typeArguments)); + return type.AcceptVisitor(new TypeParameterSubstitution(typeArguments, null)); } /// /// Gets a type visitor that performs the substitution of class type parameters with the type arguments /// of this parameterized type. /// - public TypeVisitor GetSubstitution() + public TypeParameterSubstitution GetSubstitution() + { + return new TypeParameterSubstitution(typeArguments, null); + } + + /// + /// Gets a type visitor that performs the substitution of class type parameters with the type arguments + /// of this parameterized type, + /// and also substitutes method type parameters with the specified method type arguments. + /// + public TypeVisitor GetSubstitution(IList methodTypeArguments) { - return new Substitution(typeArguments); + return new TypeParameterSubstitution(typeArguments, methodTypeArguments); } public IEnumerable GetBaseTypes(ITypeResolveContext context) { - Substitution substitution = new Substitution(typeArguments); + var substitution = GetSubstitution(); return genericType.GetBaseTypes(context).Select(t => t.AcceptVisitor(substitution)); } @@ -213,12 +185,22 @@ namespace ICSharpCode.NRefactory.TypeSystem return GetNestedTypes(this, context, filter); } + public IEnumerable GetNestedTypes(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return GetNestedTypes(this, typeArguments, context, filter); + } + internal static IEnumerable GetNestedTypes(IType type, ITypeResolveContext context, Predicate filter) { - return type.GetNonInterfaceBaseTypes(context).SelectMany(t => GetNestedTypesInternal(t, context, filter)); + return GetNestedTypes(type, null, context, filter); + } + + internal static IEnumerable GetNestedTypes(IType type, IList nestedTypeArguments, ITypeResolveContext context, Predicate filter) + { + return type.GetNonInterfaceBaseTypes(context).SelectMany(t => GetNestedTypesInternal(t, nestedTypeArguments, context, filter)); } - static IEnumerable GetNestedTypesInternal(IType baseType, ITypeResolveContext context, Predicate filter) + static IEnumerable GetNestedTypesInternal(IType baseType, IList nestedTypeArguments, ITypeResolveContext context, Predicate filter) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) @@ -227,22 +209,30 @@ namespace ICSharpCode.NRefactory.TypeSystem int outerTypeParameterCount = baseTypeDef.TypeParameterCount; ParameterizedType pt = baseType as ParameterizedType; foreach (ITypeDefinition nestedType in baseTypeDef.NestedTypes) { + int totalTypeParameterCount = nestedType.TypeParameterCount; + if (nestedTypeArguments != null) { + if (totalTypeParameterCount - outerTypeParameterCount != nestedTypeArguments.Count) + continue; + } if (!(filter == null || filter(nestedType))) continue; - int innerTypeParameterCount = nestedType.TypeParameterCount; - if (innerTypeParameterCount == 0 || (pt == null && innerTypeParameterCount == outerTypeParameterCount)) { + + if (totalTypeParameterCount == 0 || (pt == null && totalTypeParameterCount == outerTypeParameterCount)) { // The nested type has no new type parameters, and there are no type arguments // to copy from the outer type // -> we can directly return the nested type definition yield return nestedType; } else { // We need to parameterize the nested type - IType[] newTypeArguments = new IType[innerTypeParameterCount]; + IType[] newTypeArguments = new IType[totalTypeParameterCount]; for (int i = 0; i < outerTypeParameterCount; i++) { newTypeArguments[i] = pt != null ? pt.typeArguments[i] : baseTypeDef.TypeParameters[i]; } - for (int i = outerTypeParameterCount; i < innerTypeParameterCount; i++) { - newTypeArguments[i] = SharedTypes.UnboundTypeArgument; + for (int i = outerTypeParameterCount; i < totalTypeParameterCount; i++) { + if (nestedTypeArguments != null) + newTypeArguments[i] = nestedTypeArguments[i - outerTypeParameterCount]; + else + newTypeArguments[i] = SharedTypes.UnboundTypeArgument; } yield return new ParameterizedType(nestedType, newTypeArguments); } @@ -251,14 +241,10 @@ namespace ICSharpCode.NRefactory.TypeSystem public IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { - Substitution substitution = new Substitution(typeArguments); - Func substitutionFunc = t => t.Resolve(context).AcceptVisitor(substitution); + var substitution = GetSubstitution(); List methods = genericType.GetConstructors(context, filter).ToList(); for (int i = 0; i < methods.Count; i++) { - SpecializedMethod m = new SpecializedMethod(methods[i]); - m.SetDeclaringType(this); - m.SubstituteTypes(substitutionFunc); - methods[i] = m; + methods[i] = new SpecializedMethod(this, methods[i], null, substitution, context); } return methods; } @@ -268,37 +254,48 @@ namespace ICSharpCode.NRefactory.TypeSystem return GetMethods(this, context, filter); } + public IEnumerable GetMethods(IList typeArguments, ITypeResolveContext context, Predicate filter = null) + { + return GetMethods(this, typeArguments, context, filter); + } + internal static IEnumerable GetMethods(IType type, ITypeResolveContext context, Predicate filter) + { + return GetMethods(type, null, context, filter); + } + + internal static IEnumerable GetMethods(IType type, IList typeArguments, ITypeResolveContext context, Predicate filter) { Predicate newFilter; if (filter == null) newFilter = m => !m.IsConstructor; else newFilter = m => !m.IsConstructor && filter(m); - return type.GetNonInterfaceBaseTypes(context).SelectMany(t => GetMethodsInternal(t, context, newFilter)); + return type.GetNonInterfaceBaseTypes(context).SelectMany(t => GetMethodsInternal(t, typeArguments, context, newFilter)); } - static IEnumerable GetMethodsInternal(IType baseType, ITypeResolveContext context, Predicate filter) + static IEnumerable GetMethodsInternal(IType baseType, IList methodTypeArguments, ITypeResolveContext context, Predicate filter) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) yield break; baseTypeDef = baseTypeDef.GetCompoundClass(); + + ParameterizedType pt = baseType as ParameterizedType; - if (pt != null) { - Substitution substitution = null; - Func substitutionFunc = null; + if (pt != null || (methodTypeArguments != null && methodTypeArguments.Count > 0)) { + TypeVisitor substitution = null; foreach (IMethod m in baseTypeDef.Methods) { + if (methodTypeArguments != null && methodTypeArguments.Count > 0) { + if (m.TypeParameters.Count != methodTypeArguments.Count) + continue; + } if (!(filter == null || filter(m))) continue; if (substitution == null) { - substitution = new Substitution(pt.typeArguments); - substitutionFunc = t => t.Resolve(context).AcceptVisitor(substitution); + substitution = new TypeParameterSubstitution(pt != null ? pt.typeArguments : null, methodTypeArguments); } - SpecializedMethod sm = new SpecializedMethod(m); - sm.SetDeclaringType(pt); - sm.SubstituteTypes(substitutionFunc); - yield return sm; + yield return new SpecializedMethod(baseType, m, methodTypeArguments, substitution, context); } } else { foreach (IMethod m in baseTypeDef.Methods) { @@ -326,19 +323,14 @@ namespace ICSharpCode.NRefactory.TypeSystem baseTypeDef = baseTypeDef.GetCompoundClass(); ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { - Substitution substitution = null; - Func substitutionFunc = null; + TypeParameterSubstitution substitution = null; foreach (IProperty p in baseTypeDef.Properties) { if (!(filter == null || filter(p))) continue; if (substitution == null) { - substitution = new Substitution(pt.typeArguments); - substitutionFunc = t => t.Resolve(context).AcceptVisitor(substitution); + substitution = pt.GetSubstitution(); } - SpecializedProperty sp = new SpecializedProperty(p); - sp.SetDeclaringType(pt); - sp.SubstituteTypes(substitutionFunc); - yield return sp; + yield return new SpecializedProperty(pt, p, substitution, context); } } else { foreach (IProperty p in baseTypeDef.Properties) { @@ -366,19 +358,14 @@ namespace ICSharpCode.NRefactory.TypeSystem baseTypeDef = baseTypeDef.GetCompoundClass(); ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { - Substitution substitution = null; - Func substitutionFunc = null; + TypeParameterSubstitution substitution = null; foreach (IField f in baseTypeDef.Fields) { if (!(filter == null || filter(f))) continue; if (substitution == null) { - substitution = new Substitution(pt.typeArguments); - substitutionFunc = t => t.Resolve(context).AcceptVisitor(substitution); + substitution = pt.GetSubstitution(); } - SpecializedField sf = new SpecializedField(f); - sf.SetDeclaringType(pt); - sf.ReturnType = f.ReturnType.Resolve(context).AcceptVisitor(substitution); - yield return sf; + yield return new SpecializedField(pt, f, substitution, context); } } else { foreach (IField f in baseTypeDef.Fields) { @@ -406,19 +393,14 @@ namespace ICSharpCode.NRefactory.TypeSystem baseTypeDef = baseTypeDef.GetCompoundClass(); ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { - Substitution substitution = null; - Func substitutionFunc = null; + TypeParameterSubstitution substitution = null; foreach (IEvent e in baseTypeDef.Events) { if (!(filter == null || filter(e))) continue; if (substitution == null) { - substitution = new Substitution(pt.typeArguments); - substitutionFunc = t => t.Resolve(context).AcceptVisitor(substitution); + substitution = pt.GetSubstitution(); } - SpecializedEvent se = new SpecializedEvent(e); - se.SetDeclaringType(pt); - se.ReturnType = e.ReturnType.Resolve(context).AcceptVisitor(substitution); - yield return se; + yield return new SpecializedEvent(pt, e, substitution, context); } } else { foreach (IEvent e in baseTypeDef.Events) { @@ -440,14 +422,19 @@ namespace ICSharpCode.NRefactory.TypeSystem methodFilter = m => !m.IsConstructor; else methodFilter = m => !m.IsConstructor && filter(m); - return type.GetNonInterfaceBaseTypes(context).SelectMany( - delegate (IType t) { - IEnumerable members = GetMethodsInternal(t, context, methodFilter); - members = members.Concat(GetPropertiesInternal(t, context, filter)); - members = members.Concat(GetFieldsInternal(t, context, filter)); - members = members.Concat(GetEventsInternal(t, context, filter)); - return members; - }); + return type.GetNonInterfaceBaseTypes(context).SelectMany(t => GetMembersInternal(t, context, filter)); + } + + static IEnumerable GetMembersInternal(IType baseType, ITypeResolveContext context, Predicate filter) + { + foreach (var m in GetMethodsInternal(baseType, null, context, filter)) + yield return m; + foreach (var m in GetPropertiesInternal(baseType, context, filter)) + yield return m; + foreach (var m in GetFieldsInternal(baseType, context, filter)) + yield return m; + foreach (var m in GetEventsInternal(baseType, context, filter)) + yield return m; } public override bool Equals(object obj)