mirror of https://github.com/icsharpcode/ILSpy.git
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.
1060 lines
30 KiB
1060 lines
30 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.Ast; |
|
|
|
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 : AbstractFreezable, ICloneable |
|
{ |
|
IClass callingClass; |
|
IMember callingMember; |
|
IReturnType resolvedType; |
|
|
|
public ResolveResult(IClass callingClass, IMember callingMember, IReturnType resolvedType) |
|
{ |
|
this.callingClass = callingClass; |
|
this.callingMember = callingMember; |
|
this.resolvedType = resolvedType; |
|
} |
|
|
|
public virtual bool IsValid { |
|
get { return true; } |
|
} |
|
|
|
/// <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 { |
|
CheckBeforeMutation(); |
|
resolvedType = value; |
|
} |
|
} |
|
|
|
public virtual ResolveResult Clone() |
|
{ |
|
return new ResolveResult(callingClass, callingMember, resolvedType); |
|
} |
|
|
|
object ICloneable.Clone() |
|
{ |
|
return this.Clone(); |
|
} |
|
|
|
bool showAllNamespacesContentsInCC = false; |
|
/// <summary> |
|
/// Gets code completion data for this ResolveResult. |
|
/// </summary> |
|
/// <param name="projectContent"></param> |
|
/// <param name="showItemsFromAllNamespaces">If true, items (e.g. extension methods) from all namespaces are returned, regardless current imports. Default is false.</param> |
|
/// <returns></returns> |
|
public List<ICompletionEntry> GetCompletionData(IProjectContent projectContent, bool showItemsFromAllNamespaces) |
|
{ |
|
// Little hack - store value in a property to pass it to GetCompletionData(LanguageProperties language, bool showStatic) |
|
// Otherwise we would have to add it as a parameter to GetCompletionData(IProjectContent projectContent), |
|
// which would change signature in classes overriding this method as well. |
|
this.showAllNamespacesContentsInCC = showItemsFromAllNamespaces; |
|
var result = GetCompletionData(projectContent); |
|
this.showAllNamespacesContentsInCC = false; |
|
return result; |
|
} |
|
|
|
public virtual List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return GetCompletionData(projectContent.Language, false); |
|
} |
|
|
|
protected List<ICompletionEntry> GetCompletionData(LanguageProperties language, bool showStatic) |
|
{ |
|
if (resolvedType == null) return null; |
|
List<ICompletionEntry> res = new List<ICompletionEntry>(); |
|
|
|
foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(resolvedType, callingClass, language)) { |
|
if (language.ShowMember(m, showStatic)) |
|
res.Add(m); |
|
} |
|
|
|
if (!showStatic && callingClass != null) { |
|
AddExtensions(language, res.Add, callingClass, resolvedType, this.showAllNamespacesContentsInCC); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
/// <summary> |
|
/// Adds extension methods to <paramref name="res"/>. |
|
/// </summary> |
|
public static void AddExtensions(LanguageProperties language, Action<IMethodOrProperty> methodFound, IClass callingClass, IReturnType resolvedType, bool searchInAllNamespaces = false) |
|
{ |
|
if (language == null) |
|
throw new ArgumentNullException("language"); |
|
if (methodFound == null) |
|
throw new ArgumentNullException("methodFound"); |
|
if (resolvedType == null) |
|
throw new ArgumentNullException("resolvedType"); |
|
if (callingClass == null) |
|
throw new ArgumentNullException("callingClass"); |
|
|
|
// convert resolvedType into direct type to speed up the IsApplicable lookups |
|
resolvedType = resolvedType.GetDirectReturnType(); |
|
|
|
foreach (IMethodOrProperty mp in CtrlSpaceResolveHelper.FindAllExtensions(language, callingClass, searchInAllNamespaces)) { |
|
TryAddExtension(language, methodFound, mp, resolvedType); |
|
} |
|
} |
|
|
|
static void TryAddExtension(LanguageProperties language, Action<IMethodOrProperty> methodFound, IMethodOrProperty ext, IReturnType resolvedType) |
|
{ |
|
// now add the extension method if it fits the type |
|
if (MemberLookupHelper.IsApplicable(resolvedType, ext.Parameters[0].ReturnType, ext as IMethod)) { |
|
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.CreateSpecializedMember(); |
|
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; |
|
} |
|
} |
|
} |
|
methodFound(ext); |
|
} |
|
} |
|
|
|
public virtual FilePosition GetDefinitionPosition() |
|
{ |
|
// this is only possible on some subclasses of ResolveResult |
|
return FilePosition.Empty; |
|
} |
|
|
|
/// <summary> |
|
/// Gets if this ResolveResult represents a reference to the specified entity. |
|
/// </summary> |
|
public virtual bool IsReferenceTo(IEntity entity) |
|
{ |
|
return false; |
|
} |
|
} |
|
#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; |
|
|
|
protected override void FreezeInternal() |
|
{ |
|
base.FreezeInternal(); |
|
primaryResult.Freeze(); |
|
secondaryResult.Freeze(); |
|
} |
|
|
|
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) |
|
{ |
|
if (primaryResult == null) |
|
throw new ArgumentNullException("primaryResult"); |
|
if (secondaryResult == null) |
|
throw new ArgumentNullException("secondaryResult"); |
|
this.primaryResult = primaryResult; |
|
this.secondaryResult = secondaryResult; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
return primaryResult.GetDefinitionPosition(); |
|
} |
|
|
|
public override List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
List<ICompletionEntry> result = primaryResult.GetCompletionData(projectContent); |
|
List<ICompletionEntry> result2 = secondaryResult.GetCompletionData(projectContent); |
|
if (result == null) return result2; |
|
if (result2 == null) return result; |
|
foreach (ICompletionEntry o in result2) { |
|
if (!result.Contains(o)) |
|
result.Add(o); |
|
} |
|
return result; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new MixedResolveResult(primaryResult.Clone(), secondaryResult.Clone()); |
|
} |
|
|
|
public override bool IsReferenceTo(IEntity entity) |
|
{ |
|
return primaryResult.IsReferenceTo(entity) || secondaryResult.IsReferenceTo(entity); |
|
} |
|
} |
|
#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; |
|
|
|
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; |
|
if (!field.IsParameter && !field.IsLocalVariable) { |
|
throw new ArgumentException("the field must either be a local variable-field or a parameter-field"); |
|
} |
|
} |
|
|
|
public LocalResolveResult(IMember callingMember, IParameter parameter) |
|
: this(callingMember, new DefaultField.ParameterField(parameter.ReturnType, parameter.Name, parameter.Region, callingMember.DeclaringType)) |
|
{ |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new LocalResolveResult(this.CallingMember, this.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 field.IsParameter; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the parameter/local variable. |
|
/// </summary> |
|
public string VariableName { |
|
get { return field.Name; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets th position where the parameter/local variable is declared. |
|
/// </summary> |
|
public DomRegion VariableDefinitionRegion { |
|
get { return field.Region; } |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
ICompilationUnit cu = this.CallingClass.CompilationUnit; |
|
if (cu == null) { |
|
return FilePosition.Empty; |
|
} |
|
if (cu.FileName == null || cu.FileName.Length == 0) { |
|
return FilePosition.Empty; |
|
} |
|
DomRegion reg = field.Region; |
|
if (!reg.IsEmpty) { |
|
return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); |
|
} else { |
|
LoggingService.Warn("GetDefinitionPosition: field.Region is empty"); |
|
return new FilePosition(cu.FileName); |
|
} |
|
} |
|
|
|
public override bool IsReferenceTo(IEntity entity) |
|
{ |
|
IField f = entity as IField; |
|
if (f != null && (f.IsLocalVariable || f.IsParameter)) { |
|
return field.Region.BeginLine == f.Region.BeginLine |
|
&& field.Region.BeginColumn == f.Region.BeginColumn; |
|
} |
|
return base.IsReferenceTo(entity); |
|
} |
|
} |
|
#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 List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return projectContent.GetNamespaceContents(name); |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new NamespaceResolveResult(this.CallingClass, this.CallingMember, this.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 List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
return null; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new IntegerLiteralResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); |
|
} |
|
} |
|
#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(); |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new TypeResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType, this.ResolvedClass); |
|
} |
|
|
|
/// <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 List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
List<ICompletionEntry> ar = GetCompletionData(projectContent.Language, true); |
|
if (resolvedClass != null) { |
|
ar.AddRange(resolvedClass.GetCompoundClass().GetAccessibleTypes(CallingClass)); |
|
} |
|
return ar; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
if (resolvedClass == null) { |
|
return FilePosition.Empty; |
|
} |
|
ICompilationUnit cu = resolvedClass.CompilationUnit; |
|
if (cu == null || cu.FileName == null || cu.FileName.Length == 0) { |
|
return FilePosition.Empty; |
|
} |
|
DomRegion reg = resolvedClass.Region; |
|
if (!reg.IsEmpty) |
|
return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); |
|
else |
|
return new FilePosition(cu.FileName); |
|
} |
|
|
|
public override bool IsReferenceTo(IEntity entity) |
|
{ |
|
IClass c = entity as IClass; |
|
return c != null && resolvedClass != null |
|
&& resolvedClass.FullyQualifiedName == c.FullyQualifiedName |
|
&& resolvedClass.TypeParameters.Count == c.TypeParameters.Count; |
|
} |
|
} |
|
#endregion |
|
|
|
#region MemberResolveResult |
|
/// <summary> |
|
/// The MemberResolveResult 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; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new MemberResolveResult(this.CallingClass, this.CallingMember, this.ResolvedMember) { |
|
IsExtensionMethodCall = IsExtensionMethodCall |
|
}; |
|
} |
|
|
|
bool isExtensionMethodCall; |
|
|
|
public bool IsExtensionMethodCall { |
|
get { return isExtensionMethodCall; } |
|
set { |
|
CheckBeforeMutation(); |
|
isExtensionMethodCall = value; |
|
} |
|
} |
|
|
|
/// <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 FilePosition.Empty; |
|
} |
|
ICompilationUnit cu = declaringType.CompilationUnit; |
|
if (cu == null) { |
|
return FilePosition.Empty; |
|
} |
|
if (cu.FileName == null || cu.FileName.Length == 0) { |
|
return FilePosition.Empty; |
|
} |
|
DomRegion reg = resolvedMember.Region; |
|
if (!reg.IsEmpty) |
|
return new FilePosition(cu.FileName, reg.BeginLine, reg.BeginColumn); |
|
else |
|
return new FilePosition(cu.FileName); |
|
} |
|
|
|
public override bool IsReferenceTo(IEntity entity) |
|
{ |
|
IClass c = entity as IClass; |
|
if (c != null) { |
|
IMethod m = resolvedMember as IMethod; |
|
return m != null && m.IsConstructor |
|
&& m.DeclaringType.FullyQualifiedName == c.FullyQualifiedName |
|
&& m.DeclaringType.TypeParameters.Count == c.TypeParameters.Count; |
|
} else { |
|
return MemberLookupHelper.IsSimilarMember(resolvedMember, entity as IMember); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region MethodResolveResult |
|
public class MethodGroup : AbstractFreezable, IList<IMethod> |
|
{ |
|
IList<IMethod> innerList; |
|
bool isExtensionMethodGroup; |
|
|
|
public MethodGroup() : this(new List<IMethod>()) |
|
{ |
|
} |
|
|
|
public MethodGroup(IList<IMethod> innerList) |
|
{ |
|
if (innerList == null) |
|
throw new ArgumentNullException("innerList"); |
|
this.innerList = innerList; |
|
} |
|
|
|
public bool IsExtensionMethodGroup { |
|
get { return isExtensionMethodGroup; } |
|
set { |
|
CheckBeforeMutation(); |
|
isExtensionMethodGroup = value; |
|
} |
|
} |
|
|
|
protected override void FreezeInternal() |
|
{ |
|
base.FreezeInternal(); |
|
innerList = FreezeList(innerList); |
|
} |
|
|
|
public IMethod this[int index] { |
|
get { return innerList[index]; } |
|
set { innerList[index] = value; } |
|
} |
|
|
|
public int Count { |
|
get { return innerList.Count; } |
|
} |
|
|
|
public bool IsReadOnly { |
|
get { return innerList.IsReadOnly; } |
|
} |
|
|
|
public int IndexOf(IMethod item) |
|
{ |
|
return innerList.IndexOf(item); |
|
} |
|
|
|
public void Insert(int index, IMethod item) |
|
{ |
|
innerList.Insert(index, item); |
|
} |
|
|
|
public void RemoveAt(int index) |
|
{ |
|
innerList.RemoveAt(index); |
|
} |
|
|
|
public void Add(IMethod item) |
|
{ |
|
innerList.Add(item); |
|
} |
|
|
|
public void Clear() |
|
{ |
|
innerList.Clear(); |
|
} |
|
|
|
public bool Contains(IMethod item) |
|
{ |
|
return innerList.Contains(item); |
|
} |
|
|
|
public void CopyTo(IMethod[] array, int arrayIndex) |
|
{ |
|
innerList.CopyTo(array, arrayIndex); |
|
} |
|
|
|
public bool Remove(IMethod item) |
|
{ |
|
return innerList.Remove(item); |
|
} |
|
|
|
public IEnumerator<IMethod> GetEnumerator() |
|
{ |
|
return innerList.GetEnumerator(); |
|
} |
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
|
{ |
|
return this.GetEnumerator(); |
|
} |
|
} |
|
|
|
/// <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 MethodGroupResolveResult : ResolveResult |
|
{ |
|
string name; |
|
IReturnType containingType; |
|
IList<MethodGroup> possibleMethods; |
|
|
|
public bool IsVBNetAddressOf { get; set; } |
|
|
|
public MethodGroupResolveResult(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; |
|
this.ResolvedType = new MethodGroupReturnType(); |
|
} |
|
|
|
public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name, |
|
IList<MethodGroup> possibleMethods) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
if (containingType == null) |
|
throw new ArgumentNullException("containingType"); |
|
if (name == null) |
|
throw new ArgumentNullException("name"); |
|
if (possibleMethods == null) |
|
throw new ArgumentNullException("possibleMethods"); |
|
this.containingType = containingType; |
|
this.name = name; |
|
this.possibleMethods = possibleMethods; |
|
this.ResolvedType = new MethodGroupReturnType(); |
|
} |
|
|
|
public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name, |
|
IList<MethodGroup> possibleMethods, bool isVBNet, bool isAddressOf) |
|
: this(callingClass, callingMember, containingType, name, possibleMethods) |
|
{ |
|
IMethod parameterlessMethod = possibleMethods.SelectMany(list => list).FirstOrDefault(m => !m.Parameters.Any());; |
|
if (isVBNet && !isAddressOf && parameterlessMethod != null) |
|
this.ResolvedType = parameterlessMethod.ReturnType; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new MethodGroupResolveResult(this.CallingClass, this.CallingMember, this.ContainingType, this.Name, this.Methods); |
|
} |
|
|
|
protected override void FreezeInternal() |
|
{ |
|
base.FreezeInternal(); |
|
if (possibleMethods != null) { |
|
possibleMethods = FreezeList(possibleMethods); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the name of the method. |
|
/// </summary> |
|
public string Name { |
|
get { return name; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the class that contains the method. |
|
/// This property cannot be null. |
|
/// </summary> |
|
public IReturnType ContainingType { |
|
get { return containingType; } |
|
} |
|
|
|
/// <summary> |
|
/// The list of possible methods. |
|
/// </summary> |
|
public IList<MethodGroup> Methods { |
|
get { |
|
if (possibleMethods == null) { |
|
possibleMethods = FreezeList( |
|
new MethodGroup[] { |
|
new MethodGroup( |
|
containingType.GetMethods().FindAll((IMethod m) => m.Name == this.name) |
|
) |
|
}); |
|
} |
|
return possibleMethods; |
|
} |
|
} |
|
|
|
public IMethod GetMethodIfSingleOverload() |
|
{ |
|
if (this.Methods.Count > 0 && this.Methods[0].Count == 1) |
|
return this.Methods[0][0]; |
|
else |
|
return null; |
|
} |
|
|
|
public IMethod GetMethodWithEmptyParameterList() |
|
{ |
|
if (this.Methods.Count > 0 && !IsVBNetAddressOf) { |
|
return this.Methods |
|
.SelectMany(group => group.Select(item => item)) |
|
.FirstOrDefault(i => i.Parameters.Count == 0); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
IMethod m = GetMethodIfSingleOverload(); |
|
IMethod m2 = GetMethodWithEmptyParameterList(); |
|
if (m != null) |
|
return MemberResolveResult.GetDefinitionPosition(m); |
|
else if (m2 != null) |
|
return MemberResolveResult.GetDefinitionPosition(m2); |
|
else |
|
return base.GetDefinitionPosition(); |
|
} |
|
|
|
public override bool IsReferenceTo(IEntity entity) |
|
{ |
|
return MemberLookupHelper.IsSimilarMember(GetMethodIfSingleOverload(), entity as IMember); |
|
} |
|
} |
|
#endregion |
|
|
|
#region VBBaseOrThisReferenceInConstructorResolveResult |
|
/// <summary> |
|
/// Is used for "MyBase" or "Me" in VB constructors to show "New" in the completion list. |
|
/// </summary> |
|
public class VBBaseOrThisReferenceInConstructorResolveResult : ResolveResult |
|
{ |
|
public VBBaseOrThisReferenceInConstructorResolveResult(IClass callingClass, IMember callingMember, IReturnType referencedType) |
|
: base(callingClass, callingMember, referencedType) |
|
{ |
|
} |
|
|
|
public override List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
List<ICompletionEntry> res = base.GetCompletionData(projectContent); |
|
foreach (IMethod m in this.ResolvedType.GetMethods()) { |
|
if (m.IsConstructor && !m.IsStatic && m.IsAccessible(this.CallingClass, true)) |
|
res.Add(m); |
|
} |
|
return res; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new VBBaseOrThisReferenceInConstructorResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); |
|
} |
|
} |
|
#endregion |
|
|
|
#region BaseResolveResult |
|
/// <summary> |
|
/// Is used for "base"/"MyBase" expression. |
|
/// The completion list always shows protected members. |
|
/// </summary> |
|
public class BaseResolveResult : ResolveResult |
|
{ |
|
public BaseResolveResult(IClass callingClass, IMember callingMember, IReturnType baseClassType) |
|
: base(callingClass, callingMember, baseClassType) |
|
{ |
|
} |
|
|
|
public override List<ICompletionEntry> GetCompletionData(IProjectContent projectContent) |
|
{ |
|
if (this.ResolvedType == null) return null; |
|
List<ICompletionEntry> res = new List<ICompletionEntry>(); |
|
|
|
foreach (IMember m in MemberLookupHelper.GetAccessibleMembers(this.ResolvedType, this.CallingClass, projectContent.Language, true)) { |
|
if (projectContent.Language.ShowMember(m, false)) |
|
res.Add(m); |
|
} |
|
|
|
if (this.CallingClass != null) { |
|
AddExtensions(projectContent.Language, res.Add, this.CallingClass, this.ResolvedType); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new BaseResolveResult(this.CallingClass, this.CallingMember, this.ResolvedType); |
|
} |
|
} |
|
#endregion |
|
|
|
#region DelegateCallResolveResult |
|
/// <summary> |
|
/// Is used for calls to delegates/events. |
|
/// </summary> |
|
public class DelegateCallResolveResult : ResolveResult |
|
{ |
|
IMethod delegateInvokeMethod; |
|
ResolveResult targetRR; |
|
|
|
protected override void FreezeInternal() |
|
{ |
|
base.FreezeInternal(); |
|
delegateInvokeMethod.Freeze(); |
|
targetRR.Freeze(); |
|
} |
|
|
|
public DelegateCallResolveResult(ResolveResult targetRR, IMethod delegateInvokeMethod) |
|
: base(targetRR.CallingClass, targetRR.CallingMember, delegateInvokeMethod.ReturnType) |
|
{ |
|
this.targetRR = targetRR; |
|
this.delegateInvokeMethod = delegateInvokeMethod; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the Invoke() method of the delegate. |
|
/// </summary> |
|
public IMethod DelegateInvokeMethod { |
|
get { return delegateInvokeMethod; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the type of the delegate. |
|
/// </summary> |
|
public IReturnType DelegateType { |
|
get { return targetRR.ResolvedType; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the resolve result referring to the delegate. |
|
/// </summary> |
|
public ResolveResult Target { |
|
get { return targetRR; } |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new DelegateCallResolveResult(targetRR, delegateInvokeMethod); |
|
} |
|
|
|
public override FilePosition GetDefinitionPosition() |
|
{ |
|
return targetRR.GetDefinitionPosition(); |
|
} |
|
|
|
public override bool IsReferenceTo(ICSharpCode.SharpDevelop.Dom.IEntity entity) |
|
{ |
|
return targetRR.IsReferenceTo(entity); |
|
} |
|
} |
|
#endregion |
|
|
|
#region UnknownIdentifierResolveResult |
|
/// <summary> |
|
/// Used for unknown identifiers. |
|
/// </summary> |
|
public class UnknownIdentifierResolveResult : ResolveResult |
|
{ |
|
string identifier; |
|
|
|
public UnknownIdentifierResolveResult(IClass callingClass, IMember callingMember, string identifier) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
this.identifier = identifier; |
|
} |
|
|
|
public string Identifier { |
|
get { return identifier; } |
|
} |
|
|
|
public override bool IsValid { |
|
get { return false; } |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new UnknownIdentifierResolveResult(this.CallingClass, this.CallingMember, this.Identifier); |
|
} |
|
} |
|
#endregion |
|
|
|
#region UnknownMethodResolveResult |
|
/// <summary> |
|
/// Used for calls to unknown methods. |
|
/// </summary> |
|
public class UnknownMethodResolveResult : ResolveResult |
|
{ |
|
string callName; |
|
bool isStaticContext; |
|
List<IReturnType> arguments; |
|
IReturnType target; |
|
|
|
public UnknownMethodResolveResult(IClass callingClass, IMember callingMember, IReturnType target, string callName, bool isStaticContext, List<IReturnType> arguments) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
this.target = target == null ? (callingClass == null ? null : callingClass.DefaultReturnType) : target; |
|
this.callName = callName; |
|
this.arguments = arguments; |
|
this.isStaticContext = isStaticContext; |
|
} |
|
|
|
public bool IsStaticContext { |
|
get { return isStaticContext; } |
|
} |
|
|
|
public string CallName { |
|
get { return callName; } |
|
} |
|
|
|
public IReturnType Target { |
|
get { return target; } |
|
} |
|
|
|
public List<IReturnType> Arguments { |
|
get { return arguments; } |
|
} |
|
|
|
public override bool IsValid { |
|
get { return false; } |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new UnknownMethodResolveResult(this.CallingClass, this.CallingMember, this.target, this.callName, this.isStaticContext, this.arguments); |
|
} |
|
} |
|
#endregion |
|
|
|
#region UnknownConstructorCallResolveResult |
|
/// <summary> |
|
/// Used for constructor calls on unknown types. |
|
/// </summary> |
|
public class UnknownConstructorCallResolveResult : ResolveResult |
|
{ |
|
string typeName; |
|
|
|
public UnknownConstructorCallResolveResult(IClass callingClass, IMember callingMember, string typeName) |
|
: base(callingClass, callingMember, null) |
|
{ |
|
this.typeName = typeName; |
|
} |
|
|
|
public string TypeName { |
|
get { return typeName; } |
|
} |
|
|
|
public override bool IsValid { |
|
get { return false; } |
|
} |
|
|
|
public override ResolveResult Clone() |
|
{ |
|
return new UnknownConstructorCallResolveResult(this.CallingClass, this.CallingMember, this.TypeName); |
|
} |
|
} |
|
#endregion |
|
}
|
|
|