diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MemberTypeOrNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MemberTypeOrNamespaceReference.cs index 6bc3ea87e3..e702a131bd 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MemberTypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MemberTypeOrNamespaceReference.cs @@ -91,7 +91,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver for (int i = 0; i < typeArgs.Length; i++) { typeArgs[i] = typeArguments[i].Resolve(context); } - ResolveResult rr = r.ResolveMemberType(targetRR, identifier, typeArgs); + ResolveResult rr; + using (var busyLock = BusyManager.Enter(this)) { + if (busyLock.Success) { + rr = r.ResolveMemberType(targetRR, identifier, typeArgs); + } else { + // This can happen for "class Test : $Test.Base$ { public class Base {} }": + return ErrorResolveResult.UnknownError; // don't cache this error + } + } if (cacheManager != null) cacheManager.SetShared(this, rr); return rr; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs index 39c2c06012..fc9baf6f15 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs @@ -860,5 +860,13 @@ class B Assert.AreEqual("T", m.Parameters[0].Type.Resolve(context).Name); Assert.AreEqual("X", m.Parameters[1].Type.Resolve(context).Name); } + + [Test] + public void InheritingInnerClassShouldNotCauseStackOverflow() + { + string program = @"class Test : $Test.Base$, Test.ITest { public class Base {} interface ITest {} }"; + var result = Resolve(program); + Assert.AreEqual("Test.Base", result.Type.FullName); + } } } diff --git a/ICSharpCode.NRefactory/Utils/BusyManager.cs b/ICSharpCode.NRefactory/Utils/BusyManager.cs index 2c7f161aa4..51704eceeb 100644 --- a/ICSharpCode.NRefactory/Utils/BusyManager.cs +++ b/ICSharpCode.NRefactory/Utils/BusyManager.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.NRefactory.Utils /// However, using a simple 'bool busy' is not thread-safe, so we use a /// thread-static BusyManager. /// - static class BusyManager + public static class BusyManager { public struct BusyLock : IDisposable {