diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs index f652ef4c1e..c490f9b72b 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs @@ -17,7 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -141,6 +140,87 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion + #region GetAccessibleMembers + /// + /// Retrieves all members that are accessible and not hidden (by being overridden or shadowed). + /// Returns both members and nested type definitions. Does not include extension methods. + /// + public IEnumerable GetAccessibleMembers(ResolveResult targetResolveResult) + { + if (targetResolveResult == null) + throw new ArgumentNullException("targetResolveResult"); + + bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; + bool allowProtectedAccess = (targetResolveResult is ThisResolveResult || IsProtectedAccessAllowed(targetResolveResult.Type)); + + // maps the member name to the list of lookup groups + var lookupGroupDict = new Dictionary>(); + + // This loop will handle base types before derived types. + // The loop performs three jobs: + // 1) It marks entries in lookup groups from base classes as removed when those members + // are hidden by a derived class. + // 2) It adds a new lookup group with the members from a declaring type. + // 3) It replaces virtual members with the overridden version, placing the override in the + // lookup group belonging to the base class. + foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { + + List entities = new List(); + entities.AddRange(type.GetMembers(options: GetMemberOptions.IgnoreInheritedMembers)); + if (!targetIsTypeParameter) + entities.AddRange(type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions).Select(t => t.GetDefinition())); + + foreach (var entityGroup in entities.GroupBy(e => e.Name)) { + + List lookupGroups = new List(); + if (!lookupGroupDict.TryGetValue(entityGroup.Key, out lookupGroups)) + lookupGroupDict.Add(entityGroup.Key, lookupGroups = new List()); + + List newNestedTypes = null; + List newMethods = null; + IMember newNonMethod = null; + + IEnumerable typeBaseTypes = null; + + if (!targetIsTypeParameter) { + AddNestedTypes(type, entityGroup.OfType(), 0, lookupGroups, ref typeBaseTypes, ref newNestedTypes); + } + AddMembers(type, entityGroup.OfType(), allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); + + if (newNestedTypes != null || newMethods != null || newNonMethod != null) + lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); + } + } + + foreach (List lookupGroups in lookupGroupDict.Values) { + // Remove interface members hidden by class members. + if (targetIsTypeParameter) { + // This can happen only with type parameters. + RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); + } + + // Now report the results: + foreach (LookupGroup lookupGroup in lookupGroups) { + if (!lookupGroup.MethodsAreHidden) { + foreach (IMethod method in lookupGroup.Methods) { + yield return method; + } + } + if (!lookupGroup.NonMethodIsHidden) { + yield return lookupGroup.NonMethod; + } + if (lookupGroup.NestedTypes != null) { + foreach (IType type in lookupGroup.NestedTypes) { + ITypeDefinition typeDef = type.GetDefinition(); + if (typeDef != null) + yield return typeDef; + } + } + } + } + } + #endregion + #region class LookupGroup sealed class LookupGroup {