diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 55095c94c5..593bcf5892 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -58,6 +58,7 @@ + diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs new file mode 100644 index 0000000000..eb1826f9c7 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using NUnit.Framework; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + [TestFixture] + public class GetMembersTests + { + IProjectContent mscorlib = CecilLoaderTests.Mscorlib; + + [Test] + public void EmptyClassHasToString() + { + DefaultTypeDefinition c = new DefaultTypeDefinition(mscorlib, string.Empty, "C"); + Assert.AreEqual("System.Object.ToString", c.GetMethods(mscorlib, m => m.Name == "ToString").Single().FullName); + } + + [Test] + public void MultipleInheritanceTest() + { + DefaultTypeDefinition b1 = new DefaultTypeDefinition(mscorlib, string.Empty, "B1"); + b1.ClassType = ClassType.Interface; + b1.Properties.Add(new DefaultProperty(b1, "P1")); + + DefaultTypeDefinition b2 = new DefaultTypeDefinition(mscorlib, string.Empty, "B1"); + b2.ClassType = ClassType.Interface; + b2.Properties.Add(new DefaultProperty(b1, "P2")); + + DefaultTypeDefinition c = new DefaultTypeDefinition(mscorlib, string.Empty, "C"); + c.ClassType = ClassType.Interface; + c.BaseTypes.Add(b1); + c.BaseTypes.Add(b2); + + Assert.AreEqual(new[] { "P1", "P2" }, c.GetProperties(mscorlib).Select(p => p.Name).ToArray()); + // Test that there's only one copy of ToString(): + Assert.AreEqual(1, c.GetMethods(mscorlib, m => m.Name == "ToString").Count()); + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs index 8ee3262aa5..3c17dd65f3 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs @@ -19,4 +19,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver get { return true; } } } + + public class AmbiguousMemberResultResult : MemberResolveResult + { + public AmbiguousMemberResultResult(IMember member, IType returnType) : base(member, returnType) + { + } + + public override bool IsError { + get { return true; } + } + } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 7059a293e1..fda25cb2fc 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -1341,20 +1341,28 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region ResolveSimpleName + enum SimpleNameLookupMode + { + Expression, + Type, + TypeInUsingDeclaration + } + public ResolveResult ResolveSimpleName(string identifier, IList typeArguments) { // C# 4.0 spec: §7.6.2 Simple Names // TODO: lookup in local variables, in parameters, etc. - return LookupSimpleNameOrTypeName(identifier, typeArguments, false); + return LookupSimpleNameOrTypeName(identifier, typeArguments, SimpleNameLookupMode.Expression); } - public ResolveResult LookupSimpleNamespaceOrTypeName(string identifier, IList typeArguments) + public ResolveResult LookupSimpleNamespaceOrTypeName(string identifier, IList typeArguments, bool isUsingDeclaration = false) { - return LookupSimpleNameOrTypeName(identifier, typeArguments, true); + return LookupSimpleNameOrTypeName(identifier, typeArguments, + isUsingDeclaration ? SimpleNameLookupMode.TypeInUsingDeclaration : SimpleNameLookupMode.Type); } - ResolveResult LookupSimpleNameOrTypeName(string identifier, IList typeArguments, bool typeOnly) + ResolveResult LookupSimpleNameOrTypeName(string identifier, IList typeArguments, SimpleNameLookupMode lookupMode) { // C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names @@ -1381,11 +1389,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - if (typeOnly) { - // TODO: perform member lookup within the type t, restricted to finding types + if (lookupMode == SimpleNameLookupMode.Expression) { + // TODO: perform member lookup within the type t } else { - // TODO: perform member lookup within the type t + // TODO: perform member lookup within the type t, restricted to finding types + } } // look in current namespace definitions @@ -1415,41 +1424,66 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (k == 0) { if (n.ExternAliases.Contains(identifier)) { // TODO: implement extern alias support - throw new NotImplementedException(); + return new NamespaceResolveResult(string.Empty); } - foreach (var pair in n.UsingAliases) { - if (pair.Key == identifier) { - string ns = pair.Value.ResolveNamespace(context); - if (ns != null) - return new NamespaceResolveResult(ns); - else - return new TypeResolveResult(pair.Value.Resolve(context)); + if (lookupMode != SimpleNameLookupMode.TypeInUsingDeclaration || n != this.UsingScope) { + foreach (var pair in n.UsingAliases) { + if (pair.Key == identifier) { + NamespaceResolveResult ns = pair.Value.ResolveNamespace(context); + if (ns != null) + return ns; + else + return new TypeResolveResult(pair.Value.Resolve(context)); + } } } } // finally, look in the imported namespaces: - IType firstResult = null; - foreach (var u in n.Usings) { - string ns = u.ResolveNamespace(context); - if (ns != null) { - fullName = NamespaceDeclaration.BuildQualifiedName(ns, identifier); - def = context.GetClass(ns, k, StringComparer.Ordinal); - if (firstResult == null) { - if (k == 0) - firstResult = def; - else - firstResult = new ParameterizedType(def, typeArguments); - } else { - return new AmbiguousTypeResolveResult(firstResult); + if (lookupMode != SimpleNameLookupMode.TypeInUsingDeclaration || n != this.UsingScope) { + IType firstResult = null; + foreach (var u in n.Usings) { + NamespaceResolveResult ns = u.ResolveNamespace(context); + if (ns != null) { + fullName = NamespaceDeclaration.BuildQualifiedName(ns.NamespaceName, identifier); + def = context.GetClass(fullName, k, StringComparer.Ordinal); + if (firstResult == null) { + if (k == 0) + firstResult = def; + else + firstResult = new ParameterizedType(def, typeArguments); + } else { + return new AmbiguousTypeResolveResult(firstResult); + } } } + if (firstResult != null) + return new TypeResolveResult(firstResult); } - if (firstResult != null) - return new TypeResolveResult(firstResult); // if we didn't find anything: repeat lookup with parent namespace } return ErrorResult; } + + /// + /// Looks up an alias (identifier in front of :: operator) + /// + public ResolveResult ResolveAlias(string identifier) + { + if (identifier == "global") + return new NamespaceResolveResult(string.Empty); + for (UsingScope n = this.UsingScope; n != null; n = n.Parent) { + if (n.ExternAliases.Contains(identifier)) { + // TODO: implement extern alias support + return new NamespaceResolveResult(string.Empty); + } + foreach (var pair in n.UsingAliases) { + if (pair.Key == identifier) { + return pair.Value.ResolveNamespace(context) ?? ErrorResult; + } + } + } + return ErrorResult; + } #endregion } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ITypeOrNamespaceReference.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ITypeOrNamespaceReference.cs index 53a4abf3de..fb8b80eda4 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ITypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ITypeOrNamespaceReference.cs @@ -11,6 +11,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public interface ITypeOrNamespaceReference : ITypeReference { - string ResolveNamespace(ITypeResolveContext context); + /// + /// Returns the namespace that is referenced; or null if no such namespace is found. + /// + NamespaceResolveResult ResolveNamespace(ITypeResolveContext context); } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs index 57285f6317..e3611030d6 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs @@ -3,6 +3,8 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -27,7 +29,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return true; return member.ReturnType.Resolve(context).IsDelegate(); } - #endregion ITypeResolveContext context; @@ -48,9 +49,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets whether is accessible in the current class. /// /// The entity to test - /// The type used to access the member, or null if no target is used (e.g. static method call) - /// true if the member is accessible - public bool IsAccessible(IEntity entity, IType typeOfReference) + /// Whether protected access is allowed. + /// True if the type of the reference is derived from the current class. + public bool IsAccessible(IEntity entity, bool allowProtectedAccess) { if (entity == null) throw new ArgumentNullException("entity"); @@ -63,14 +64,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case Accessibility.Public: return true; case Accessibility.Protected: - return IsProtectedAccessible(entity.DeclaringTypeDefinition, typeOfReference); + return allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition); case Accessibility.Internal: return IsInternalAccessible(entity.ProjectContent); case Accessibility.ProtectedOrInternal: - return IsProtectedAccessible(entity.DeclaringTypeDefinition, typeOfReference) + return (allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition)) || IsInternalAccessible(entity.ProjectContent); case Accessibility.ProtectedAndInternal: - return IsProtectedAccessible(entity.DeclaringTypeDefinition, typeOfReference) + return (allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition)) && IsInternalAccessible(entity.ProjectContent); default: throw new Exception("Invalid value for Accessibility"); @@ -82,19 +83,140 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return declaringProject != null && currentProject != null && declaringProject.InternalsVisibleTo(currentProject, context); } - bool IsProtectedAccessible(ITypeDefinition declaringType, IType typeOfReference) + bool IsProtectedAccessible(ITypeDefinition declaringType) { if (declaringType == currentTypeDefinition) return true; // PERF: this might hurt performance as this method is called several times (once for each member) // make sure resolving base types is cheap (caches?) or cache within the MemberLookup instance - if (currentTypeDefinition == null || !currentTypeDefinition.IsDerivedFrom(declaringType, context)) - return false; - if (typeOfReference == null) - return true; // no restriction on the type of reference - ITypeDefinition referenceDef = typeOfReference.GetDefinition(); - return referenceDef != null && referenceDef.IsDerivedFrom(currentTypeDefinition, context); + return currentTypeDefinition != null && currentTypeDefinition.IsDerivedFrom(declaringType, context); } #endregion + + /// + /// Performs a member lookup. + /// + public ResolveResult Lookup(IType type, string name, int typeParameterCount, bool isInvocation) + { + 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. + types.AddRange(type.GetNestedTypes(context, + d => d.TypeParameterCount == typeParameterCount + && d.Name == name && IsAccessible(d, true))); + } + + ITypeDefinition typeDef = type.GetDefinition(); + bool allowProtectedAccess = typeDef != null && typeDef.IsDerivedFrom(currentTypeDefinition, context); + + if (typeParameterCount == 0) { + Predicate memberFilter = delegate(IMember member) { + return !member.IsOverride && member.Name == name && IsAccessible(member, allowProtectedAccess); + }; + members.AddRange(type.GetMethods(context, memberFilter)); + members.AddRange(type.GetProperties(context, memberFilter)); + members.AddRange(type.GetFields(context, memberFilter)); + members.AddRange(type.GetEvents(context, memberFilter)); + 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 == typeParameterCount + && !method.IsOverride && method.Name == name && IsAccessible(method, allowProtectedAccess); + }; + members.AddRange(type.GetMethods(context, memberFilter)); + } + + // remove types hidden by other types + for (int i = types.Count - 1; i >= 0; i--) { + ITypeDefinition d = GetDeclaringTypeDef(types[i]); + if (d == null) + continue; + // nested loop depends on the fact that the members of more derived classes appear later in the list + for (int j = i + 1; j < types.Count; j++) { + if (types[i].TypeParameterCount != types[j].TypeParameterCount) + continue; + ITypeDefinition s = GetDeclaringTypeDef(types[j]); + if (s != null && s != d && s.IsDerivedFrom(d, context)) { + // types[j] hides types[i] + types.RemoveAt(i); + break; + } + } + } + // remove members hidden by types + for (int i = 0; i < types.Count; i++) { + ITypeDefinition d = GetDeclaringTypeDef(types[i]); + if (d != null) + members.RemoveAll(m => d.IsDerivedFrom(m.DeclaringTypeDefinition, context)); + } + // remove members hidden by other members + for (int i = members.Count - 1; i >= 0; i--) { + ITypeDefinition d = members[i].DeclaringTypeDefinition; + IMethod mi = members[i] as IMethod; + // nested loop depends on the fact that the members of more derived classes appear later in the list + for (int j = i + 1; j < members.Count; j++) { + if (mi != null) { + IMethod mj = members[j] as IMethod; + if (mj != null && !ParameterListComparer.Instance.Equals(mi, mj)) + continue; + } + ITypeDefinition s = members[j].DeclaringTypeDefinition; + if (s != null && s != d && s.IsDerivedFrom(d, context)) { + // members[j] hides members[i] + members.RemoveAt(i); + break; + } + } + } + // remove interface members hidden by class members + if (type is ITypeParameter) { + // this can happen only with type parameters + for (int i = members.Count - 1; i >= 0; i--) { + ITypeDefinition d = members[i].DeclaringTypeDefinition; + if (d.ClassType != ClassType.Interface) + continue; + IMethod mi = members[i] as IMethod; + for (int j = 0; j < members.Count; j++) { + if (mi != null) { + IMethod mj = members[j] as IMethod; + if (mj != null && !ParameterListComparer.Instance.Equals(mi, mj)) + continue; + } + ITypeDefinition s = members[j].DeclaringTypeDefinition; + if (s != null && IsNonInterfaceType(s)) { + // members[j] hides members[i] + members.RemoveAt(i); + break; + } + } + } + } + + if (types.Count == 1 && members.Count == 0) + return new TypeResolveResult(types[0]); + if (types.Count > 0) + return new AmbiguousTypeResolveResult(types[0]); + IMember firstNonMethod = members.FirstOrDefault(m => !(m is IMethod)); + if (members.Count == 1 && firstNonMethod != null) + return new MemberResolveResult(firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); + if (firstNonMethod == null) + return new MethodGroupResolveResult(members.ConvertAll(m => (IMethod)m)); + return new AmbiguousMemberResultResult(firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); + } + + static bool IsNonInterfaceType(ITypeDefinition def) + { + return def.ClassType != ClassType.Interface && !(def.Name == "Object" && def.Namespace == "System" && def.TypeParameterCount == 0); + } + + static ITypeDefinition GetDeclaringTypeDef(IType type) + { + IType declType = type.DeclaringType; + return declType != null ? declType.GetDefinition() : null; + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs new file mode 100644 index 0000000000..75e47eba4e --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents the result of a member invocation. + /// + public class MemberResolveResult : ResolveResult + { + readonly IMember member; + + public MemberResolveResult(IMember member, IType returnType) : base(returnType) + { + this.member = member; + } + + public IMember Member { + get { return member; } + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs new file mode 100644 index 0000000000..a698c8be89 --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents a group of methods. + /// + public class MethodGroupResolveResult : ResolveResult + { + readonly ReadOnlyCollection methods; + + public MethodGroupResolveResult(IList methods) : base(SharedTypes.UnknownType) + { + if (methods == null) + throw new ArgumentNullException("methods"); + this.methods = new ReadOnlyCollection(methods); + } + + public ReadOnlyCollection Methods { + get { return methods; } + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs b/ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs index cbd4528469..280ac98994 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs @@ -9,44 +9,42 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp.Resolver { /// - /// Represents a simple C# name. (a single identifier with an optional list of type arguments) + /// Represents a simple C# name. (a single non-qualified identifier with an optional list of type arguments) /// public sealed class SimpleTypeOrNamespaceReference : ITypeOrNamespaceReference { - readonly IMember parentMember; readonly ITypeDefinition parentTypeDefinition; readonly UsingScope parentUsingScope; readonly string identifier; readonly IList typeArguments; + readonly bool isInUsingDeclaration; - public SimpleTypeOrNamespaceReference(string identifier, IList typeArguments, IMember parentMember, ITypeDefinition parentTypeDefinition, UsingScope parentUsingScope) + public SimpleTypeOrNamespaceReference(string identifier, IList typeArguments, ITypeDefinition parentTypeDefinition, UsingScope parentUsingScope, bool isInUsingDeclaration = false) { if (identifier == null) throw new ArgumentNullException("identifier"); this.identifier = identifier; this.typeArguments = typeArguments ?? EmptyList.Instance; - this.parentMember = parentMember; this.parentTypeDefinition = parentTypeDefinition; this.parentUsingScope = parentUsingScope; + this.isInUsingDeclaration = isInUsingDeclaration; } ResolveResult DoResolve(ITypeResolveContext context) { CSharpResolver r = new CSharpResolver(context); - r.CurrentMember = parentMember; r.CurrentTypeDefinition = parentTypeDefinition.GetCompoundClass(); r.UsingScope = parentUsingScope; IType[] typeArgs = new IType[typeArguments.Count]; for (int i = 0; i < typeArgs.Length; i++) { typeArgs[i] = typeArguments[i].Resolve(context); } - return r.LookupSimpleNamespaceOrTypeName(identifier, typeArgs); + return r.LookupSimpleNamespaceOrTypeName(identifier, typeArgs, isInUsingDeclaration); } - public string ResolveNamespace(ITypeResolveContext context) + public NamespaceResolveResult ResolveNamespace(ITypeResolveContext context) { - NamespaceResolveResult nrr = DoResolve(context) as NamespaceResolveResult; - return nrr != null ? nrr.NamespaceName : null; + return DoResolve(context) as NamespaceResolveResult; } public IType Resolve(ITypeResolveContext context) diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 53c16e9397..bfa9ecae9e 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -147,6 +147,8 @@ + + @@ -212,6 +214,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs index 8fcbc60b0c..f15f100f5a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs @@ -46,22 +46,55 @@ namespace ICSharpCode.NRefactory.TypeSystem return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions; } + static readonly GetClassTypeReference systemArray = new GetClassTypeReference("System.Array", 0); + static readonly GetClassTypeReference listInterface = new GetClassTypeReference("System.Collections.Generic.IList", 1); + public override IEnumerable GetBaseTypes(ITypeResolveContext context) { List baseTypes = new List(); - // PERF: if profiling shows the GetClass(typeof()) here to be a problem, create - // a static cache for the ITypeDefinitions - ITypeDefinition t = context.GetClass(typeof(Array)); - if (t != null) + IType t = systemArray.Resolve(context); + if (t != SharedTypes.UnknownType) baseTypes.Add(t); if (dimensions == 1) { // single-dimensional arrays implement IList - t = context.GetClass(typeof(IList<>)); - if (t != null) - baseTypes.Add(new ParameterizedType(t, new[] { elementType })); + ITypeDefinition def = listInterface.Resolve(context) as ITypeDefinition; + if (def != null) + baseTypes.Add(new ParameterizedType(def, new[] { elementType })); } return baseTypes; } + public override IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) + { + return systemArray.Resolve(context).GetMethods(context, filter); + } + + public override IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) + { + ITypeDefinition arrayDef = systemArray.Resolve(context) as ITypeDefinition; + if (arrayDef != null) { + foreach (IProperty p in arrayDef.GetProperties(context, filter)) { + yield return p; + } + DefaultProperty indexer = new DefaultProperty(arrayDef, "Items") { + ReturnType = elementType, + Accessibility = Accessibility.Public, + GetterAccessibility = Accessibility.Public, + SetterAccessibility = Accessibility.Public, + CanGet = true, + CanSet = true, + IsIndexer = true, + IsSynthetic = true + }; + indexer.Freeze(); + if (filter == null || filter(indexer)) { + yield return indexer; + } + } + } + + // Events, Fields: System.Array doesn't have any; so we can use the AbstractType default implementation + // that simply returns an empty list + public override IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitArrayType(this); diff --git a/ICSharpCode.NRefactory/TypeSystem/IType.cs b/ICSharpCode.NRefactory/TypeSystem/IType.cs index 28dc4c569b..d8cec40917 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IType.cs @@ -60,44 +60,34 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Gets inner classes (including inherited inner classes). /// - /// - /// If the inner class is generic, this method produces s that - /// parameterize each nested class with its own type parameters. - /// TODO: does this make sense? ConstructedType needs it, but maybe it would be better to build - /// those self-parameterized types only in ConstructedType? - /// - IEnumerable GetNestedTypes(ITypeResolveContext context); - - // TODO: PERF maybe give GetMethods/GetProperties/etc a filter predicate - // that allows filtering the members pre-substitution? - // that could dramatically decrease the number of substitutions we have to perform + IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null); /// /// Gets all methods that can be called on this return type. /// /// The list does not include constructors. - IEnumerable GetMethods(ITypeResolveContext context); + IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null); /// /// Gets all instance constructors for this type. /// /// This list does not include constructors in base classes or static constructors. - IEnumerable GetConstructors(ITypeResolveContext context); + IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null); /// /// Gets all properties that can be called on this return type. /// - IEnumerable GetProperties(ITypeResolveContext context); + IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null); /// /// Gets all fields that can be called on this return type. /// - IEnumerable GetFields(ITypeResolveContext context); + IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null); /// /// Gets all events that can be called on this return type. /// - IEnumerable GetEvents(ITypeResolveContext context); + IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null); } [ContractClassFor(typeof(IType))] @@ -125,42 +115,42 @@ namespace ICSharpCode.NRefactory.TypeSystem return null; } - IEnumerable IType.GetNestedTypes(ITypeResolveContext context) + IEnumerable IType.GetNestedTypes(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); return null; } - IEnumerable IType.GetMethods(ITypeResolveContext context) + IEnumerable IType.GetMethods(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); return null; } - IEnumerable IType.GetConstructors(ITypeResolveContext context) + IEnumerable IType.GetConstructors(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); return null; } - IEnumerable IType.GetProperties(ITypeResolveContext context) + IEnumerable IType.GetProperties(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); return null; } - IEnumerable IType.GetFields(ITypeResolveContext context) + IEnumerable IType.GetFields(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); return null; } - IEnumerable IType.GetEvents(ITypeResolveContext context) + IEnumerable IType.GetEvents(ITypeResolveContext context, Predicate filter) { Contract.Requires(context != null); Contract.Ensures(Contract.Result>() != null); diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs index e074d43b56..b2f981df6a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs @@ -61,32 +61,32 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return EmptyList.Instance; } - public virtual IEnumerable GetNestedTypes(ITypeResolveContext context) + public virtual IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - - public virtual IEnumerable GetMethods(ITypeResolveContext context) + + public virtual IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public virtual IEnumerable GetConstructors(ITypeResolveContext context) + public virtual IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public virtual IEnumerable GetProperties(ITypeResolveContext context) + public virtual IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public virtual IEnumerable GetFields(ITypeResolveContext context) + public virtual IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public virtual IEnumerable GetEvents(ITypeResolveContext context) + public virtual IEnumerable GetEvents(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 f4a7e74d33..d70429b3a2 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs @@ -377,11 +377,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return this; } - public virtual IEnumerable GetNestedTypes(ITypeResolveContext context) + public virtual IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetNestedTypes(context); + return compound.GetNestedTypes(context, filter); List nestedTypes = new List(); using (var busyLock = BusyManager.Enter(this)) { @@ -391,15 +391,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null && baseTypeDef.ClassType != ClassType.Interface) { // get nested types from baseType (not baseTypeDef) so that generics work correctly - nestedTypes.AddRange(baseType.GetNestedTypes(context)); + nestedTypes.AddRange(baseType.GetNestedTypes(context, filter)); break; // there is at most 1 non-interface base } } foreach (ITypeDefinition innerClass in this.InnerClasses) { - if (innerClass.TypeParameterCount > 0) { - // Parameterize inner classes with their own type parameters, as per on IType.GetNestedTypes. - nestedTypes.Add(new ParameterizedType(innerClass, innerClass.TypeParameters)); - } else { + if (filter == null || filter(innerClass)) { nestedTypes.Add(innerClass); } } @@ -408,113 +405,151 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return nestedTypes; } - public virtual IEnumerable GetMethods(ITypeResolveContext context) + public virtual IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetMethods(context); + return compound.GetMethods(context, filter); List methods = new List(); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { - foreach (var baseTypeRef in this.BaseTypes) { - IType baseType = baseTypeRef.Resolve(context); + int baseCount = 0; + foreach (var baseType in GetBaseTypes(context)) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null && (baseTypeDef.ClassType != ClassType.Interface || this.ClassType == ClassType.Interface)) { - methods.AddRange(baseType.GetMethods(context)); + methods.AddRange(baseType.GetMethods(context, filter)); + baseCount++; } } - methods.AddRange(this.Methods.Where(m => !m.IsConstructor)); + if (baseCount > 1) + RemoveDuplicates(methods); + AddFilteredRange(methods, this.Methods.Where(m => !m.IsConstructor), filter); } } return methods; } - public virtual IEnumerable GetConstructors(ITypeResolveContext context) + public virtual IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetConstructors(context); + return compound.GetConstructors(context, filter); List methods = new List(); - methods.AddRange(this.Methods.Where(m => m.IsConstructor && !m.IsStatic)); + AddFilteredRange(methods, this.Methods.Where(m => m.IsConstructor && !m.IsStatic), filter); if (this.AddDefaultConstructorIfRequired) { if (this.ClassType == ClassType.Class && methods.Count == 0 || this.ClassType == ClassType.Enum || this.ClassType == ClassType.Struct) { - methods.Add(DefaultMethod.CreateDefaultConstructor(this)); + var m = DefaultMethod.CreateDefaultConstructor(this); + if (filter == null || filter(m)) + methods.Add(m); } } return methods; } - public virtual IEnumerable GetProperties(ITypeResolveContext context) + public virtual IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetProperties(context); + return compound.GetProperties(context, filter); List properties = new List(); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { - foreach (var baseTypeRef in this.BaseTypes) { - IType baseType = baseTypeRef.Resolve(context); + int baseCount = 0; + foreach (var baseType in GetBaseTypes(context)) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null && (baseTypeDef.ClassType != ClassType.Interface || this.ClassType == ClassType.Interface)) { - properties.AddRange(baseType.GetProperties(context)); + properties.AddRange(baseType.GetProperties(context, filter)); + baseCount++; } } - properties.AddRange(this.Properties); + if (baseCount > 1) + RemoveDuplicates(properties); + AddFilteredRange(properties, this.Properties, filter); } } return properties; } - public virtual IEnumerable GetFields(ITypeResolveContext context) + public virtual IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetFields(context); + return compound.GetFields(context, filter); List fields = new List(); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { - foreach (var baseTypeRef in this.BaseTypes) { - IType baseType = baseTypeRef.Resolve(context); + int baseCount = 0; + foreach (var baseType in GetBaseTypes(context)) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null && (baseTypeDef.ClassType != ClassType.Interface || this.ClassType == ClassType.Interface)) { - fields.AddRange(baseType.GetFields(context)); + fields.AddRange(baseType.GetFields(context, filter)); + baseCount++; } } - fields.AddRange(this.Fields); + if (baseCount > 1) + RemoveDuplicates(fields); + AddFilteredRange(fields, this.Fields, filter); } } return fields; } - public virtual IEnumerable GetEvents(ITypeResolveContext context) + public virtual IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null) { ITypeDefinition compound = GetCompoundClass(); if (compound != this) - return compound.GetEvents(context); + return compound.GetEvents(context, filter); List events = new List(); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { - foreach (var baseTypeRef in this.BaseTypes) { - IType baseType = baseTypeRef.Resolve(context); + int baseCount = 0; + foreach (var baseType in GetBaseTypes(context)) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null && (baseTypeDef.ClassType != ClassType.Interface || this.ClassType == ClassType.Interface)) { - events.AddRange(baseType.GetEvents(context)); + events.AddRange(baseType.GetEvents(context, filter)); + baseCount++; } } - events.AddRange(this.Events); + if (baseCount > 1) + RemoveDuplicates(events); + AddFilteredRange(events, this.Events, filter); } } return events; } + static void AddFilteredRange(List targetList, IEnumerable sourceList, Predicate filter) where T : class + { + if (filter == null) { + targetList.AddRange(sourceList); + } else { + foreach (T element in sourceList) { + if (filter(element)) + targetList.Add(element); + } + } + } + + /// + /// Removes duplicate members from the list. + /// This is necessary when the same member can be inherited twice due to multiple inheritance. + /// + static void RemoveDuplicates(List list) where T : class + { + if (list.Count > 1) { + HashSet hash = new HashSet(); + list.RemoveAll(m => !hash.Add(m)); + } + } + // we use reference equality bool IEquatable.Equals(IType other) { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs index 1d137aa494..7964ff0346 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -231,22 +231,23 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return c; } - public IEnumerable GetConstructors(ITypeResolveContext context) + public IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { if (HasDefaultConstructorConstraint || HasValueTypeConstraint) { - return new [] { DefaultMethod.CreateDefaultConstructor(GetDummyClassForTypeParameter()) }; - } else { - return EmptyList.Instance; + DefaultMethod m = DefaultMethod.CreateDefaultConstructor(GetDummyClassForTypeParameter()); + if (filter(m)) + return new [] { m }; } + return EmptyList.Instance; } - public IEnumerable GetMethods(ITypeResolveContext context) + public IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { // TODO: get methods from constraints IType objectType = context.GetClass("System.Object", 0, StringComparer.Ordinal); IEnumerable objectMethods; if (objectType != null) - objectMethods = objectType.GetMethods(context); + objectMethods = objectType.GetMethods(context, filter); else objectMethods = EmptyList.Instance; @@ -254,22 +255,22 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return objectMethods.Where(m => !m.IsStatic); } - public IEnumerable GetProperties(ITypeResolveContext context) + public IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public IEnumerable GetFields(ITypeResolveContext context) + public IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - public IEnumerable GetEvents(ITypeResolveContext context) + public IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } - IEnumerable IType.GetNestedTypes(ITypeResolveContext context) + IEnumerable IType.GetNestedTypes(ITypeResolveContext context, Predicate filter = null) { return EmptyList.Instance; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs index 6daad440ce..cebee4244c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs @@ -17,27 +17,27 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.IsSealed = true; } - public override IEnumerable GetConstructors(ITypeResolveContext context) + public override IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; } - public override IEnumerable GetEvents(ITypeResolveContext context) + public override IEnumerable GetEvents(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; } - public override IEnumerable GetFields(ITypeResolveContext context) + public override IEnumerable GetFields(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; } - public override IEnumerable GetMethods(ITypeResolveContext context) + public override IEnumerable GetMethods(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; } - public override IEnumerable GetProperties(ITypeResolveContext context) + public override IEnumerable GetProperties(ITypeResolveContext context, Predicate filter) { return EmptyList.Instance; } diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs new file mode 100644 index 0000000000..e488f6def8 --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + public sealed class ParameterListComparer : IEqualityComparer + { + public static readonly ParameterListComparer Instance = new ParameterListComparer(); + + public bool Equals(IParameterizedMember x, IParameterizedMember y) + { + var px = x.Parameters; + var py = y.Parameters; + if (px.Count != py.Count) + return false; + for (int i = 0; i < px.Count; i++) { + if (!px[i].Type.Equals(py[i].Type)) + return false; + } + return true; + } + + public int GetHashCode(IParameterizedMember obj) + { + int hashCode = obj.Parameters.Count; + unchecked { + foreach (IParameter p in obj.Parameters) { + hashCode *= 27; + hashCode += p.Type.GetHashCode(); + } + } + return hashCode; + } + } +} diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs index f224b601b5..7fb857149c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs @@ -158,7 +158,7 @@ namespace ICSharpCode.NRefactory.TypeSystem return genericType.GetBaseTypes(context).Select(substitution.Apply); } - public IEnumerable GetNestedTypes(ITypeResolveContext context) + public IEnumerable GetNestedTypes(ITypeResolveContext context, Predicate filter = null) { /* class Base { @@ -172,17 +172,30 @@ namespace ICSharpCode.NRefactory.TypeSystem Base.GetNestedTypes() = { Base`1+Nested } where T2 = copy of T in Base`1+Nested */ Substitution substitution = new Substitution(typeArguments); - List types = genericType.GetNestedTypes(context).ToList(); + List types = genericType.GetNestedTypes(context, filter).ToList(); for (int i = 0; i < types.Count; i++) { - types[i] = types[i].AcceptVisitor(substitution); + ITypeDefinition def = types[i] as ITypeDefinition; + if (def != null && def.TypeParameterCount > 0) { + // (partially) parameterize the nested type definition + IType[] newTypeArgs = new IType[def.TypeParameterCount]; + for (int j = 0; j < newTypeArgs.Length; j++) { + if (j < typeArguments.Length) + newTypeArgs[j] = typeArguments[i]; + else + newTypeArgs[j] = def.TypeParameters[j]; + } + types[i] = new ParameterizedType(def, newTypeArgs); + } else { + types[i] = types[i].AcceptVisitor(substitution); + } } return types; } - public IEnumerable GetMethods(ITypeResolveContext context) + public IEnumerable GetMethods(ITypeResolveContext context, Predicate filter = null) { Substitution substitution = new Substitution(typeArguments); - List methods = genericType.GetMethods(context).ToList(); + List methods = genericType.GetMethods(context, filter).ToList(); for (int i = 0; i < methods.Count; i++) { SpecializedMethod m = new SpecializedMethod(methods[i]); m.SetDeclaringType(this); @@ -192,10 +205,10 @@ namespace ICSharpCode.NRefactory.TypeSystem return methods; } - public IEnumerable GetConstructors(ITypeResolveContext context) + public IEnumerable GetConstructors(ITypeResolveContext context, Predicate filter = null) { Substitution substitution = new Substitution(typeArguments); - List methods = genericType.GetConstructors(context).ToList(); + List methods = genericType.GetConstructors(context, filter).ToList(); for (int i = 0; i < methods.Count; i++) { SpecializedMethod m = new SpecializedMethod(methods[i]); m.SetDeclaringType(this); @@ -205,10 +218,10 @@ namespace ICSharpCode.NRefactory.TypeSystem return methods; } - public IEnumerable GetProperties(ITypeResolveContext context) + public IEnumerable GetProperties(ITypeResolveContext context, Predicate filter = null) { Substitution substitution = new Substitution(typeArguments); - List properties = genericType.GetProperties(context).ToList(); + List properties = genericType.GetProperties(context, filter).ToList(); for (int i = 0; i < properties.Count; i++) { SpecializedProperty p = new SpecializedProperty(properties[i]); p.SetDeclaringType(this); @@ -218,10 +231,10 @@ namespace ICSharpCode.NRefactory.TypeSystem return properties; } - public IEnumerable GetFields(ITypeResolveContext context) + public IEnumerable GetFields(ITypeResolveContext context, Predicate filter = null) { Substitution substitution = new Substitution(typeArguments); - List fields = genericType.GetFields(context).ToList(); + List fields = genericType.GetFields(context, filter).ToList(); for (int i = 0; i < fields.Count; i++) { SpecializedField f = new SpecializedField(fields[i]); f.SetDeclaringType(this); @@ -231,10 +244,10 @@ namespace ICSharpCode.NRefactory.TypeSystem return fields; } - public IEnumerable GetEvents(ITypeResolveContext context) + public IEnumerable GetEvents(ITypeResolveContext context, Predicate filter = null) { Substitution substitution = new Substitution(typeArguments); - List events = genericType.GetEvents(context).ToList(); + List events = genericType.GetEvents(context, filter).ToList(); for (int i = 0; i < events.Count; i++) { SpecializedEvent e = new SpecializedEvent(events[i]); e.SetDeclaringType(this); diff --git a/README b/README index 73587367d1..af63079446 100644 --- a/README +++ b/README @@ -94,6 +94,7 @@ A: This question is a bit difficult to answer. But of course, this does not mean that everything is thread-safe. First off, there's no hidden static state, so any two operations working on independent data can be executed concurrently. + [Actually, sometimes static state is used for caches, but those uses are thread-safe.] TODO: what about the C# parser? gmcs is full of static state... Some instance methods may use hidden instance state, so it is not safe to e.g use an instance of the CSharp.Resolver.Conversions class @@ -120,7 +121,7 @@ A: This question is a bit difficult to answer. results (e.g. because another thread updated a class definition). Also, there's a performance problem: if you have a composite of 15 SimpleProjectContents and the resolve algorithm requests 100 types, that's 1500 times entering and leaving the read-lock. - Moreoever, the ITypeResolveContext methods that return collections need to create a copy of the collection. + Moreoever, internal caches in the library are not used when passing a mutable ITypeResolveContext. The solution is to make the read lock more coarse-grained: using (var syncContext = compositeTypeResolveContext.Synchronize()) { @@ -128,7 +129,8 @@ A: This question is a bit difficult to answer. } On the call to Synchronize(), all 15 SimpleProjectContents are locked for reading. The return value "syncContext" can then be used to access the type resolve context without further synchronization overhead. - Once the return value is disposed, the read-locks are released. + It is guaranteed not to change (within the using block), so the library may cache some information. (TODO: give example of a cache) + Once the return value is disposed, the read-locks are released (and the caches are cleared). Q: What format do the .ToString() methods use?