// // // // // $Revision$ // using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using ICSharpCode.Core; namespace ICSharpCode.SharpDevelop.Dom { #region ResolveResult /// /// 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. /// 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; } /// /// Gets the class that contained the expression used to get this ResolveResult. /// Can be null when the class is unknown. /// public IClass CallingClass { get { return callingClass; } } /// /// Gets the member (method or property in ) 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. /// public IMember CallingMember { get { return callingMember; } } /// /// 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). /// 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; } /// /// Adds extension methods to . /// 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 /// /// The MixedResolveResult is used when an expression can have multiple meanings, for example /// "Size" in a class deriving from "Control". /// public class MixedResolveResult : ResolveResult { ResolveResult primaryResult, secondaryResult; public ResolveResult PrimaryResult { get { return primaryResult; } } public IEnumerable 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 /// /// The LocalResolveResult is used when an expression was a simple local variable /// or parameter. /// /// /// For fields in the current class, a MemberResolveResult is used, so "e" is not always /// a LocalResolveResult. /// 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"); } } /// /// Gets the field representing the local variable. /// public IField Field { get { return field; } } /// /// Gets if the variable is a parameter (true) or a local variable (false). /// 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 /// /// The NamespaceResolveResult is used when an expression was the name of a namespace. /// is always null on a NamespaceReturnType. /// /// /// Example expressions: /// "System" /// "System.Windows.Forms" /// "using Win = System.Windows; Win.Forms" /// 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; } /// /// Gets the name of the namespace. /// public string Name { get { return name; } } public override ArrayList GetCompletionData(IProjectContent projectContent) { return projectContent.GetNamespaceContents(name); } } #endregion #region IntegerLiteralResolveResult /// /// 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. /// 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 /// /// 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. /// /// /// Example expressions: /// "System.EventArgs" /// "using System; EventArgs" /// 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(); } /// /// Gets the class corresponding to the resolved type. /// This property can be null when the type has no class (for example a type parameter). /// 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 /// /// The TypeResolveResult is used when an expression was a member /// (field, property, event or method call). /// /// /// 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)" /// 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; } /// /// Gets the member that was resolved. /// 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 /// /// 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. /// is always null on a MethodReturnType. /// /// /// Example expressions: /// "System.Console.WriteLine" /// "a.Add" (where a is List<string>) /// "SomeMethod" (when SomeMethod is a method in the current class) /// 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; } /// /// Gets the name of the method. /// public string Name { get { return name; } } /// /// Gets the class that contains the method. /// public IReturnType ContainingType { get { return containingType; } } public IMethod GetMethodIfSingleOverload() { List 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 }