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 4f49c37454..21a8f22fb3 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryASTConvertVisitor.cs @@ -377,10 +377,10 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver ConvertAttributes(typeDeclaration, c); c.Documentation = GetDocumentation(region.BeginLine, typeDeclaration.Attributes); - if (currentClass.Count > 0) { - DefaultClass cur = GetCurrentClass(); - cur.InnerClasses.Add(c); - c.FullyQualifiedName = cur.FullyQualifiedName + '.' + typeDeclaration.Name; + DefaultClass outerClass = GetCurrentClass(); + if (outerClass != null) { + outerClass.InnerClasses.Add(c); + c.FullyQualifiedName = outerClass.FullyQualifiedName + '.' + typeDeclaration.Name; } else { c.FullyQualifiedName = PrependCurrentNamespace(typeDeclaration.Name); cu.Classes.Add(c); @@ -388,7 +388,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver c.UsingScope = currentNamespace; currentClass.Push(c); - ConvertTemplates(typeDeclaration.Templates, c); // resolve constrains in context of the class + ConvertTemplates(outerClass, typeDeclaration.Templates, c); // resolve constrains in context of the class // templates must be converted before base types because base types may refer to generic types if (c.ClassType != ClassType.Enum && typeDeclaration.BaseTypes != null) { @@ -421,19 +421,33 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver return ret; } - void ConvertTemplates(IList templateList, DefaultClass c) + void ConvertTemplates(DefaultClass outerClass, IList templateList, DefaultClass c) { - int index = 0; - if (templateList.Count == 0) { + int outerClassTypeParameterCount = outerClass != null ? outerClass.TypeParameters.Count : 0; + if (templateList.Count == 0 && outerClassTypeParameterCount == 0) { c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList; } else { Debug.Assert(c.TypeParameters.Count == 0); + + int index = 0; + if (outerClassTypeParameterCount > 0) { + foreach (DefaultTypeParameter outerTypeParamter in outerClass.TypeParameters) { + DefaultTypeParameter p = new DefaultTypeParameter(c, outerTypeParamter.Name, index++); + p.HasConstructableConstraint = outerTypeParamter.HasConstructableConstraint; + p.HasReferenceTypeConstraint = outerTypeParamter.HasReferenceTypeConstraint; + p.HasValueTypeConstraint = outerTypeParamter.HasValueTypeConstraint; + p.Attributes.AddRange(outerTypeParamter.Attributes); + p.Constraints.AddRange(outerTypeParamter.Constraints); + c.TypeParameters.Add(p); + } + } + foreach (AST.TemplateDefinition template in templateList) { 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]); + ConvertConstraints(templateList[i], (DefaultTypeParameter)c.TypeParameters[i + outerClassTypeParameterCount]); } } } @@ -487,17 +501,17 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver void CreateDelegate(DefaultClass c, string name, AST.TypeReference returnType, IList templates, IList parameters) { c.BaseTypes.Add(c.ProjectContent.SystemTypes.MulticastDelegate); - if (currentClass.Count > 0) { - DefaultClass cur = GetCurrentClass(); - cur.InnerClasses.Add(c); - c.FullyQualifiedName = cur.FullyQualifiedName + '.' + name; + DefaultClass outerClass = GetCurrentClass(); + if (outerClass != null) { + outerClass.InnerClasses.Add(c); + c.FullyQualifiedName = outerClass.FullyQualifiedName + '.' + name; } else { c.FullyQualifiedName = PrependCurrentNamespace(name); cu.Classes.Add(c); } c.UsingScope = currentNamespace; currentClass.Push(c); // necessary for CreateReturnType - ConvertTemplates(templates, c); + ConvertTemplates(outerClass, templates, c); List p = new List(); if (parameters != null) { 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 7359cef600..e720318568 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 @@ -6,6 +6,7 @@ // using System; +using System.Linq; using System.IO; using ICSharpCode.NRefactory; using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; @@ -209,5 +210,37 @@ class X { Assert.AreEqual("Foo", p.Name); Assert.AreEqual(1, p.Parameters.Count); } + + [Test] + public void GenericInnerClassTest() + { + ICompilationUnit cu = Parse(@" +using System; + +class Outer where T1 : IDisposable { + class Inner where T2 : T1 { + public static void Method(T1 p1, T2 p2, T3 p3) where T3 : T2 { } + } +} +"); + IClass outer = cu.Classes[0]; + Assert.AreEqual(1, outer.TypeParameters.Count); + + IClass inner = outer.InnerClasses[0]; + Assert.AreEqual(2, inner.TypeParameters.Count); + Assert.AreEqual("T1", inner.TypeParameters[0].Name); + Assert.AreSame(inner, inner.TypeParameters[0].Class); + Assert.AreEqual("T2", inner.TypeParameters[1].Name); + Assert.AreSame(inner.TypeParameters[0], inner.TypeParameters[1].Constraints[0].CastToGenericReturnType().TypeParameter); + Assert.AreEqual("IDisposable", inner.TypeParameters[0].Constraints[0].Name); + + IMethod method = inner.Methods.Single(m => m.Name == "Method"); + Assert.AreEqual(1, method.TypeParameters.Count); + Assert.AreSame(inner.TypeParameters[0], method.Parameters[0].ReturnType.CastToGenericReturnType().TypeParameter); + Assert.AreSame(inner.TypeParameters[1], method.Parameters[1].ReturnType.CastToGenericReturnType().TypeParameter); + Assert.AreSame(method.TypeParameters[0], method.Parameters[2].ReturnType.CastToGenericReturnType().TypeParameter); + + Assert.AreSame(inner.TypeParameters[1], method.TypeParameters[0].Constraints[0].CastToGenericReturnType().TypeParameter); + } } }