You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
585 lines
17 KiB
585 lines
17 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/> |
|
// <version>$Revision$</version> |
|
// </file> |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Drawing; |
|
|
|
using ICSharpCode.Core; |
|
|
|
namespace ICSharpCode.SharpDevelop.Dom |
|
{ |
|
#region ResolveResult |
|
/// <summary> |
|
/// The base class of all resolve results. |
|
/// This class is used whenever an expression is not one of the special expressions |
|
/// (having their own ResolveResult class). |
|
/// The ResolveResult specified the location where Resolve was called (Class+Member) |
|
/// and the type of the resolved expression. |
|
/// </summary> |
|
public class ResolveResult |
|
{ |
|
IClass callingClass; |
|
IMember callingMember; |
|
IReturnType resolvedType; |
|
|
|
public ResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType) |
|
{ |
|
this.callingClass = callingClass; |
|
this.callingMember = callingMember; |
|
this.resolvedType = resolvedType; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the class that contained the expression used to get this ResolveResult. |
|
/// Can be null when the class is unknown. |
|
/// </summary> |
|
public IClass CallingClass { |
|
get { |
|
return callingClass; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the member (method or property in <see cref="CallingClass"/>) that contained the |
|
/// expression used to get this ResolveResult. |
|
/// Can be null when the expression was not inside a member or the member is unknown. |
|
/// </summary> |
|
public IMember CallingMember { |
|
get { |
|
return callingMember; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the type of the resolved expression. |
|
/// Can be null when the type cannot be represented by a IReturnType (e.g. when the |
|
/// expression was a namespace name). |
|
/// </summary> |
|
public IReturnType ResolvedType { |
|
get { |
|
return resolvedType; |
|
} |
|
set { |
|
resolvedType = value; |
|
} |
|
} |
|
|
|
public virtual ArrayList GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return GetCompletionData(projectContent.Language, false); |
|
} |
|
|
|
protected ArrayList GetCompletionData(LanguageProperties language, bool showStatic) |
|
{ |
|
if (resolvedType == null) return null; |
|
ArrayList res = new ArrayList(); |
|
bool isClassInInheritanceTree = false; |
|
if (callingClass != null) |
|
isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(resolvedType.GetUnderlyingClass()); |
|
foreach (IMethod m in resolvedType.GetMethods()) { |
|
if (language.ShowMember(m, showStatic) && m.IsAccessible(callingClass, isClassInInheritanceTree)) |
|
res.Add(m); |
|
} |
|
foreach (IEvent e in resolvedType.GetEvents()) { |
|
if (language.ShowMember(e, showStatic) && e.IsAccessible(callingClass, isClassInInheritanceTree)) |
|
res.Add(e); |
|
} |
|
foreach (IField f in resolvedType.GetFields()) { |
|
if (language.ShowMember(f, showStatic) && f.IsAccessible(callingClass, isClassInInheritanceTree)) |
|
res.Add(f); |
|
} |
|
foreach (IProperty p in resolvedType.GetProperties()) { |
|
if (language.ShowMember(p, showStatic) && p.IsAccessible(callingClass, isClassInInheritanceTree)) |
|
res.Add(p); |
|
} |
|
|
|
if (!showStatic && callingClass != null) { |
|
AddExtensions(language, res, callingClass, resolvedType); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
/// <summary> |
|
/// Adds extension methods to <paramref name="res"/>. |
|
/// </summary> |
|
public static void AddExtensions(LanguageProperties language, ArrayList res, IClass callingClass, IReturnType resolvedType) |
|
{ |
|
if (language == null) |
|
throw new ArgumentNullException("language"); |
|
if (res == null) |
|
throw new ArgumentNullException("res"); |
|
if (callingClass == null) |
|
throw new ArgumentNullException("callingClass"); |
|
if (resolvedType == null) |
|
throw new ArgumentNullException("resolvedType"); |
|
|
|
bool supportsExtensionMethods = language.SupportsExtensionMethods; |
|
bool supportsExtensionProperties = language.SupportsExtensionProperties; |
|
if (supportsExtensionMethods || supportsExtensionProperties) { |
|
ArrayList list = new ArrayList(); |
|
IMethod dummyMethod = new DefaultMethod("dummy", VoidReturnType.Instance, ModifierEnum.Static, DomRegion.Empty, DomRegion.Empty, callingClass); |
|
NRefactoryResolver.NRefactoryResolver.AddContentsFromCalling(list, callingClass, dummyMethod); |
|
NRefactoryResolver.NRefactoryResolver.AddImportedNamespaceContents(list, callingClass.CompilationUnit, callingClass); |
|
|
|
bool searchExtensionsInClasses = language.SearchExtensionsInClasses; |
|
foreach (object o in list) { |
|
if (supportsExtensionMethods && o is IMethod || supportsExtensionProperties && o is IProperty) { |
|
TryAddExtension(language, res, o as IMethodOrProperty, resolvedType); |
|
} else if (searchExtensionsInClasses && o is IClass) { |
|
IClass c = o as IClass; |
|
if (c.HasExtensionMethods) { |
|
if (supportsExtensionProperties) { |
|
foreach (IProperty p in c.Properties) { |
|
TryAddExtension(language, res, p, resolvedType); |
|
} |
|
} |
|
if (supportsExtensionMethods) { |
|
foreach (IMethod m in c.Methods) { |
|
TryAddExtension(language, res, m, resolvedType); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void TryAddExtension(LanguageProperties language, ArrayList res, IMethodOrProperty ext, IReturnType resolvedType) |
|
{ |
|
// accept only extension methods |
|
if (!ext.IsExtensionMethod) |
|
return; |
|
// don't add extension if method with that name already exists |
|
// but allow overloading extension methods |
|
foreach (IMember member in res) { |
|
IMethodOrProperty p = member as IMethodOrProperty; |
|
if (p != null && p.IsExtensionMethod) |
|
continue; |
|
if (language.NameComparer.Equals(member.Name, ext.Name)) { |
|
return; |
|
} |
|
} |
|
// now add the extension method if it fits the type |
|
if (MemberLookupHelper.ConversionExists(resolvedType, ext.Parameters[0].ReturnType)) { |
|
IMethod method = ext as IMethod; |
|
if (method != null && method.TypeParameters.Count > 0) { |
|
IReturnType[] typeArguments = new IReturnType[method.TypeParameters.Count]; |
|
MemberLookupHelper.InferTypeArgument(method.Parameters[0].ReturnType, resolvedType, typeArguments); |
|
for (int i = 0; i < typeArguments.Length; i++) { |
|
if (typeArguments[i] != null) { |
|
ext = (IMethod)ext.Clone(); |
|
ext.ReturnType = ConstructedReturnType.TranslateType(ext.ReturnType, typeArguments, true); |
|
for (int j = 0; j < ext.Parameters.Count; ++j) { |
|
ext.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(ext.Parameters[j].ReturnType, typeArguments, true); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
res.Add(ext); |
|
} |
|
} |
|
|
|
public virtual FilePosition GetDefinitionPosition() |
|
{ |
|
// this is only possible on some subclasses of ResolveResult |
|
return null; |
|
} |
|
} |
|
#endregion |
|
|
|
#region MixedResolveResult |
|
/// <summary> |
|
/// The MixedResolveResult is used when an expression can have multiple meanings, for example |
|
/// "Size" in a class deriving from "Control". |
|
/// </summary> |
|
public class MixedResolveResult : ResolveResult |
|
{ |
|
ResolveResult primaryResult, secondaryResult; |
|
|
|
public ResolveResult PrimaryResult { |
|
get { |
|
return primaryResult; |
|
} |
|
} |
|
|
|
public IEnumerable<ResolveResult> Results { |
|
get { |
|
yield return primaryResult; |
|
yield return secondaryResult; |
|
} |
|
} |
|
|
|
public TypeResolveResult TypeResult { |
|
get { |
|
if (primaryResult is TypeResolveResult) |
|
return (TypeResolveResult)primaryResult; |
|
if (secondaryResult is TypeResolveResult) |
|
return (TypeResolveResult)secondaryResult; |
|
return null; |
|
} |
|
} |
|
|
|
public MixedResolveResult(ResolveResult primaryResult, ResolveResult secondaryResult) |
|
: base(primaryResult.CallingClass, primaryResult.CallingMember, primaryResult.ResolvedType) |
|
{ |
|
this.primaryResult = primaryResult; |
|
this.secondaryResult = secondaryResult; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
return primaryResult.GetDefinitionPosition(); |
|
} |
|
|
|
public override ArrayList GetCompletionData(IProjectContent projectContent) |
|
{ |
|
ArrayList result = primaryResult.GetCompletionData(projectContent); |
|
ArrayList result2 = secondaryResult.GetCompletionData(projectContent); |
|
if (result == null) return result2; |
|
if (result2 == null) return result; |
|
foreach (object o in result2) { |
|
if (!result.Contains(o)) |
|
result.Add(o); |
|
} |
|
return result; |
|
} |
|
} |
|
#endregion |
|
|
|
#region LocalResolveResult |
|
/// <summary> |
|
/// The LocalResolveResult is used when an expression was a simple local variable |
|
/// or parameter. |
|
/// </summary> |
|
/// <remarks> |
|
/// For fields in the current class, a MemberResolveResult is used, so "e" is not always |
|
/// a LocalResolveResult. |
|
/// </remarks> |
|
public class LocalResolveResult : ResolveResult |
|
{ |
|
IField field; |
|
bool isParameter; |
|
|
|
public LocalResolveResult(IMember callingMember, IField field) |
|
: base(callingMember.DeclaringType, callingMember, field.ReturnType) |
|
{ |
|
if (callingMember == null) |
|
throw new ArgumentNullException("callingMember"); |
|
if (field == null) |
|
throw new ArgumentNullException("field"); |
|
this.field = field; |
|
this.isParameter = field.IsParameter; |
|
if (!isParameter && !field.IsLocalVariable) { |
|
throw new ArgumentException("the field must either be a local variable-field or a parameter-field"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the field representing the local variable. |
|
/// </summary> |
|
public IField Field { |
|
get { |
|
return field; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets if the variable is a parameter (true) or a local variable (false). |
|
/// </summary> |
|
public bool IsParameter { |
|
get { |
|
return isParameter; |
|
} |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
ICompilationUnit cu = this.CallingClass.CompilationUnit; |
|
if (cu == null) { |
|
return null; |
|
} |
|
if (cu.FileName == null || cu.FileName.Length == 0) { |
|
return null; |
|
} |
|
DomRegion reg = field.Region; |
|
if (!reg.IsEmpty) { |
|
return new FilePosition(cu.FileName, new Point(reg.BeginLine, reg.BeginColumn)); |
|
} else { |
|
LoggingService.Warn("GetDefinitionPosition: field.Region is empty"); |
|
return new FilePosition(cu.FileName, Point.Empty); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region NamespaceResolveResult |
|
/// <summary> |
|
/// The NamespaceResolveResult is used when an expression was the name of a namespace. |
|
/// <see cref="ResolveResult.ResolvedType"/> is always null on a NamespaceReturnType. |
|
/// </summary> |
|
/// <example> |
|
/// Example expressions: |
|
/// "System" |
|
/// "System.Windows.Forms" |
|
/// "using Win = System.Windows; Win.Forms" |
|
/// </example> |
|
public class NamespaceResolveResult : ResolveResult |
|
{ |
|
string name; |
|
|
|
public NamespaceResolveResult(IClass callingClass, IMember callingMember, string name) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
if (name == null) |
|
throw new ArgumentNullException("name"); |
|
this.name = name; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the namespace. |
|
/// </summary> |
|
public string Name { |
|
get { |
|
return name; |
|
} |
|
} |
|
|
|
public override ArrayList GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return projectContent.GetNamespaceContents(name); |
|
} |
|
} |
|
#endregion |
|
|
|
#region IntegerLiteralResolveResult |
|
/// <summary> |
|
/// The IntegerLiteralResolveResult is used when an expression was an integer literal. |
|
/// It is a normal ResolveResult with a type of "int", but does not provide |
|
/// any code completion data. |
|
/// </summary> |
|
public class IntegerLiteralResolveResult : ResolveResult |
|
{ |
|
public IntegerLiteralResolveResult(IClass callingClass, IMember callingMember, IReturnType systemInt32) |
|
: base(callingClass, callingMember, systemInt32) |
|
{ |
|
} |
|
|
|
public override ArrayList GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return null; |
|
} |
|
} |
|
#endregion |
|
|
|
#region TypeResolveResult |
|
/// <summary> |
|
/// The TypeResolveResult is used when an expression was the name of a type. |
|
/// This resolve result makes code completion show the static members instead |
|
/// of the instance members. |
|
/// </summary> |
|
/// <example> |
|
/// Example expressions: |
|
/// "System.EventArgs" |
|
/// "using System; EventArgs" |
|
/// </example> |
|
public class TypeResolveResult : ResolveResult |
|
{ |
|
IClass resolvedClass; |
|
|
|
public TypeResolveResult(IClass callingClass, IMember callingMember, IClass resolvedClass) |
|
: base(callingClass, callingMember, resolvedClass.DefaultReturnType) |
|
{ |
|
this.resolvedClass = resolvedClass; |
|
} |
|
|
|
public TypeResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType, IClass resolvedClass) |
|
: base(callingClass, callingMember, resolvedType) |
|
{ |
|
this.resolvedClass = resolvedClass; |
|
} |
|
|
|
public TypeResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType) |
|
: base(callingClass, callingMember, resolvedType) |
|
{ |
|
this.resolvedClass = resolvedType.GetUnderlyingClass(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the class corresponding to the resolved type. |
|
/// This property can be null when the type has no class (for example a type parameter). |
|
/// </summary> |
|
public IClass ResolvedClass { |
|
get { |
|
return resolvedClass; |
|
} |
|
} |
|
|
|
public override ArrayList GetCompletionData(IProjectContent projectContent) |
|
{ |
|
ArrayList ar = GetCompletionData(projectContent.Language, true); |
|
if (resolvedClass != null) { |
|
foreach (IClass baseClass in resolvedClass.ClassInheritanceTree) { |
|
ar.AddRange(baseClass.InnerClasses); |
|
} |
|
} |
|
return ar; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
if (resolvedClass == null) { |
|
return null; |
|
} |
|
ICompilationUnit cu = resolvedClass.CompilationUnit; |
|
if (cu == null || cu.FileName == null || cu.FileName.Length == 0) { |
|
return null; |
|
} |
|
DomRegion reg = resolvedClass.Region; |
|
if (!reg.IsEmpty) |
|
return new FilePosition(cu.FileName, new Point(reg.BeginLine, reg.BeginColumn)); |
|
else |
|
return new FilePosition(cu.FileName, Point.Empty); |
|
} |
|
} |
|
#endregion |
|
|
|
#region MemberResolveResult |
|
/// <summary> |
|
/// The TypeResolveResult is used when an expression was a member |
|
/// (field, property, event or method call). |
|
/// </summary> |
|
/// <example> |
|
/// Example expressions: |
|
/// "(any expression).fieldName" |
|
/// "(any expression).eventName" |
|
/// "(any expression).propertyName" |
|
/// "(any expression).methodName(arguments)" (methods only when method parameters are part of expression) |
|
/// "using System; EventArgs.Empty" |
|
/// "fieldName" (when fieldName is a field in the current class) |
|
/// "new System.Windows.Forms.Button()" (constructors are also methods) |
|
/// "SomeMethod()" (when SomeMethod is a method in the current class) |
|
/// "System.Console.WriteLine(text)" |
|
/// </example> |
|
public class MemberResolveResult : ResolveResult |
|
{ |
|
IMember resolvedMember; |
|
|
|
public MemberResolveResult(IClass callingClass, IMember callingMember, IMember resolvedMember) |
|
: base(callingClass, callingMember, resolvedMember.ReturnType) |
|
{ |
|
if (resolvedMember == null) |
|
throw new ArgumentNullException("resolvedMember"); |
|
this.resolvedMember = resolvedMember; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the member that was resolved. |
|
/// </summary> |
|
public IMember ResolvedMember { |
|
get { |
|
return resolvedMember; |
|
} |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
return GetDefinitionPosition(resolvedMember); |
|
} |
|
|
|
internal static FilePosition GetDefinitionPosition(IMember resolvedMember) |
|
{ |
|
IClass declaringType = resolvedMember.DeclaringType; |
|
if (declaringType == null) { |
|
return null; |
|
} |
|
ICompilationUnit cu = declaringType.CompilationUnit; |
|
if (cu == null) { |
|
return null; |
|
} |
|
if (cu.FileName == null || cu.FileName.Length == 0) { |
|
return null; |
|
} |
|
DomRegion reg = resolvedMember.Region; |
|
if (!reg.IsEmpty) |
|
return new FilePosition(cu.FileName, new Point(reg.BeginLine, reg.BeginColumn)); |
|
else |
|
return new FilePosition(cu.FileName, Point.Empty); |
|
} |
|
} |
|
#endregion |
|
|
|
#region MethodResolveResult |
|
/// <summary> |
|
/// The MethodResolveResult is used when an expression was the name of a method, |
|
/// but there were no parameters specified so the exact overload could not be found. |
|
/// <see cref="ResolveResult.ResolvedType"/> is always null on a MethodReturnType. |
|
/// </summary> |
|
/// <example> |
|
/// Example expressions: |
|
/// "System.Console.WriteLine" |
|
/// "a.Add" (where a is List<string>) |
|
/// "SomeMethod" (when SomeMethod is a method in the current class) |
|
/// </example> |
|
public class MethodResolveResult : ResolveResult |
|
{ |
|
string name; |
|
IReturnType containingType; |
|
|
|
public MethodResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
if (containingType == null) |
|
throw new ArgumentNullException("containingType"); |
|
if (name == null) |
|
throw new ArgumentNullException("name"); |
|
this.containingType = containingType; |
|
this.name = name; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the method. |
|
/// </summary> |
|
public string Name { |
|
get { |
|
return name; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the class that contains the method. |
|
/// </summary> |
|
public IReturnType ContainingType { |
|
get { |
|
return containingType; |
|
} |
|
} |
|
|
|
public IMethod GetMethodIfSingleOverload() |
|
{ |
|
List<IMethod> methods = containingType.GetMethods(); |
|
methods = methods.FindAll(delegate(IMethod m) { return m.Name == this.Name; }); |
|
if (methods.Count == 1) |
|
return methods[0]; |
|
else |
|
return null; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
IMethod m = GetMethodIfSingleOverload(); |
|
if (m != null) |
|
return MemberResolveResult.GetDefinitionPosition(m); |
|
else |
|
return base.GetDefinitionPosition(); |
|
} |
|
} |
|
#endregion |
|
}
|
|
|