diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index d498aa65ae..e818b6a5a0 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -805,10 +805,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } var or = rr.PerformOverloadResolution(compilation, args, allowExpandingParams: false, conversions: this); - if (or.FoundApplicableCandidate) - return Conversion.MethodGroupConversion((IMethod)or.GetBestCandidateWithSubstitutedTypeArguments()); - else + if (or.FoundApplicableCandidate) { + IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments(); + var thisRR = rr.TargetResult as ThisResolveResult; + bool isVirtual = method.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation); + return Conversion.MethodGroupConversion(method, isVirtual); + } else { return Conversion.None; + } } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs index 1b86a8a00a..4bdd9d9dd2 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.NRefactory.CSharp.Refactoring; @@ -143,6 +144,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } internal string searchTerm; + internal FindReferences findReferences; internal ICompilation declarationCompilation; internal Accessibility accessibility; internal ITypeDefinition topLevelTypeDefinition; @@ -152,6 +154,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver FindReferenceNavigator n = factory(compilation); if (n != null) { n.callback = callback; + n.findReferences = findReferences; return n; } else { return new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null); @@ -178,6 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver abstract class FindReferenceNavigator : IResolveVisitorNavigator { internal FoundReferenceCallback callback; + internal FindReferences findReferences; internal abstract bool CanMatch(AstNode node); internal abstract bool IsMatch(ResolveResult rr); @@ -218,6 +222,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { if (entity == null) throw new ArgumentNullException("entity"); + if (entity is IMember) + entity = NormalizeMember((IMember)entity); Accessibility effectiveAccessibility = GetEffectiveAccessibility(entity); ITypeDefinition topLevelTypeDefinition = entity.DeclaringTypeDefinition; while (topLevelTypeDefinition != null && topLevelTypeDefinition.DeclaringTypeDefinition != null) @@ -252,7 +258,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver scope = GetSearchScopeForOperator((IMethod)entity); break; case EntityType.Constructor: - IMethod ctor = (IMethod)((IMethod)entity).MemberDefinition; + IMethod ctor = (IMethod)entity; scope = FindObjectCreateReferences(ctor); additionalScope = FindChainedConstructorReferences(ctor); break; @@ -266,11 +272,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver scope.accessibility = effectiveAccessibility; scope.declarationCompilation = entity.Compilation; scope.topLevelTypeDefinition = topLevelTypeDefinition; + scope.findReferences = this; if (additionalScope != null) { if (additionalScope.accessibility == Accessibility.None) additionalScope.accessibility = effectiveAccessibility; additionalScope.declarationCompilation = entity.Compilation; additionalScope.topLevelTypeDefinition = topLevelTypeDefinition; + additionalScope.findReferences = this; return new[] { scope, additionalScope }; } else { return new[] { scope }; @@ -418,17 +426,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (searchTerm.Length > 9 && searchTerm.EndsWith("Attribute", StringComparison.Ordinal)) { // The type might be an attribute, so we also need to look for the short form: string shortForm = searchTerm.Substring(0, searchTerm.Length - 9); - additionalScope = new SearchScope( - shortForm, - delegate (ICompilation compilation) { - ITypeDefinition imported = compilation.Import(typeDefinition); - if (imported != null) - return new FindTypeDefinitionReferencesNavigator(imported, shortForm); - else - return null; - }); + additionalScope = FindTypeDefinitionReferences(typeDefinition, shortForm); } } + return FindTypeDefinitionReferences(typeDefinition, searchTerm); + } + + SearchScope FindTypeDefinitionReferences(ITypeDefinition typeDefinition, string searchTerm) + { return new SearchScope( searchTerm, delegate (ICompilation compilation) { @@ -495,11 +500,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver SearchScope FindMemberReferences(IEntity member, Func factory) { string searchTerm = member.Name; - IMember memberDefinition = ((IMember)member).MemberDefinition; return new SearchScope( searchTerm, delegate(ICompilation compilation) { - IMember imported = compilation.Import(memberDefinition); + IMember imported = compilation.Import((IMember)member); return imported != null ? factory(imported) : null; }); } @@ -511,7 +515,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public FindMemberReferencesNavigator(IMember member) { - this.member = member.MemberDefinition; + this.member = member; this.searchTerm = member.Name; } @@ -539,10 +543,60 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && member == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(member, mrr.Member, mrr.IsVirtualCall); } } + IMember NormalizeMember(IMember member) + { + if (WholeVirtualSlot && member.IsOverride) + member = InheritanceHelper.GetBaseMembers(member, false).FirstOrDefault(m => !m.IsOverride) ?? member; + if (!FindOnlySpecializedReferences) + member = member.MemberDefinition; + return member; + } + + bool IsMemberMatch(IMember member, IMember referencedMember, bool isVirtualCall) + { + referencedMember = NormalizeMember(referencedMember); + if (member.Equals(referencedMember)) + return true; + if (!isVirtualCall) + return false; + bool isInterfaceCall = referencedMember.DeclaringTypeDefinition != null && referencedMember.DeclaringTypeDefinition.Kind == TypeKind.Interface; + if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot && !isInterfaceCall) { + // Test if 'member' overrides 'referencedMember': + foreach (var baseMember in InheritanceHelper.GetBaseMembers(member, false)) { + if (FindOnlySpecializedReferences) { + if (baseMember.Equals(referencedMember)) + return true; + } else { + if (baseMember.MemberDefinition.Equals(referencedMember)) + return true; + } + if (!baseMember.IsOverride) + break; + } + return false; + } else if (FindCallsThroughInterface && isInterfaceCall) { + // Test if 'member' implements 'referencedMember': + if (FindOnlySpecializedReferences) { + return member.ImplementedInterfaceMembers.Contains(referencedMember); + } else { + return member.ImplementedInterfaceMembers.Any(m => m.MemberDefinition.Equals(referencedMember)); + } + } + return false; + } + + bool PerformVirtualLookup(IMember member, IMember referencedMember) + { + if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot) + return true; + var typeDef = referencedMember.DeclaringTypeDefinition; + return FindCallsThroughInterface && typeDef != null && typeDef.Kind == TypeKind.Interface; + } + sealed class FindFieldReferences : FindMemberReferencesNavigator { public FindFieldReferences(IField field) : base(field) @@ -601,10 +655,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Find References to IEnumerator.Current SearchScope FindEnumeratorCurrentReferences(IProperty property) { - IProperty propertyDefinition = (IProperty)property.MemberDefinition; return new SearchScope( delegate(ICompilation compilation) { - IProperty imported = compilation.Import(propertyDefinition); + IProperty imported = compilation.Import(property); return imported != null ? new FindEnumeratorCurrentReferencesNavigator(imported) : null; }); } @@ -626,7 +679,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { ForEachResolveResult ferr = rr as ForEachResolveResult; - return ferr != null && ferr.CurrentProperty != null && ferr.CurrentProperty.MemberDefinition == property; + return ferr != null && ferr.CurrentProperty != null && findReferences.IsMemberMatch(property, ferr.CurrentProperty, true); } } #endregion @@ -634,8 +687,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Find Method References SearchScope GetSearchScopeForMethod(IMethod method) { - method = (IMethod)method.MemberDefinition; - Type specialNodeType; switch (method.Name) { case "Add": @@ -741,18 +792,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver var ferr = rr as ForEachResolveResult; if (ferr != null) { return IsMatch(ferr.GetEnumeratorCall) - || (ferr.MoveNextMethod != null && method == ferr.MoveNextMethod.MemberDefinition); + || (ferr.MoveNextMethod != null && findReferences.IsMemberMatch(method, ferr.MoveNextMethod, true)); } } var mrr = rr as MemberResolveResult; - return mrr != null && method == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(method, mrr.Member, mrr.IsVirtualCall); } internal override void NavigatorDone(CSharpAstResolver resolver, CancellationToken cancellationToken) { foreach (var expr in potentialMethodGroupConversions) { var conversion = resolver.GetConversion(expr, cancellationToken); - if (conversion.IsMethodGroupConversion && conversion.Method.MemberDefinition == method) { + if (conversion.IsMethodGroupConversion && findReferences.IsMemberMatch(method, conversion.Method, conversion.IsVirtualMethodLookup)) { IType targetType = resolver.GetExpectedType(expr, cancellationToken); ResolveResult result = resolver.Resolve(expr, cancellationToken); ReportMatch(expr, new ConversionResolveResult(targetType, result, conversion)); @@ -766,7 +817,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Find Indexer References SearchScope FindIndexerReferences(IProperty indexer) { - indexer = (IProperty)indexer.MemberDefinition; return new SearchScope( delegate (ICompilation compilation) { IProperty imported = compilation.Import(indexer); @@ -794,7 +844,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && indexer == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(indexer, mrr.Member, mrr.IsVirtualCall); } } #endregion @@ -867,7 +917,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver SearchScope FindOperator(IMethod op, Func factory) { - op = (IMethod)op.MemberDefinition; return new SearchScope( delegate (ICompilation compilation) { IMethod imported = compilation.Import(op); @@ -908,7 +957,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && op == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } } @@ -940,7 +989,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && op == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } } @@ -961,12 +1010,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && op == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } public override void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) { - if (conversion.IsUserDefined && conversion.Method.MemberDefinition == op) { + if (conversion.IsUserDefined && findReferences.IsMemberMatch(op, conversion.Method, conversion.IsVirtualMethodLookup)) { ReportMatch(expression, result); } } @@ -989,7 +1038,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { ConversionResolveResult crr = rr as ConversionResolveResult; - return crr != null && crr.Conversion.IsUserDefined && crr.Conversion.Method.MemberDefinition == op; + return crr != null && crr.Conversion.IsUserDefined + && findReferences.IsMemberMatch(op, crr.Conversion.Method, crr.Conversion.IsVirtualMethodLookup); } } #endregion @@ -997,7 +1047,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Find Constructor References SearchScope FindObjectCreateReferences(IMethod ctor) { - ctor = (IMethod)ctor.MemberDefinition; string searchTerm = null; if (KnownTypeReference.GetCSharpNameByTypeCode(ctor.DeclaringTypeDefinition.KnownTypeCode) == null) { // not a built-in type @@ -1035,13 +1084,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && ctor == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall); } } SearchScope FindChainedConstructorReferences(IMethod ctor) { - ctor = (IMethod)ctor.MemberDefinition; SearchScope searchScope = new SearchScope( delegate (ICompilation compilation) { IMethod imported = compilation.Import(ctor); @@ -1075,7 +1123,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && ctor == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall); } } #endregion @@ -1083,22 +1131,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Find Destructor References SearchScope GetSearchScopeForDestructor(IMethod dtor) { - dtor = (IMethod)dtor.MemberDefinition; - string searchTerm = null; - if (KnownTypeReference.GetCSharpNameByTypeCode(dtor.DeclaringTypeDefinition.KnownTypeCode) == null) { - // not a built-in type - searchTerm = dtor.DeclaringTypeDefinition.Name; - } - return new SearchScope ( - searchTerm, + var scope = new SearchScope ( delegate (ICompilation compilation) { - IMethod imported = compilation.Import(dtor); - if (imported != null) { - return new FindDestructorReferencesNavigator (imported); - } else { - return null; - } - }); + IMethod imported = compilation.Import(dtor); + if (imported != null) { + return new FindDestructorReferencesNavigator (imported); + } else { + return null; + } + }); + scope.accessibility = Accessibility.Private; + return scope; } sealed class FindDestructorReferencesNavigator : FindReferenceNavigator @@ -1118,7 +1161,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; - return mrr != null && dtor == mrr.Member.MemberDefinition; + return mrr != null && findReferences.IsMemberMatch(dtor, mrr.Member, mrr.IsVirtualCall); } } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index 6e05c625e9..c24f65f4bb 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -650,7 +650,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult result = errorResult; if (variableInitializer.Parent is FieldDeclaration || variableInitializer.Parent is EventDeclaration) { if (resolver.CurrentMember != null) { - result = new MemberResolveResult(null, resolver.CurrentMember); + result = new MemberResolveResult(null, resolver.CurrentMember, false); } } else { string identifier = variableInitializer.Name; @@ -707,7 +707,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (resolverEnabled) { ResolveResult result = errorResult; if (resolver.CurrentMember != null) { - result = new MemberResolveResult(null, resolver.CurrentMember); + result = new MemberResolveResult(null, resolver.CurrentMember, false); } ResolveAndProcessConversion(fixedVariableInitializer.CountExpression, resolver.Compilation.FindType(KnownTypeCode.Int32)); return result; @@ -726,7 +726,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ScanChildren(member); if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember); + return new MemberResolveResult(null, resolver.CurrentMember, false); else return errorResult; } finally { @@ -772,7 +772,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember); + return new MemberResolveResult(null, resolver.CurrentMember, false); else return errorResult; } finally { @@ -805,7 +805,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember); + return new MemberResolveResult(null, resolver.CurrentMember, false); else return errorResult; } finally { @@ -885,7 +885,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (resolverEnabled && resolver.CurrentTypeDefinition != null) { ResolveAndProcessConversion(enumMemberDeclaration.Initializer, resolver.CurrentTypeDefinition.EnumUnderlyingType); if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember); + return new MemberResolveResult(null, resolver.CurrentMember, false); else return errorResult; } else { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs index 30b76d561e..1e38e226d5 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs @@ -33,24 +33,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver CompilationUnit compilationUnit; CSharpParsedFile parsedFile; ICompilation compilation; + FindReferences findReferences; void Init(string code) { compilationUnit = new CSharpParser().Parse(new StringReader(code), "test.cs"); parsedFile = compilationUnit.ToTypeSystem(); compilation = TypeSystemHelper.CreateCompilation(parsedFile); + findReferences = new FindReferences(); } AstNode[] FindReferences(IEntity entity) { var result = new List(); - var findReferences = new FindReferences(); var searchScopes = findReferences.GetSearchScopes(entity); findReferences.FindReferencesInFile(searchScopes, parsedFile, compilationUnit, compilation, (node, rr) => result.Add(node), CancellationToken.None); return result.OrderBy(n => n.StartLocation).ToArray(); } + #region Method Group [Test] public void FindMethodGroupReference() { @@ -107,7 +109,9 @@ class Test { Assert.AreEqual(new [] { new TextLocation(4, 49), new TextLocation(7, 2) }, FindReferences(m_string).Select(n => n.StartLocation).ToArray()); } + #endregion + #region GetEnumerator [Test] public void FindReferenceToGetEnumeratorUsedImplicitlyInForeach() { @@ -129,7 +133,9 @@ class Test { Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is ForeachStatement)); } + #endregion + #region Op_Implicit [Test] public void FindReferencesForOpImplicitInLocalVariableInitialization() { @@ -147,5 +153,63 @@ class Test { Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is ObjectCreateExpression)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 6 && r is OperatorDeclaration)); } + #endregion + + #region Inheritance + const string inheritanceTest = @"using System; +class A { public virtual void M() {} } +class B : A { public override void M() {} } +class C : A { public override void M() {} } +class Calls { + void Test(A a, B b, C c) { + a.M(); + b.M(); + c.M(); + } +}"; + + [Test] + public void InheritanceTest1() + { + Init(inheritanceTest); + var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); + var BM = test.Methods.Single(m => m.Name == "M"); + var actual = FindReferences(BM).ToList(); + Assert.AreEqual(2, actual.Count); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); + } + + [Test] + public void InheritanceTest2() + { + Init(inheritanceTest); + findReferences.FindCallsThroughVirtualBaseMethod = true; + var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); + var BM = test.Methods.Single(m => m.Name == "M"); + var actual = FindReferences(BM).ToList(); + Assert.AreEqual(3, actual.Count); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); + } + + [Test] + public void InheritanceTest3() + { + Init(inheritanceTest); + findReferences.WholeVirtualSlot = true; + var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "B"); + var BM = test.Methods.Single(m => m.Name == "M"); + var actual = FindReferences(BM).ToList(); + Assert.AreEqual(6, actual.Count); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 2 && r is MethodDeclaration)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is MethodDeclaration)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 7 && r is InvocationExpression)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is InvocationExpression)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 9 && r is InvocationExpression)); + } + #endregion } } diff --git a/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs b/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs index 9be979de08..12b9616cd3 100644 --- a/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs +++ b/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.Documentation /// public static class IdStringProvider { - #region GetIDString + #region GetIdString /// /// Gets the ID string (C# 4.0 spec, §A.3.1) for the specified entity. /// diff --git a/ICSharpCode.NRefactory/Semantics/Conversion.cs b/ICSharpCode.NRefactory/Semantics/Conversion.cs index 716aa92044..a68a7f407f 100644 --- a/ICSharpCode.NRefactory/Semantics/Conversion.cs +++ b/ICSharpCode.NRefactory/Semantics/Conversion.cs @@ -88,11 +88,11 @@ namespace ICSharpCode.NRefactory.Semantics return new UserDefinedConversion(false, operatorMethod, isLifted); } - public static Conversion MethodGroupConversion(IMethod chosenMethod) + public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup) { if (chosenMethod == null) throw new ArgumentNullException("chosenMethod"); - return new MethodGroupConv(chosenMethod); + return new MethodGroupConv(chosenMethod, isVirtualMethodLookup); } #endregion @@ -302,10 +302,12 @@ namespace ICSharpCode.NRefactory.Semantics sealed class MethodGroupConv : Conversion { readonly IMethod method; + readonly bool isVirtualMethodLookup; - public MethodGroupConv(IMethod method) + public MethodGroupConv(IMethod method, bool isVirtualMethodLookup) { this.method = method; + this.isVirtualMethodLookup = isVirtualMethodLookup; } public override bool IsImplicit { @@ -316,6 +318,10 @@ namespace ICSharpCode.NRefactory.Semantics get { return true; } } + public override bool IsVirtualMethodLookup { + get { return isVirtualMethodLookup; } + } + public override IMethod Method { get { return method; } } @@ -434,6 +440,13 @@ namespace ICSharpCode.NRefactory.Semantics get { return false; } } + /// + /// For method-group conversions, gets whether to perform a virtual method lookup at runtime. + /// + public virtual bool IsVirtualMethodLookup { + get { return false; } + } + /// /// Gets whether this conversion is an anonymous function conversion. /// diff --git a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs index ed03205c31..c0991eef8d 100644 --- a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs @@ -34,12 +34,30 @@ namespace ICSharpCode.NRefactory.Semantics readonly bool isConstant; readonly object constantValue; readonly ResolveResult targetResult; + readonly bool isVirtualCall; public MemberResolveResult(ResolveResult targetResult, IMember member) : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) { this.targetResult = targetResult; this.member = member; + var thisRR = targetResult as ThisResolveResult; + this.isVirtualCall = member.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation); + + IField field = member as IField; + if (field != null) { + isConstant = field.IsConst; + if (isConstant) + constantValue = field.ConstantValue; + } + } + + public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall) + : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) + { + this.targetResult = targetResult; + this.member = member; + this.isVirtualCall = isVirtualCall; IField field = member as IField; if (field != null) { isConstant = field.IsConst; @@ -69,6 +87,13 @@ namespace ICSharpCode.NRefactory.Semantics get { return member; } } + /// + /// Gets whether this MemberResolveResult is a virtual call. + /// + public bool IsVirtualCall { + get { return isVirtualCall; } + } + public override bool IsCompileTimeConstant { get { return isConstant; } } diff --git a/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs b/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs index 831b47638a..3666bf30be 100644 --- a/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs @@ -27,8 +27,18 @@ namespace ICSharpCode.NRefactory.Semantics /// public class ThisResolveResult : ResolveResult { - public ThisResolveResult(IType type) : base(type) + bool causesNonVirtualInvocation; + + public ThisResolveResult(IType type, bool causesNonVirtualInvocation = false) : base(type) { + this.causesNonVirtualInvocation = causesNonVirtualInvocation; + } + + /// + /// Gets whether this resolve result causes member invocations to be non-virtual. + /// + public bool CausesNonVirtualInvocation { + get { return causesNonVirtualInvocation; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs index 335f3413d6..bae1d5b9db 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.Documentation; using ICSharpCode.NRefactory.Utils; @@ -65,18 +66,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - public override DocumentationComment Documentation { - get { - IUnresolvedDocumentationProvider docProvider = unresolved.ParsedFile as IUnresolvedDocumentationProvider; - if (docProvider != null) { - var doc = docProvider.GetDocumentation(unresolved, this); - if (doc != null) - return doc; - } - return base.Documentation; - } - } - IList FindImplementedInterfaceMembers() { if (unresolved.IsExplicitInterfaceImplementation) { @@ -87,8 +76,25 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation result.Add(member); } return result.ToArray(); + } else if (unresolved.IsStatic) { + return EmptyList.Instance; } else { - throw new NotImplementedException(); + // TODO: implement interface member mappings correctly + return InheritanceHelper.GetBaseMembers(this, true) + .Where(m => m.DeclaringTypeDefinition != null && m.DeclaringTypeDefinition.Kind == TypeKind.Interface) + .ToArray(); + } + } + + public override DocumentationComment Documentation { + get { + IUnresolvedDocumentationProvider docProvider = unresolved.ParsedFile as IUnresolvedDocumentationProvider; + if (docProvider != null) { + var doc = docProvider.GetDocumentation(unresolved, this); + if (doc != null) + return doc; + } + return base.Documentation; } }