diff --git a/src/Main/Base/Test/GenericResolverTests.cs b/src/Main/Base/Test/GenericResolverTests.cs index f81b2d8eac..112886a884 100644 --- a/src/Main/Base/Test/GenericResolverTests.cs +++ b/src/Main/Base/Test/GenericResolverTests.cs @@ -475,6 +475,25 @@ class D : Program { Assert.AreEqual("ExtensionMethods.OfType", mrr.ResolvedMember.FullyQualifiedName); Assert.AreEqual("System.Collections.Generic.IEnumerable{System.String}", mrr.ResolvedType.DotNetName); } + + [Test] + public void SD2_1528() + { + string program = @"using System; + class TestClass { + static void Test(IRuleBuilder ruleBuilder) { + + } + } + delegate R Func(T arg); + interface IRuleBuilder { } + interface IRuleBuilder : IRuleBuilder { } + static class ExtensionMethods { + public static IRuleBuilderOptions LessThan(this IRuleBuilder ruleBuilder, Func valueToCompare) where TProperty: IComparable {} + }"; + var rr = Resolve(program, "ruleBuilder", 4); + rr.GetCompletionData(rr.CallingClass.ProjectContent); + } #endregion #region C# 3.0 Type Inference diff --git a/src/Main/Base/Test/MemberLookupHelperTests.cs b/src/Main/Base/Test/MemberLookupHelperTests.cs index 6404b27b2a..ff338a71be 100644 --- a/src/Main/Base/Test/MemberLookupHelperTests.cs +++ b/src/Main/Base/Test/MemberLookupHelperTests.cs @@ -319,11 +319,13 @@ namespace ICSharpCode.SharpDevelop.Tests [Test] public void NoConversionExistsFromStringToDisposableT() { - Assert.IsFalse(IsApplicable(msc.SystemTypes.String, - CreateTWithDisposableConstraint())); - + // no conversion exists Assert.IsFalse(MemberLookupHelper.ConversionExists(msc.SystemTypes.String, CreateTWithDisposableConstraint())); + + // but it is applicable (applicability ignores constraints) + Assert.IsTrue(IsApplicable(msc.SystemTypes.String, + CreateTWithDisposableConstraint())); } [Test] diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs index f58edd1163..23ffa7e264 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs @@ -470,7 +470,7 @@ namespace ICSharpCode.SharpDevelop.Dom static bool IsConstructedConversionToGenericReturnType(IReturnType from, IReturnType to, IMethod allowGenericTargetsOnThisMethod) { // null could be passed when type arguments could not be resolved/inferred - if (from == null && to == null) + if (from == to) // both are null or return true; if (from == null || to == null) return false; @@ -483,14 +483,15 @@ namespace ICSharpCode.SharpDevelop.Dom if (to.IsGenericReturnType) { ITypeParameter typeParameter = to.CastToGenericReturnType().TypeParameter; - if (typeParameter.Method != allowGenericTargetsOnThisMethod) - return false; - foreach (IReturnType constraintType in typeParameter.Constraints) { - if (!ConversionExistsInternal(from, constraintType, allowGenericTargetsOnThisMethod)) { - return false; - } - } - return true; + if (typeParameter.Method == allowGenericTargetsOnThisMethod) + return true; + // applicability ignores constraints +// foreach (IReturnType constraintType in typeParameter.Constraints) { +// if (!ConversionExistsInternal(from, constraintType, allowGenericTargetsOnThisMethod)) { +// return false; +// } +// } + return false; } // for conversions like from IEnumerable to IEnumerable, where T is a GenericReturnType diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs index 527fa94f6c..c99d9ae2fb 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs @@ -422,8 +422,13 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver if (templateList.Count == 0) { c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; } else { + Debug.Assert(c.TypeParameters.Count == 0); foreach (AST.TemplateDefinition template in templateList) { - c.TypeParameters.Add(ConvertConstraints(template, new DefaultTypeParameter(c, template.Name, index++))); + c.TypeParameters.Add(new DefaultTypeParameter(c, template.Name, index++)); + } + // converting the constraints requires that the type parameters are already present + for (int i = 0; i < templateList.Count; i++) { + ConvertConstraints(templateList[i], (DefaultTypeParameter)c.TypeParameters[i]); } } } @@ -434,13 +439,18 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver if (templateList.Count == 0) { m.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; } else { + Debug.Assert(m.TypeParameters.Count == 0); foreach (AST.TemplateDefinition template in templateList) { - m.TypeParameters.Add(ConvertConstraints(template, new DefaultTypeParameter(m, template.Name, index++))); + m.TypeParameters.Add(new DefaultTypeParameter(m, template.Name, index++)); + } + // converting the constraints requires that the type parameters are already present + for (int i = 0; i < templateList.Count; i++) { + ConvertConstraints(templateList[i], (DefaultTypeParameter)m.TypeParameters[i]); } } } - DefaultTypeParameter ConvertConstraints(AST.TemplateDefinition template, DefaultTypeParameter typeParameter) + void ConvertConstraints(AST.TemplateDefinition template, DefaultTypeParameter typeParameter) { foreach (AST.TypeReference typeRef in template.Bases) { if (typeRef == AST.TypeReference.NewConstraint) { @@ -450,13 +460,12 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver } else if (typeRef == AST.TypeReference.StructConstraint) { typeParameter.HasValueTypeConstraint = true; } else { - IReturnType rt = CreateReturnType(typeRef); + IReturnType rt = CreateReturnType(typeRef, typeParameter.Method, TypeVisitor.ReturnTypeOptions.None); if (rt != null) { typeParameter.Constraints.Add(rt); } } } - return typeParameter; } public override object VisitDelegateDeclaration(AST.DelegateDeclaration delegateDeclaration, object data) diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs index a677dd4a04..0c625c4d31 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NRefactoryAstConverterTests.cs @@ -119,5 +119,37 @@ namespace X "); Assert.AreEqual(SurroundWithSummaryTags("This is the comment"), cu.Classes[0].Documentation); } + + [Test] + public void GenericMethodWithConstraintsTest() + { + // Test that constaints can reference other type parameters. + ICompilationUnit cu = Parse(@" +using System; + +class X { + public static A Method(A p1, B p2) where A : IComparable where B : IComparable { } +} +"); + IMethod method = cu.Classes[0].Methods[0]; + Assert.AreEqual(2, method.TypeParameters.Count); + + ITypeParameter a = method.TypeParameters[0]; + ITypeParameter b = method.TypeParameters[1]; + + Assert.AreSame(a, method.ReturnType.CastToGenericReturnType().TypeParameter); + Assert.AreSame(a, method.Parameters[0].ReturnType.CastToGenericReturnType().TypeParameter); + Assert.AreSame(b, method.Parameters[1].ReturnType.CastToGenericReturnType().TypeParameter); + + Assert.AreEqual(1, a.Constraints.Count); + ConstructedReturnType crt = a.Constraints[0].CastToConstructedReturnType(); + Assert.AreEqual("IComparable", crt.Name); + Assert.AreSame(b, crt.TypeArguments[0].CastToGenericReturnType().TypeParameter); + + Assert.AreEqual(1, b.Constraints.Count); + crt = b.Constraints[0].CastToConstructedReturnType(); + Assert.AreEqual("IComparable", crt.Name); + Assert.AreSame(a, crt.TypeArguments[0].CastToGenericReturnType().TypeParameter); + } } }