diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs
index b3b020579c..4d91e4e187 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs
@@ -6,11 +6,13 @@
//
using System;
+using System.Diagnostics;
using System.Collections.Generic;
using System.Drawing;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom;
+using ICSharpCode.SharpDevelop.Dom.Refactoring;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
@@ -177,58 +179,36 @@ namespace ICSharpCode.SharpDevelop.Refactoring
bool isLocal,
string fileName, string fileContent)
{
- string lowerFileContent = fileContent.ToLowerInvariant();
- string searchedText; // the text that is searched for
- bool searchingIndexer = false;
-
+ TextFinder textFinder; // the class used to find the position to resolve
if (member == null) {
- searchedText = parentClass.Name.ToLowerInvariant();
+ textFinder = parentClass.ProjectContent.Language.GetFindClassReferencesTextFinder(parentClass);
} else {
- // When looking for a member, the name of the parent class does not always exist
- // in the file where the member is accessed.
- // (examples: derived classes, partial classes)
- if (member is IMethod && ((IMethod)member).IsConstructor)
- searchedText = parentClass.Name.ToLowerInvariant();
- else {
- if (member is IProperty && ((IProperty)member).IsIndexer) {
- searchingIndexer = true;
- searchedText = GetIndexerExpressionStartToken(fileName);
- } else {
- searchedText = member.Name.ToLowerInvariant();
- }
- }
+ Debug.Assert(member.DeclaringType == parentClass);
+ textFinder = parentClass.ProjectContent.Language.GetFindMemberReferencesTextFinder(member);
}
// It is possible that a class or member does not have a name (when parsing incomplete class definitions)
// - in that case, we cannot find references.
- if (searchedText.Length == 0) {
+ if (textFinder == null) {
return;
}
- int pos = -1;
- int exprPos;
+ string fileContentForFinder = textFinder.PrepareInputText(fileContent);
+
IExpressionFinder expressionFinder = null;
- while ((pos = lowerFileContent.IndexOf(searchedText, pos + 1)) >= 0) {
- if (!searchingIndexer) {
- if (pos > 0 && char.IsLetterOrDigit(fileContent, pos - 1)) {
- continue; // memberName is not a whole word (a.SomeName cannot reference Name)
- }
- if (pos < fileContent.Length - searchedText.Length - 1
- && char.IsLetterOrDigit(fileContent, pos + searchedText.Length))
- {
- continue; // memberName is not a whole word (a.Name2 cannot reference Name)
- }
- exprPos = pos;
- } else {
- exprPos = pos-1; // indexer expressions are found by resolving the part before the indexer
- }
+ TextFinderMatch match = new TextFinderMatch(-1, 0);
+
+ while (true) {
+ match = textFinder.Find(fileContentForFinder, match.Position + 1);
+ if (match.Position < 0)
+ break;
if (expressionFinder == null) {
expressionFinder = ParserService.GetExpressionFinder(fileName);
}
- ExpressionResult expr = expressionFinder.FindFullExpression(fileContent, exprPos);
+ ExpressionResult expr = expressionFinder.FindFullExpression(fileContent, match.ResolvePosition);
if (expr.Expression != null) {
- Point position = GetPosition(fileContent, exprPos);
+ Point position = GetPosition(fileContent, match.ResolvePosition);
repeatResolve:
// TODO: Optimize by re-using the same resolver if multiple expressions were
// found in this file (the resolver should parse all methods at once)
@@ -237,14 +217,14 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (isLocal) {
// find reference to local variable
if (IsReferenceToLocalVariable(rr, member)) {
- list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr));
+ list.Add(new Reference(fileName, match.Position, match.Length, expr.Expression, rr));
} else if (FixIndexerExpression(expressionFinder, ref expr, mrr)) {
goto repeatResolve;
}
} else if (member != null) {
// find reference to member
if (IsReferenceToMember(member, rr)) {
- list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr));
+ list.Add(new Reference(fileName, match.Position, match.Length, expr.Expression, rr));
} else if (FixIndexerExpression(expressionFinder, ref expr, mrr)) {
goto repeatResolve;
}
@@ -253,12 +233,12 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (mrr != null) {
if (mrr.ResolvedMember is IMethod && ((IMethod)mrr.ResolvedMember).IsConstructor) {
if (mrr.ResolvedMember.DeclaringType.FullyQualifiedName == parentClass.FullyQualifiedName) {
- list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr));
+ list.Add(new Reference(fileName, match.Position, match.Length, expr.Expression, rr));
}
}
} else {
if (rr is TypeResolveResult && rr.ResolvedType.FullyQualifiedName == parentClass.FullyQualifiedName) {
- list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr));
+ list.Add(new Reference(fileName, match.Position, match.Length, expr.Expression, rr));
}
}
}
@@ -286,25 +266,6 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return false;
}
- ///
- /// Determines the token that denotes a possible beginning of an indexer
- /// expression in the specified file.
- ///
- static string GetIndexerExpressionStartToken(string fileName)
- {
- if (fileName != null) {
- ParseInformation pi = ParserService.GetParseInformation(fileName);
- if (pi != null &&
- pi.MostRecentCompilationUnit != null &&
- pi.MostRecentCompilationUnit.ProjectContent != null &&
- pi.MostRecentCompilationUnit.ProjectContent.Language != null) {
- return pi.MostRecentCompilationUnit.ProjectContent.Language.IndexerExpressionStartToken;
- }
- }
- LoggingService.Warn("RefactoringService: unable to determine the correct indexer expression start token for file '"+fileName+"'");
- return LanguageProperties.CSharp.IndexerExpressionStartToken;
- }
-
static Point GetPosition(string fileContent, int pos)
{
int line = 1;
@@ -547,6 +508,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public static IMember FindBaseMember(IMember member)
{
if (member == null) return null;
+ if (member is IMethod && (member as IMethod).IsConstructor) return null;
IClass parentClass = member.DeclaringType;
IClass baseClass = parentClass.BaseClass;
if (baseClass == null) return null;
diff --git a/src/Main/Base/Test/NRefactoryResolverTests.cs b/src/Main/Base/Test/NRefactoryResolverTests.cs
index c4eabdf20c..5b39060c0d 100644
--- a/src/Main/Base/Test/NRefactoryResolverTests.cs
+++ b/src/Main/Base/Test/NRefactoryResolverTests.cs
@@ -143,6 +143,44 @@ class TestClass {
// to the generic method.
Assert.IsTrue(Refactoring.RefactoringService.IsReferenceToMember(genericMethod, mrr));
}
+
+ [Test]
+ public void ConstructorBaseCall()
+ {
+ string program = @"using System;
+class A {
+ public A(int a) {}
+}
+class B : A {
+ public B(int a)
+ : base(a) /*7*/
+ {}
+}
+class C : B {
+ public C(int a)
+ : base(a) /*12*/
+ {}
+}
+";
+ MemberResolveResult mrr = Resolve(program, "new A(2)", 3);
+ IMember aCtor = mrr.ResolvedMember;
+ Assert.AreEqual("A.#ctor", mrr.ResolvedMember.FullyQualifiedName);
+
+ mrr = Resolve(program, "new B(2)", 3);
+ IMember bCtor = mrr.ResolvedMember;
+ Assert.AreEqual("B.#ctor", mrr.ResolvedMember.FullyQualifiedName);
+
+ mrr = Resolve(program, "base(a)", 7);
+ Assert.AreEqual("A.#ctor", mrr.ResolvedMember.FullyQualifiedName);
+
+ mrr = Resolve(program, "base(a)", 12);
+ Assert.AreEqual("B.#ctor", mrr.ResolvedMember.FullyQualifiedName);
+
+ // ensure that the reference pointing to the B ctor is not seen as a reference
+ // to the A ctor.
+ Assert.IsTrue(Refactoring.RefactoringService.IsReferenceToMember(bCtor, mrr));
+ Assert.IsFalse(Refactoring.RefactoringService.IsReferenceToMember(aCtor, mrr));
+ }
#endregion
#region Test for old issues (Fidalgo)
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj
index fa127d7bbb..305f9f02eb 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj
@@ -96,6 +96,7 @@
+
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs
index 20643852d4..5f75bdf2ef 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs
@@ -6,6 +6,8 @@
//
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using ICSharpCode.SharpDevelop.Dom.Refactoring;
namespace ICSharpCode.SharpDevelop.Dom
@@ -169,12 +171,27 @@ namespace ICSharpCode.SharpDevelop.Dom
}
///
- /// Gets the token that denotes a possible beginning of an indexer expression.
+ /// Gets the start token of an indexer expression in the language. Usually '[' or '('.
///
public virtual string IndexerExpressionStartToken {
get { return "["; }
}
+ public virtual TextFinder GetFindClassReferencesTextFinder(IClass c)
+ {
+ return new WholeWordTextFinder(c.Name, nameComparer);
+ }
+
+ public virtual TextFinder GetFindMemberReferencesTextFinder(IMember member)
+ {
+ IProperty property = member as IProperty;
+ if (property != null && property.IsIndexer) {
+ return new IndexBeforeTextFinder(IndexerExpressionStartToken);
+ } else {
+ return new WholeWordTextFinder(member.Name, nameComparer);
+ }
+ }
+
public virtual bool IsClassWithImplicitlyStaticMembers(IClass c)
{
return false;
@@ -259,6 +276,20 @@ namespace ICSharpCode.SharpDevelop.Dom
{
return "[LanguageProperties: C#]";
}
+
+ public override TextFinder GetFindMemberReferencesTextFinder(IMember member)
+ {
+ IMethod method = member as IMethod;
+ if (method != null && method.IsConstructor) {
+ return new CombinedTextFinder(
+ new WholeWordTextFinder(member.DeclaringType.Name, this.NameComparer),
+ new WholeWordTextFinder("this", this.NameComparer),
+ new WholeWordTextFinder("base", this.NameComparer)
+ );
+ } else {
+ return base.GetFindMemberReferencesTextFinder(member);
+ }
+ }
}
#endregion
@@ -293,12 +324,6 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
- public override string IndexerExpressionStartToken {
- get {
- return "(";
- }
- }
-
public override bool IsClassWithImplicitlyStaticMembers(IClass c)
{
return c.ClassType == ClassType.Module;
@@ -350,11 +375,112 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
+ public override string IndexerExpressionStartToken {
+ get { return "("; }
+ }
+
public override string ToString()
{
return "[LanguageProperties: VB.NET]";
}
}
#endregion
+
+ #region Text Finder
+ protected sealed class WholeWordTextFinder : TextFinder
+ {
+ readonly string searchedText;
+ readonly bool caseInsensitive;
+
+ public WholeWordTextFinder(string word, StringComparer nameComparer)
+ {
+ if (word == null) word = string.Empty;
+
+ caseInsensitive = nameComparer.Equals("a", "A");
+ if (caseInsensitive)
+ this.searchedText = word.ToLowerInvariant();
+ else
+ this.searchedText = word;
+ }
+
+ public override string PrepareInputText(string inputText)
+ {
+ if (caseInsensitive)
+ return inputText.ToLowerInvariant();
+ else
+ return inputText;
+ }
+
+ public override TextFinderMatch Find(string inputText, int startPosition)
+ {
+ if (searchedText.Length == 0)
+ return TextFinderMatch.Empty;
+ int pos = startPosition - 1;
+ while ((pos = inputText.IndexOf(searchedText, pos + 1)) >= 0) {
+ if (pos > 0 && char.IsLetterOrDigit(inputText, pos - 1)) {
+ continue; // memberName is not a whole word (a.SomeName cannot reference Name)
+ }
+ if (pos < inputText.Length - searchedText.Length - 1
+ && char.IsLetterOrDigit(inputText, pos + searchedText.Length))
+ {
+ continue; // memberName is not a whole word (a.Name2 cannot reference Name)
+ }
+ return new TextFinderMatch(pos, searchedText.Length);
+ }
+ return TextFinderMatch.Empty;
+ }
+ }
+
+ protected sealed class CombinedTextFinder : TextFinder
+ {
+ readonly TextFinder[] finders;
+
+ public CombinedTextFinder(params TextFinder[] finders)
+ {
+ if (finders == null)
+ throw new ArgumentNullException("finders");
+ if (finders.Length == 0)
+ throw new ArgumentException("finders.Length must be > 0");
+ this.finders = finders;
+ }
+
+ public override string PrepareInputText(string inputText)
+ {
+ return finders[0].PrepareInputText(inputText);
+ }
+
+ public override TextFinderMatch Find(string inputText, int startPosition)
+ {
+ TextFinderMatch best = TextFinderMatch.Empty;
+ foreach (TextFinder f in finders) {
+ TextFinderMatch r = f.Find(inputText, startPosition);
+ if (r.Position >= 0 && (best.Position < 0 || r.Position < best.Position)) {
+ best = r;
+ }
+ }
+ return best;
+ }
+ }
+
+ protected sealed class IndexBeforeTextFinder : TextFinder
+ {
+ readonly string searchText;
+
+ public IndexBeforeTextFinder(string searchText)
+ {
+ this.searchText = searchText;
+ }
+
+ public override TextFinderMatch Find(string inputText, int startPosition)
+ {
+ int pos = inputText.IndexOf(searchText, startPosition);
+ if (pos >= 0) {
+ return new TextFinderMatch(pos, searchText.Length, pos - 1);
+ } else {
+ return TextFinderMatch.Empty;
+ }
+ }
+ }
+ #endregion
}
}
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs
index a4c3bbe1ee..50fa9ce97d 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/TypeVisitor.cs
@@ -202,6 +202,15 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return GetVisualBasicIndexer(invocationExpression);
return FindOverload(methods, typeParameters, invocationExpression.Arguments);
} else {
+ if (resolver.Language == SupportedLanguage.CSharp && resolver.CallingClass != null) {
+ if (invocationExpression.TargetObject is ThisReferenceExpression) {
+ // call to constructor
+ return FindOverload(GetConstructors(resolver.CallingClass), typeParameters, invocationExpression.Arguments);
+ } else if (invocationExpression.TargetObject is BaseReferenceExpression) {
+ return FindOverload(GetConstructors(resolver.CallingClass.BaseClass), typeParameters, invocationExpression.Arguments);
+ }
+ }
+
// this could be a nested indexer access
if (resolver.Language == SupportedLanguage.VBNet)
return GetVisualBasicIndexer(invocationExpression);
@@ -209,6 +218,13 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return null;
}
+ IList GetConstructors(IClass c)
+ {
+ if (c == null)
+ return emptyMethodsArray;
+ return c.Methods.FindAll(delegate(IMethod m) { return m.IsConstructor; });
+ }
+
IProperty GetVisualBasicIndexer(InvocationExpression invocationExpression)
{
return GetIndexer(new IndexerExpression(invocationExpression.TargetObject, invocationExpression.Arguments));
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs
new file mode 100644
index 0000000000..52f1fbc6bf
--- /dev/null
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/TextFinder.cs
@@ -0,0 +1,45 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+namespace ICSharpCode.SharpDevelop.Dom.Refactoring
+{
+ public struct TextFinderMatch
+ {
+ public readonly int Position;
+ public readonly int Length;
+
+ public readonly int ResolvePosition;
+
+ public static readonly TextFinderMatch Empty = new TextFinderMatch(-1, 0);
+
+ public TextFinderMatch(int position, int length)
+ {
+ this.Position = position;
+ this.Length = length;
+ this.ResolvePosition = position;
+ }
+
+ public TextFinderMatch(int position, int length, int resolvePosition)
+ {
+ this.Position = position;
+ this.Length = length;
+ this.ResolvePosition = resolvePosition;
+ }
+ }
+
+ public abstract class TextFinder
+ {
+ public virtual string PrepareInputText(string inputText)
+ {
+ return inputText;
+ }
+
+ public abstract TextFinderMatch Find(string inputText, int startPosition);
+ }
+}