From e2d20525d9f39982f54270e063ef13165b4da61c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 22 Oct 2005 19:04:29 +0000 Subject: [PATCH] Improved BooBinding: Allow for code completion on ArrayReturnType and ConstructedReturnType even if they are encapsulated by an InferredReturnType. Fixed code completion on arrays created through array literals or the "array" builtin. Recognize variables created by "for" statements if their type is inferred from the element type of the enumerable object. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@610 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Boo/BooBinding/Project/BooBinding.csproj | 1 + .../Boo/BooBinding/Project/Src/BooAmbience.cs | 17 ++--- .../Project/Src/CodeCompletion/BooResolver.cs | 16 ++-- .../Src/CodeCompletion/ConvertVisitor.cs | 2 +- .../Src/CodeCompletion/ElementReturnType.cs | 64 ++++++++++++++++ .../Src/CodeCompletion/ResolveVisitor.cs | 66 ++++++++++++---- .../CodeCompletion/VariableLookupVisitor.cs | 76 +++++++++++++++---- .../Project/Src/CSharpAmbience.cs | 16 ++-- .../VBNetBinding/Project/Src/VBNetAmbience.cs | 16 ++-- .../NAntAddIn/Test/NAntAddIn.Tests.csproj | 2 +- .../ICSharpCode.TextEditor.Test.csproj.user | 14 +--- src/Main/Base/Project/Src/Dom/IReturnType.cs | 24 ++++++ .../Dom/Implementations/AbstractReturnType.cs | 18 +++++ .../Dom/Implementations/ArrayReturnType.cs | 17 ++++- .../Dom/Implementations/CombinedReturnType.cs | 32 ++++---- .../Implementations/ConstructedReturnType.cs | 42 +++++----- .../Dom/Implementations/ProxyReturnType.cs | 21 +++++ .../Project/Src/Dom/MemberLookupHelper.cs | 76 ++++++++++++------- .../Src/Dom/ReflectionLayer/DomPersistence.cs | 27 +++---- .../RefactoringService/CodeGenerator.cs | 12 ++- .../ICSharpCode.SharpDevelop.Tests.csproj | 1 + src/Main/Base/Test/MemberLookupHelperTests.cs | 44 +++++++++++ 22 files changed, 434 insertions(+), 170 deletions(-) create mode 100644 src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ElementReturnType.cs create mode 100644 src/Main/Base/Test/MemberLookupHelperTests.cs diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/BooBinding.csproj b/src/AddIns/BackendBindings/Boo/BooBinding/Project/BooBinding.csproj index 83267217fd..885ed9e79f 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/BooBinding.csproj +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/BooBinding.csproj @@ -83,6 +83,7 @@ + diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooAmbience.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooAmbience.cs index 232e71967f..b664424733 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooAmbience.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooAmbience.cs @@ -486,21 +486,20 @@ namespace Grunwald.BooBinding void UnpackNestedType(StringBuilder builder, IReturnType returnType) { - ArrayReturnType art = returnType as ArrayReturnType; - if (art != null) { + if (returnType.ArrayDimensions > 0) { builder.Append('('); - UnpackNestedType(builder, art.ElementType); - for (int i = 1; i < art.ArrayDimensions; ++i) { + UnpackNestedType(builder, returnType.ArrayElementType); + if (returnType.ArrayDimensions > 1) { builder.Append(','); + builder.Append(returnType.ArrayDimensions); } builder.Append(')'); - } else if (returnType is ConstructedReturnType) { - ConstructedReturnType rt = (ConstructedReturnType)returnType; - UnpackNestedType(builder, rt.BaseType); + } else if (returnType.TypeArguments != null) { + UnpackNestedType(builder, returnType.UnboundType); builder.Append("[of "); - for (int i = 0; i < rt.TypeArguments.Count; ++i) { + for (int i = 0; i < returnType.TypeArguments.Count; ++i) { if (i > 0) builder.Append(", "); - builder.Append(Convert(rt.TypeArguments[i])); + builder.Append(Convert(returnType.TypeArguments[i])); } builder.Append(']'); } else { diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs index 9cdb18fa30..c9cf330139 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs @@ -206,6 +206,10 @@ namespace Grunwald.BooBinding.CodeCompletion if (c != null) return new TypeResolveResult(callingClass, callingMember, c); } + string namespaceName = pc.SearchNamespace(name, callingClass, cu, caretLine, caretColumn); + if (namespaceName != null) { + return new NamespaceResolveResult(callingClass, callingMember, namespaceName); + } return null; } else { if (expr.NodeType == AST.NodeType.ReferenceExpression) { @@ -285,21 +289,17 @@ namespace Grunwald.BooBinding.CodeCompletion NRResolver.AddContentsFromCalling(result, callingClass, callingMember); - ArrayList knownVariableNames = new ArrayList(); + List knownVariableNames = new List(); foreach (object o in result) { IMember m = o as IMember; if (m != null) { knownVariableNames.Add(m.Name); } } - VariableListLookupVisitor vllv = new VariableListLookupVisitor(knownVariableNames); + VariableListLookupVisitor vllv = new VariableListLookupVisitor(knownVariableNames, this); vllv.Visit(GetCurrentBooMethod()); - foreach (DictionaryEntry entry in vllv.Results) { - IReturnType type; - if (entry.Value is AST.TypeReference) { - type = ConvertType((AST.TypeReference)entry.Value); - result.Add(new DefaultField.LocalVariableField(type, (string)entry.Key, DomRegion.Empty, callingClass)); - } + foreach (KeyValuePair entry in vllv.Results) { + result.Add(new DefaultField.LocalVariableField(entry.Value, entry.Key, DomRegion.Empty, callingClass)); } return result; diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs index 5436a996b7..b6ac136eff 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs @@ -203,7 +203,7 @@ namespace Grunwald.BooBinding.CodeCompletion AST.ArrayTypeReference arr = (AST.ArrayTypeReference)reference; return new ArrayReturnType(CreateReturnType(arr.ElementType, callingClass, callingMember, caretLine, caretColumn, projectContent, useLazyReturnType), - (int)arr.Rank.Value); + (arr.Rank != null) ? (int)arr.Rank.Value : 1); } else if (reference is AST.SimpleTypeReference) { string name = ((AST.SimpleTypeReference)reference).Name; if (BooAmbience.ReverseTypeConversionTable.ContainsKey(name)) diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ElementReturnType.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ElementReturnType.cs new file mode 100644 index 0000000000..5bb523774b --- /dev/null +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ElementReturnType.cs @@ -0,0 +1,64 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 22.10.2005 + * Time: 19:19 + */ + +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Dom; + +namespace Grunwald.BooBinding.CodeCompletion +{ + /// + /// The return type that is the element of an enumerable. + /// Used to infer the type in "for x in enumerableVariable" loops. + /// + public class ElementReturnType : ProxyReturnType + { + IReturnType listType; + + public ElementReturnType(IReturnType listType) + { + // listType is probably an InferredReturnType + this.listType = listType; + } + + public override IReturnType BaseType { + get { + // get element type from listType + if (listType.ArrayDimensions > 0) + return listType.ArrayElementType; + + IClass c = listType.GetUnderlyingClass(); + if (c == null) + return null; + IClass genumerable = ProjectContentRegistry.Mscorlib.GetClass("System.Collections.Generic.IEnumerable", 1); + if (c.IsTypeInInheritanceTree(genumerable)) { + return MemberLookupHelper.GetTypeParameterPassedToBaseClass(listType, genumerable, 0); + } + IClass enumerable = ProjectContentRegistry.Mscorlib.GetClass("System.Collections.IEnumerable", 0); + if (c.IsTypeInInheritanceTree(enumerable)) { + // We can't use the EnumeratorItemType attribute because SharpDevelop + // does not store attribute argument values in the cache. + + // HACK: Hacked in support for range(), take out when RangeEnumerator implements IEnumerable + if (c.FullyQualifiedName == "Boo.Lang.Builtins.RangeEnumerator") { + return ReflectionReturnType.Int; + } + + return ReflectionReturnType.Object; + } + return null; + } + } + + public override bool IsDefaultReturnType { + get { + IReturnType baseType = BaseType; + return (baseType != null) ? baseType.IsDefaultReturnType : false; + } + } + } +} diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs index 9454ec7925..f4802feabb 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs @@ -293,24 +293,17 @@ namespace Grunwald.BooBinding.CodeCompletion string methodName = ((MethodResolveResult)resolveResult).Name; IReturnType containingType = ((MethodResolveResult)resolveResult).ContainingType; - List methods = new List(); - bool isClassInInheritanceTree = false; - if (callingClass != null) - isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(containingType.GetUnderlyingClass()); - - foreach (IMethod m in containingType.GetMethods()) { - if (IsSameName(m.Name, methodName) - && m.IsAccessible(callingClass, isClassInInheritanceTree) - ) { - methods.Add(m); - } - } - ResolveInvocation(methods, node.Arguments); + ResolveMethodInType(containingType, methodName, node.Arguments); } else if (resolveResult is TypeResolveResult || resolveResult is MixedResolveResult) { TypeResolveResult trr = resolveResult as TypeResolveResult; if (trr == null) trr = (resolveResult as MixedResolveResult).TypeResult; if (trr != null && trr.ResolvedClass != null) { + if (trr.ResolvedClass.FullyQualifiedName == "array") { + ResolveArrayCreation(node.Arguments); + return; + } + List methods = new List(); bool isClassInInheritanceTree = false; if (callingClass != null) @@ -353,6 +346,38 @@ namespace Grunwald.BooBinding.CodeCompletion } } + void ResolveArrayCreation(ExpressionCollection arguments) + { + if (arguments.Count == 2) { + ClearResult(); + arguments[0].Accept(this); + TypeResolveResult trr = resolveResult as TypeResolveResult; + if (trr != null) { + MakeResult(new ArrayReturnType(trr.ResolvedType, 1)); + } + } else { + ResolveMethodInType(new GetClassReturnType(projectContent, "Boo.Lang.Builtins", 0), + "array", arguments); + } + } + + void ResolveMethodInType(IReturnType containingType, string methodName, ExpressionCollection arguments) + { + List methods = new List(); + bool isClassInInheritanceTree = false; + if (callingClass != null) + isClassInInheritanceTree = callingClass.IsTypeInInheritanceTree(containingType.GetUnderlyingClass()); + + foreach (IMethod m in containingType.GetMethods()) { + if (IsSameName(m.Name, methodName) + && m.IsAccessible(callingClass, isClassInInheritanceTree) + ) { + methods.Add(m); + } + } + ResolveInvocation(methods, arguments); + } + void ResolveInvocation(List methods, ExpressionCollection arguments) { ClearResult(); @@ -471,8 +496,19 @@ namespace Grunwald.BooBinding.CodeCompletion public override void OnArrayLiteralExpression(ArrayLiteralExpression node) { - // TODO: get real array type - MakeLiteralResult("System.Array"); + IReturnType elementType = null; + foreach (Expression expr in node.Items) { + ClearResult(); + node.Items[0].Accept(this); + IReturnType thisType = (resolveResult != null) ? resolveResult.ResolvedType : null; + if (elementType == null) + elementType = thisType; + else if (thisType != null) + elementType = MemberLookupHelper.GetCommonType(elementType, thisType); + } + if (elementType == null) + elementType = ReflectionReturnType.Object; + MakeResult(new ArrayReturnType(elementType, 1)); } public override void OnAsExpression(AsExpression node) diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs index 729b983e5a..3b1f71d9b9 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs @@ -6,7 +6,8 @@ // using System; -using System.Collections; +using System.Collections.Generic; +using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Dom; using Boo.Lang.Compiler.Ast; @@ -36,13 +37,16 @@ namespace Grunwald.BooBinding.CodeCompletion } } - private void InferResult(Expression expr, string name, LexicalInfo lexicalInfo) + private void InferResult(Expression expr, string name, LexicalInfo lexicalInfo, bool useElementType) { if (expr == null) return; if (result != null) return; - result = new DefaultField.LocalVariableField(new InferredReturnType(expr), name, + IReturnType returnType = new InferredReturnType(expr); + if (useElementType) + returnType = new ElementReturnType(returnType); + result = new DefaultField.LocalVariableField(returnType, name, new DomRegion(lexicalInfo.Line, lexicalInfo.Column), resolver.CallingClass); } @@ -65,10 +69,15 @@ namespace Grunwald.BooBinding.CodeCompletion { if (node.Declaration.Name == lookFor) { Visit(node.Declaration); - InferResult(node.Initializer, node.Declaration.Name, node.LexicalInfo); + InferResult(node.Initializer, node.Declaration.Name, node.LexicalInfo, false); } } + protected override void OnError(Node node, Exception error) + { + MessageService.ShowError(error, "VariableLookupVisitor: error processing " + node); + } + public override void OnBinaryExpression(BinaryExpression node) { if (acceptImplicit) { @@ -76,13 +85,29 @@ namespace Grunwald.BooBinding.CodeCompletion if (node.Operator == BinaryOperatorType.Assign && reference != null) { if (!(reference is MemberReferenceExpression)) { if (reference.Name == lookFor) { - InferResult(node.Right, reference.Name, reference.LexicalInfo); + InferResult(node.Right, reference.Name, reference.LexicalInfo, false); } } } } base.OnBinaryExpression(node); } + + public override void OnForStatement(ForStatement node) + { + if (node.LexicalInfo.Line > resolver.CaretLine || node.Block.EndSourceLocation.Line < resolver.CaretLine) + return; + + if (node.Declarations.Count != 1) { + // TODO: support unpacking + base.OnForStatement(node); + return; + } + if (node.Declarations[0].Name == lookFor) { + Visit(node.Declarations[0]); + InferResult(node.Iterator, node.Declarations[0].Name, node.LexicalInfo, true); + } + } } /// @@ -91,28 +116,33 @@ namespace Grunwald.BooBinding.CodeCompletion /// public class VariableListLookupVisitor : DepthFirstVisitor { - ArrayList knownVariableNames; + List knownVariableNames; + BooResolver resolver; - public VariableListLookupVisitor(ArrayList knownVariableNames) + public VariableListLookupVisitor(List knownVariableNames, BooResolver resolver) { this.knownVariableNames = knownVariableNames; + this.resolver = resolver; } - Hashtable results = new Hashtable(); + Dictionary results = new Dictionary(); - public Hashtable Results { + public Dictionary Results { get { return results; } } - private void Add(string name, Expression expr) + private void Add(string name, Expression expr, bool elementReturnType) { if (name == null || expr == null) return; if (results.ContainsKey(name)) return; - results.Add(name, expr); + if (elementReturnType) + results.Add(name, new ElementReturnType(new InferredReturnType(expr))); + else + results.Add(name, new InferredReturnType(expr)); } private void Add(string name, TypeReference reference) @@ -121,7 +151,7 @@ namespace Grunwald.BooBinding.CodeCompletion return; if (results.ContainsKey(name)) return; - results.Add(name, reference); + results.Add(name, resolver.ConvertType(reference)); } public override void OnDeclaration(Declaration node) @@ -132,7 +162,12 @@ namespace Grunwald.BooBinding.CodeCompletion public override void OnDeclarationStatement(DeclarationStatement node) { Visit(node.Declaration); - Add(node.Declaration.Name, node.Initializer); + Add(node.Declaration.Name, node.Initializer, false); + } + + protected override void OnError(Node node, Exception error) + { + MessageService.ShowError(error, "VariableListLookupVisitor: error processing " + node); } public override void OnBinaryExpression(BinaryExpression node) @@ -142,12 +177,25 @@ namespace Grunwald.BooBinding.CodeCompletion if (node.Operator == BinaryOperatorType.Assign && reference != null) { if (!(reference is MemberReferenceExpression)) { if (!knownVariableNames.Contains(reference.Name)) { - Add(reference.Name, node.Right); + Add(reference.Name, node.Right, false); } } } } base.OnBinaryExpression(node); } + + public override void OnForStatement(ForStatement node) + { + if (node.LexicalInfo.Line > resolver.CaretLine || node.Block.EndSourceLocation.Line < resolver.CaretLine) + return; + + if (node.Declarations.Count != 1) { + // TODO: support unpacking + base.OnForStatement(node); + return; + } + Add(node.Declarations[0].Name, node.Iterator, true); + } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs index fc27b1c22d..9983de480b 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs @@ -491,21 +491,19 @@ namespace ICSharpCode.Core void UnpackNestedType(StringBuilder builder, IReturnType returnType) { - ArrayReturnType art = returnType as ArrayReturnType; - if (art != null) { + if (returnType.ArrayDimensions > 0) { builder.Append('['); - for (int i = 1; i < art.ArrayDimensions; ++i) { + for (int i = 1; i < returnType.ArrayDimensions; ++i) { builder.Append(','); } builder.Append(']'); - UnpackNestedType(builder, art.ElementType); - } else if (returnType is ConstructedReturnType) { - ConstructedReturnType rt = (ConstructedReturnType)returnType; - UnpackNestedType(builder, rt.BaseType); + UnpackNestedType(builder, returnType.ArrayElementType); + } else if (returnType.TypeArguments != null) { + UnpackNestedType(builder, returnType.UnboundType); builder.Append('<'); - for (int i = 0; i < rt.TypeArguments.Count; ++i) { + for (int i = 0; i < returnType.TypeArguments.Count; ++i) { if (i > 0) builder.Append(", "); - builder.Append(Convert(rt.TypeArguments[i])); + builder.Append(Convert(returnType.TypeArguments[i])); } builder.Append('>'); } diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs index bfc10a852e..a37502ea09 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs @@ -493,21 +493,19 @@ namespace VBNetBinding void UnpackNestedType(StringBuilder builder, IReturnType returnType) { - ArrayReturnType art = returnType as ArrayReturnType; - if (art != null) { + if (returnType.ArrayDimensions > 0) { builder.Append('('); - for (int i = 1; i < art.ArrayDimensions; ++i) { + for (int i = 1; i < returnType.ArrayDimensions; ++i) { builder.Append(','); } builder.Append(')'); - UnpackNestedType(builder, art.ElementType); - } else if (returnType is ConstructedReturnType) { - ConstructedReturnType rt = (ConstructedReturnType)returnType; - UnpackNestedType(builder, rt.BaseType); + UnpackNestedType(builder, returnType.ArrayElementType); + } else if (returnType.TypeArguments != null) { + UnpackNestedType(builder, returnType.UnboundType); builder.Append("(Of "); - for (int i = 0; i < rt.TypeArguments.Count; ++i) { + for (int i = 0; i < returnType.TypeArguments.Count; ++i) { if (i > 0) builder.Append(", "); - builder.Append(Convert(rt.TypeArguments[i])); + builder.Append(Convert(returnType.TypeArguments[i])); } builder.Append(')'); } diff --git a/src/AddIns/Misc/NAntAddIn/Test/NAntAddIn.Tests.csproj b/src/AddIns/Misc/NAntAddIn/Test/NAntAddIn.Tests.csproj index ddcaeb7c84..97bbf8e629 100644 --- a/src/AddIns/Misc/NAntAddIn/Test/NAntAddIn.Tests.csproj +++ b/src/AddIns/Misc/NAntAddIn/Test/NAntAddIn.Tests.csproj @@ -39,7 +39,7 @@ - + ..\..\..\..\..\bin\ICSharpCode.SharpDevelop.dll False diff --git a/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj.user b/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj.user index 591cb72a66..e1d33e369a 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj.user +++ b/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj.user @@ -1,16 +1,4 @@ - - - - - - - - - - - - - + 8.0.41115 ProjectFiles diff --git a/src/Main/Base/Project/Src/Dom/IReturnType.cs b/src/Main/Base/Project/Src/Dom/IReturnType.cs index 9bab1ecaf6..0e3f11ad6b 100644 --- a/src/Main/Base/Project/Src/Dom/IReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/IReturnType.cs @@ -70,6 +70,14 @@ namespace ICSharpCode.SharpDevelop.Dom get; } + /// + /// Gets the element type of the array. + /// When the return type is not an array, a NotSupportedException is thrown. + /// + IReturnType ArrayElementType { + get; + } + /// /// Gets the count of type parameters the target class should have. /// @@ -88,6 +96,22 @@ namespace ICSharpCode.SharpDevelop.Dom get; } + /// + /// Gets the type arguments used if this is a ConstructedReturnType. + /// Otherwise, null is returned. + /// + IList TypeArguments { + get; + } + + /// + /// Gets the unbound type if this is a ConstructedReturnType. + /// Otherwise, a NotSupportedException is thrown. + /// + IReturnType UnboundType { + get; + } + /// /// Gets the underlying class of this return type. /// diff --git a/src/Main/Base/Project/Src/Dom/Implementations/AbstractReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/AbstractReturnType.cs index c37c614b78..d5ee2edf4e 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/AbstractReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/AbstractReturnType.cs @@ -91,5 +91,23 @@ namespace ICSharpCode.SharpDevelop.Dom return true; } } + + public IReturnType ArrayElementType { + get { + throw new NotSupportedException(); + } + } + + public IReturnType UnboundType { + get { + throw new NotSupportedException(); + } + } + + public IList TypeArguments { + get { + return null; + } + } } } diff --git a/src/Main/Base/Project/Src/Dom/Implementations/ArrayReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/ArrayReturnType.cs index 3a8312406b..3a3eb05b23 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/ArrayReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/ArrayReturnType.cs @@ -33,7 +33,22 @@ namespace ICSharpCode.SharpDevelop.Dom this.dimensions = dimensions; } - public IReturnType ElementType { + public override bool Equals(object o) + { + IReturnType rt = o as IReturnType; + if (rt == null) return false; + if (rt.ArrayDimensions != dimensions) return false; + return elementType.Equals(rt.ArrayElementType); + } + + public override int GetHashCode() + { + unchecked { + return 2 * elementType.GetHashCode() + 27 * dimensions; + } + } + + public override IReturnType ArrayElementType { get { return elementType; } diff --git a/src/Main/Base/Project/Src/Dom/Implementations/CombinedReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/CombinedReturnType.cs index 7c3f2ac259..3a3e84ecd7 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/CombinedReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/CombinedReturnType.cs @@ -12,9 +12,9 @@ using ICSharpCode.Core; namespace ICSharpCode.SharpDevelop.Dom { /// - /// Combines multiple return types. + /// Combines multiple return types for use in contraints. /// - public sealed class CombinedReturnType : IReturnType + public sealed class CombinedReturnType : AbstractReturnType { IList baseTypes; @@ -93,69 +93,63 @@ namespace ICSharpCode.SharpDevelop.Dom return list; } - public List GetMethods() + public override List GetMethods() { return Combine(delegate(IReturnType type) { return type.GetMethods(); }); } - public List GetProperties() + public override List GetProperties() { return Combine(delegate(IReturnType type) { return type.GetProperties(); }); } - public List GetFields() + public override List GetFields() { return Combine(delegate(IReturnType type) { return type.GetFields(); }); } - public List GetEvents() + public override List GetEvents() { return Combine(delegate(IReturnType type) { return type.GetEvents(); }); } - public string FullyQualifiedName { + public override string FullyQualifiedName { get { return fullName; } } - public string Name { + public override string Name { get { return name; } } - public string Namespace { + public override string Namespace { get { return @namespace; } } - public string DotNetName { + public override string DotNetName { get { return dotnetName; } } - public int ArrayDimensions { - get { - return 0; - } - } - - public bool IsDefaultReturnType { + public override bool IsDefaultReturnType { get { return false; } } - public int TypeParameterCount { + public override int TypeParameterCount { get { return 0; } } - public IClass GetUnderlyingClass() + public override IClass GetUnderlyingClass() { return null; } diff --git a/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs index 5450ca33f6..87b1a869ca 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.SharpDevelop.Dom IList typeParameters; IReturnType baseType; - public IList TypeArguments { + public override IList TypeArguments { get { return typeParameters; } @@ -45,14 +45,9 @@ namespace ICSharpCode.SharpDevelop.Dom public override bool Equals(object o) { - ConstructedReturnType rt = o as ConstructedReturnType; + IReturnType rt = o as IReturnType; if (rt == null) return false; - if (!baseType.Equals(rt.baseType)) return false; - if (typeParameters.Count != rt.typeParameters.Count) return false; - for (int i = 0; i < typeParameters.Count; ++i) { - if (!object.Equals(typeParameters[i], rt.typeParameters[i])) return false; - } - return true; + return this.DotNetName == rt.DotNetName; } public override int GetHashCode() @@ -70,15 +65,21 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public override IReturnType UnboundType { + get { + return baseType; + } + } + bool CheckReturnType(IReturnType t) { if (t is GenericReturnType) { GenericReturnType rt = (GenericReturnType)t; return rt.TypeParameter.Method == null; - } else if (t is ArrayReturnType) { - return CheckReturnType(((ArrayReturnType)t).ElementType); - } else if (t is ConstructedReturnType) { - foreach (IReturnType para in ((ConstructedReturnType)t).TypeArguments) { + } else if (t.ArrayDimensions > 0) { + return CheckReturnType(t.ArrayElementType); + } else if (t.TypeArguments != null) { + foreach (IReturnType para in t.TypeArguments) { if (CheckReturnType(para)) return true; } return false; @@ -129,18 +130,17 @@ namespace ICSharpCode.SharpDevelop.Dom } } } - } else if (input is ArrayReturnType) { - IReturnType e = ((ArrayReturnType)input).ElementType; + } else if (input.ArrayDimensions > 0) { + IReturnType e = input.ArrayElementType; IReturnType t = TranslateType(e, typeParameters, convertForMethod); if (e != t && t != null) return new ArrayReturnType(t, input.ArrayDimensions); - } else if (input is ConstructedReturnType) { - ConstructedReturnType r = (ConstructedReturnType)input; - List para = new List(r.TypeArguments.Count); - for (int i = 0; i < r.TypeArguments.Count; ++i) { - para.Add(TranslateType(r.TypeArguments[i], typeParameters, convertForMethod)); + } else if (input.TypeArguments != null) { + List para = new List(input.TypeArguments.Count); + foreach (IReturnType argument in input.TypeArguments) { + para.Add(TranslateType(argument, typeParameters, convertForMethod)); } - return new ConstructedReturnType(r.baseType, para); + return new ConstructedReturnType(input.UnboundType, para); } return input; } @@ -212,7 +212,7 @@ namespace ICSharpCode.SharpDevelop.Dom public override string ToString() { - string r = "[SpecificReturnType: "; + string r = "[ConstructedReturnType: "; r += baseType; r += "<"; for (int i = 0; i < typeParameters.Count; i++) { diff --git a/src/Main/Base/Project/Src/Dom/Implementations/ProxyReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/ProxyReturnType.cs index 4337b35c69..f89a98fab8 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/ProxyReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/ProxyReturnType.cs @@ -66,6 +66,27 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public virtual IReturnType ArrayElementType { + get { + IReturnType baseType = BaseType; + return (baseType != null) ? baseType.ArrayElementType : null; + } + } + + public virtual IReturnType UnboundType { + get { + IReturnType baseType = BaseType; + return (baseType != null) ? baseType.UnboundType : null; + } + } + + public virtual IList TypeArguments { + get { + IReturnType baseType = BaseType; + return (baseType != null) ? baseType.TypeArguments : null; + } + } + /// /// Gets the underlying class of this return type. /// diff --git a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs index 24c8c039f2..8edb890c54 100644 --- a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs +++ b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs @@ -241,8 +241,8 @@ namespace ICSharpCode.SharpDevelop.Dom if (j < parameters.Count) { IParameter parameter = parameters[j]; if (parameter.IsParams && needToExpand[i]) { - if (parameter.ReturnType is ArrayReturnType) { - paramsType = ((ArrayReturnType)parameter.ReturnType).ElementType; + if (parameter.ReturnType.ArrayDimensions > 0) { + paramsType = parameter.ReturnType.ArrayElementType; paramsType = ConstructedReturnType.TranslateType(paramsType, typeParameters, true); } expandedParameters[i][j] = paramsType; @@ -347,14 +347,17 @@ namespace ICSharpCode.SharpDevelop.Dom static int GetMoreSpecific(IReturnType r, IReturnType s) { + if (r == null && s == null) return 0; + if (r == null) return 2; + if (s == null) return 1; if (r is GenericReturnType && !(s is GenericReturnType)) return 2; if (s is GenericReturnType && !(r is GenericReturnType)) return 1; - if (r is ArrayReturnType && s is ArrayReturnType) - return GetMoreSpecific(((ArrayReturnType)r).ElementType, ((ArrayReturnType)s).ElementType); - if (r is ConstructedReturnType && s is ConstructedReturnType) - return GetMoreSpecific(((ConstructedReturnType)r).TypeArguments, ((ConstructedReturnType)s).TypeArguments); + if (r.ArrayDimensions > 0 && s.ArrayDimensions > 0) + return GetMoreSpecific(r.ArrayElementType, s.ArrayElementType); + if (r.TypeArguments != null && s.TypeArguments != null) + return GetMoreSpecific(r.TypeArguments, s.TypeArguments); return 0; } #endregion @@ -372,11 +375,8 @@ namespace ICSharpCode.SharpDevelop.Dom break; if (!InferTypeArgument(parameters[i].ReturnType, arguments[i], result)) { // inferring failed: maybe this is a params parameter that must be expanded? - if (parameters[i].IsParams) { - ArrayReturnType art = parameters[i].ReturnType as ArrayReturnType; - if (art != null) { - InferTypeArgument(art.ElementType, arguments[i], result); - } + if (parameters[i].IsParams && parameters[i].ReturnType.ArrayDimensions == 1) { + InferTypeArgument(parameters[i].ReturnType.ArrayElementType, arguments[i], result); } } } @@ -392,16 +392,15 @@ namespace ICSharpCode.SharpDevelop.Dom static bool InferTypeArgument(IReturnType expectedArgument, IReturnType passedArgument, IReturnType[] outputArray) { if (passedArgument == null) return true; // TODO: NullTypeReference - ArrayReturnType ea = expectedArgument as ArrayReturnType; - if (ea != null) { - if (passedArgument is ArrayReturnType && ea.ArrayDimensions == passedArgument.ArrayDimensions) { - return InferTypeArgument(ea.ElementType, ((ArrayReturnType)passedArgument).ElementType, outputArray); - } else if (passedArgument is ConstructedReturnType) { + if (expectedArgument != null && expectedArgument.ArrayDimensions > 0) { + if (expectedArgument.ArrayDimensions == passedArgument.ArrayDimensions) { + return InferTypeArgument(expectedArgument.ArrayElementType, passedArgument.ArrayElementType, outputArray); + } else if (passedArgument.TypeArguments != null) { switch (passedArgument.FullyQualifiedName) { case "System.Collections.Generic.IList": case "System.Collections.Generic.ICollection": case "System.Collections.Generic.IEnumerable": - return InferTypeArgument(ea.ElementType, ((ConstructedReturnType)passedArgument).TypeArguments[0], outputArray); + return InferTypeArgument(expectedArgument.ArrayElementType, passedArgument.TypeArguments[0], outputArray); } } // If P is an array type, and A is not an array type of the same rank, @@ -416,15 +415,13 @@ namespace ICSharpCode.SharpDevelop.Dom } return true; } - ConstructedReturnType constructed = expectedArgument as ConstructedReturnType; - if (constructed != null) { + if (expectedArgument.TypeArguments != null) { // The spec for this case is quite complex. // For our purposes, we can simplify enourmously: - ConstructedReturnType passed = passedArgument as ConstructedReturnType; - if (passed == null) return false; - int count = Math.Min(constructed.TypeArguments.Count, passed.TypeArguments.Count); + if (passedArgument.TypeArguments == null) return false; + int count = Math.Min(expectedArgument.TypeArguments.Count, passedArgument.TypeArguments.Count); for (int i = 0; i < count; i++) { - InferTypeArgument(constructed.TypeArguments[i], passed.TypeArguments[i], outputArray); + InferTypeArgument(expectedArgument.TypeArguments[i], passedArgument.TypeArguments[i], outputArray); } } return true; @@ -476,12 +473,12 @@ namespace ICSharpCode.SharpDevelop.Dom score++; // - all additional parameters must be applicable to the unpacked array - ArrayReturnType rt = parameters[lastParameter].ReturnType as ArrayReturnType; - if (rt == null) { + IReturnType rt = parameters[lastParameter].ReturnType; + if (rt == null || rt.ArrayDimensions == 0) { return false; } for (int i = lastParameter; i < arguments.Length; i++) { - if (IsApplicable(arguments[i], rt.ElementType)) { + if (IsApplicable(arguments[i], rt.ArrayElementType)) { score++; } else { ok = false; @@ -558,9 +555,9 @@ namespace ICSharpCode.SharpDevelop.Dom return true; } } - if (from is ArrayReturnType && to is ArrayReturnType && from.ArrayDimensions == to.ArrayDimensions) { + if (from.ArrayDimensions > 0 && from.ArrayDimensions == to.ArrayDimensions) { // from array to other array type - return ConversionExists((from as ArrayReturnType).ElementType, (to as ArrayReturnType).ElementType); + return ConversionExists(from.ArrayElementType, to.ArrayElementType); } return false; } @@ -685,5 +682,28 @@ namespace ICSharpCode.SharpDevelop.Dom return a; return ReflectionReturnType.Object; } + + /// + /// Gets the type parameter that was passed to a certain base class. + /// For example, when is Dictionary(of string, int) + /// this method will return KeyValuePair(of string, int) + /// + public static IReturnType GetTypeParameterPassedToBaseClass(IReturnType returnType, IClass baseClass, int baseClassTypeParameterIndex) + { + IClass c = returnType.GetUnderlyingClass(); + if (c == null) return null; + foreach (IReturnType baseType in c.BaseTypes) { + if (baseClass.CompareTo(baseType.GetUnderlyingClass()) == 0) { + if (baseType.TypeArguments == null || baseClassTypeParameterIndex >= baseType.TypeArguments.Count) + return null; + IReturnType result = baseType.TypeArguments[baseClassTypeParameterIndex]; + if (returnType.TypeArguments != null) { + result = ConstructedReturnType.TranslateType(result, returnType.TypeArguments, false); + } + return result; + } + } + return null; + } } } diff --git a/src/Main/Base/Project/Src/Dom/ReflectionLayer/DomPersistence.cs b/src/Main/Base/Project/Src/Dom/ReflectionLayer/DomPersistence.cs index 9700ee0b77..41ce01c5af 100644 --- a/src/Main/Base/Project/Src/Dom/ReflectionLayer/DomPersistence.cs +++ b/src/Main/Base/Project/Src/Dom/ReflectionLayer/DomPersistence.cs @@ -526,12 +526,11 @@ namespace ICSharpCode.SharpDevelop.Dom classIndices.Add(pair, externalTypes.Count + classCount); externalTypes.Add(pair); } - } else if (rt is ArrayReturnType) { - AddExternalType(((ArrayReturnType)rt).ElementType, externalTypes, classCount); - } else if (rt is ConstructedReturnType) { - ConstructedReturnType crt = (ConstructedReturnType)rt; - AddExternalType(crt.BaseType, externalTypes, classCount); - foreach (IReturnType typeArgument in crt.TypeArguments) { + } else if (rt.ArrayDimensions > 0) { + AddExternalType(rt.ArrayElementType, externalTypes, classCount); + } else if (rt.TypeArguments != null) { + AddExternalType(rt.UnboundType, externalTypes, classCount); + foreach (IReturnType typeArgument in rt.TypeArguments) { AddExternalType(typeArgument, externalTypes, classCount); } } else if (rt is GenericReturnType) { @@ -561,17 +560,15 @@ namespace ICSharpCode.SharpDevelop.Dom } else { writer.Write(classIndices[new ClassNameTypeCountPair(rt)]); } - } else if (rt is ArrayReturnType) { - ArrayReturnType art = (ArrayReturnType)rt; + } else if (rt.ArrayDimensions > 0) { writer.Write(ArrayRTCode); - writer.Write(art.ArrayDimensions); - WriteType(art.ElementType); - } else if (rt is ConstructedReturnType) { - ConstructedReturnType crt = (ConstructedReturnType)rt; + writer.Write(rt.ArrayDimensions); + WriteType(rt.ArrayElementType); + } else if (rt.TypeArguments != null) { writer.Write(ConstructedRTCode); - WriteType(crt.BaseType); - writer.Write((byte)crt.TypeArguments.Count); - foreach (IReturnType typeArgument in crt.TypeArguments) { + WriteType(rt.UnboundType); + writer.Write((byte)rt.TypeArguments.Count); + foreach (IReturnType typeArgument in rt.TypeArguments) { WriteType(typeArgument); } } else if (rt is GenericReturnType) { diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/CodeGenerator.cs b/src/Main/Base/Project/Src/Services/RefactoringService/CodeGenerator.cs index dd94adadc2..49f638dd88 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/CodeGenerator.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/CodeGenerator.cs @@ -24,17 +24,15 @@ namespace ICSharpCode.SharpDevelop.Refactoring if (returnType is NullReturnType) return TypeReference.Null; TypeReference typeRef = new TypeReference(returnType.FullyQualifiedName); - while (returnType is ArrayReturnType) { - ArrayReturnType art = (ArrayReturnType)returnType; + while (returnType.ArrayDimensions > 0) { int[] rank = typeRef.RankSpecifier ?? new int[0]; Array.Resize(ref rank, rank.Length + 1); - rank[rank.Length - 1] = art.ArrayDimensions; + rank[rank.Length - 1] = returnType.ArrayDimensions; typeRef.RankSpecifier = rank; - returnType = art.ElementType; + returnType = returnType.ArrayElementType; } - if (returnType is ConstructedReturnType) { - ConstructedReturnType rt = (ConstructedReturnType)returnType; - foreach (IReturnType typeArgument in rt.TypeArguments) { + if (returnType.TypeArguments != null) { + foreach (IReturnType typeArgument in returnType.TypeArguments) { typeRef.GenericTypes.Add(ConvertType(typeArgument)); } } diff --git a/src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj b/src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj index c7f16f2b31..37cc958de4 100644 --- a/src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj +++ b/src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Main/Base/Test/MemberLookupHelperTests.cs b/src/Main/Base/Test/MemberLookupHelperTests.cs new file mode 100644 index 0000000000..8def52249a --- /dev/null +++ b/src/Main/Base/Test/MemberLookupHelperTests.cs @@ -0,0 +1,44 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Dom; + +namespace ICSharpCode.SharpDevelop.Tests +{ + [TestFixture] + public class MemberLookupHelperTests + { + IProjectContent msc = ProjectContentRegistry.Mscorlib; + + public IReturnType DictionaryRT { + get { + return new GetClassReturnType(msc, "System.Collections.Generic.Dictionary", 2); + } + } + + public IClass EnumerableClass { + get { + return msc.GetClass("System.Collections.Generic.IEnumerable", 1); + } + } + + [Test] + public void TypeParameterPassedToBaseClassTest() + { + IReturnType[] stringInt = { ReflectionReturnType.String, ReflectionReturnType.Int }; + IReturnType rrt = new ConstructedReturnType(DictionaryRT, stringInt); + IReturnType res = MemberLookupHelper.GetTypeParameterPassedToBaseClass(rrt, EnumerableClass, 0); + Assert.AreEqual("System.Collections.Generic.KeyValuePair", res.FullyQualifiedName); + Assert.AreEqual("System.String", res.TypeArguments[0].FullyQualifiedName); + Assert.AreEqual("System.Int32", res.TypeArguments[1].FullyQualifiedName); + } + } +}