Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Refactoring/TypeGuessing.cs @ 13397

Last change on this file since 13397 was 11700, checked in by jkarder, 9 years ago

#2077: created branch and added first version

File size: 12.2 KB
Line 
1//
2// TypeGuessing.cs
3//
4// Author:
5//       Mike KrÃŒger <mkrueger@xamarin.com>
6//
7// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26using System;
27using System.Collections.Generic;
28using ICSharpCode.NRefactory.TypeSystem;
29using ICSharpCode.NRefactory.CSharp.Resolver;
30using ICSharpCode.NRefactory.CSharp.Refactoring;
31using ICSharpCode.NRefactory.Semantics;
32using System.Linq;
33
34namespace ICSharpCode.NRefactory.CSharp
35{
36  public static class TypeGuessing
37  {
38    static int GetArgumentIndex(IEnumerable<Expression> arguments, AstNode parameter)
39    {
40      int argumentNumber = 0;
41      foreach (var arg in arguments) {
42        if (arg == parameter) {
43          return argumentNumber;
44        }
45        argumentNumber++;
46      }
47      return -1;
48    }
49
50    static IEnumerable<IType> GetAllValidTypesFromInvocation(CSharpAstResolver resolver, InvocationExpression invoke, AstNode parameter)
51    {
52      int index = GetArgumentIndex(invoke.Arguments, parameter);
53      if (index < 0)
54        yield break;
55
56      var targetResult = resolver.Resolve(invoke.Target) as MethodGroupResolveResult;
57      if (targetResult != null) {
58        foreach (var method in targetResult.Methods) {
59          if (index < method.Parameters.Count) {
60            if (method.Parameters [index].IsParams) {
61              var arrayType = method.Parameters [index].Type as ArrayType;
62              if (arrayType != null)
63                yield return arrayType.ElementType;
64            }
65
66            yield return method.Parameters [index].Type;
67          }
68        }
69        foreach (var extMethods in targetResult.GetExtensionMethods ()) {
70          foreach (var extMethod in extMethods) {
71            IType[] inferredTypes;
72            var m = extMethod;
73            if (CSharpResolver.IsEligibleExtensionMethod(targetResult.TargetType, extMethod, true, out inferredTypes)) {
74              if (inferredTypes != null)
75                m = extMethod.Specialize(new TypeParameterSubstitution(null, inferredTypes));
76            }
77
78            int correctedIndex = index + 1;
79            if (correctedIndex < m.Parameters.Count) {
80              if (m.Parameters [correctedIndex].IsParams) {
81                var arrayType = m.Parameters [correctedIndex].Type as ArrayType;
82                if (arrayType != null)
83                  yield return arrayType.ElementType;
84              }
85              yield return m.Parameters [correctedIndex].Type;
86            }
87          }
88        }
89      }
90    }
91
92    static IEnumerable<IType> GetAllValidTypesFromObjectCreation(CSharpAstResolver resolver, ObjectCreateExpression invoke, AstNode parameter)
93    {
94      int index = GetArgumentIndex(invoke.Arguments, parameter);
95      if (index < 0)
96        yield break;
97
98      var targetResult = resolver.Resolve(invoke.Type);
99      if (targetResult is TypeResolveResult) {
100        var type = ((TypeResolveResult)targetResult).Type;
101        if (type.Kind == TypeKind.Delegate && index == 0) {
102          yield return type;
103          yield break;
104        }
105        foreach (var constructor in type.GetConstructors ()) {
106          if (index < constructor.Parameters.Count)
107            yield return constructor.Parameters [index].Type;
108        }
109      }
110    }
111
112    public static IType GetElementType(CSharpAstResolver resolver, IType type)
113    {
114      // TODO: A better get element type method.
115      if (type.Kind == TypeKind.Array || type.Kind == TypeKind.Dynamic) {
116        if (type.Kind == TypeKind.Array)
117          return ((ArrayType)type).ElementType;
118        return resolver.Compilation.FindType(KnownTypeCode.Object);
119      }
120
121
122      foreach (var method in type.GetMethods (m => m.Name == "GetEnumerator")) {
123        IType returnType = null;
124        foreach (var prop in method.ReturnType.GetProperties(p => p.Name == "Current")) {
125          if (returnType != null && prop.ReturnType.IsKnownType (KnownTypeCode.Object))
126            continue;
127          returnType = prop.ReturnType;
128        }
129        if (returnType != null)
130          return returnType;
131      }
132
133      return resolver.Compilation.FindType(KnownTypeCode.Object);
134    }
135
136    static IEnumerable<IType> GuessFromConstructorInitializer(CSharpAstResolver resolver, AstNode expr)
137    {
138      var init = expr.Parent as ConstructorInitializer;
139      var rr = resolver.Resolve(expr.Parent);
140      int index = GetArgumentIndex(init.Arguments, expr);
141      if (index >= 0) {
142        foreach (var constructor in rr.Type.GetConstructors()) {
143          if (index < constructor.Parameters.Count) {
144            yield return constructor.Parameters[index].Type;
145          }
146        }
147      }
148    }
149
150    public static IEnumerable<IType> GetValidTypes(CSharpAstResolver resolver, AstNode expr)
151    {
152      if (expr.Role == Roles.Condition) {
153        return new [] { resolver.Compilation.FindType (KnownTypeCode.Boolean) };
154      }
155
156      var mref = expr as MemberReferenceExpression;
157      if (mref != null) {
158        // case: guess enum when trying to access not existent enum member
159        var rr = resolver.Resolve(mref.Target);
160        if (!rr.IsError && rr.Type.Kind == TypeKind.Enum)
161          return new [] { rr.Type };
162      }
163
164      if (expr.Parent is ParenthesizedExpression || expr.Parent is NamedArgumentExpression) {
165        return GetValidTypes(resolver, expr.Parent);
166      }
167      if (expr.Parent is DirectionExpression) {
168        var parent = expr.Parent.Parent;
169        if (parent is InvocationExpression) {
170          var invoke = (InvocationExpression)parent;
171          return GetAllValidTypesFromInvocation(resolver, invoke, expr.Parent);
172        }
173      }
174
175      if (expr.Parent is ArrayInitializerExpression) {
176        if (expr is NamedExpression)
177          return new [] { resolver.Resolve(((NamedExpression)expr).Expression).Type };
178
179        var aex = expr.Parent as ArrayInitializerExpression;
180        if (aex.IsSingleElement)
181          aex = aex.Parent as ArrayInitializerExpression;
182        var type = GetElementType(resolver, resolver.Resolve(aex.Parent).Type);
183        if (type.Kind != TypeKind.Unknown)
184          return new [] { type };
185      }
186
187      if (expr.Parent is ObjectCreateExpression) {
188        var invoke = (ObjectCreateExpression)expr.Parent;
189        return GetAllValidTypesFromObjectCreation(resolver, invoke, expr);
190      }
191
192      if (expr.Parent is ArrayCreateExpression) {
193        var ace = (ArrayCreateExpression)expr.Parent;
194        if (!ace.Type.IsNull) {
195          return new [] { resolver.Resolve(ace.Type).Type };
196        }
197      }
198
199      if (expr.Parent is InvocationExpression) {
200        var parent = expr.Parent;
201        if (parent is InvocationExpression) {
202          var invoke = (InvocationExpression)parent;
203          return GetAllValidTypesFromInvocation(resolver, invoke, expr);
204        }
205      }
206
207      if (expr.Parent is VariableInitializer) {
208        var initializer = (VariableInitializer)expr.Parent;
209        var field = initializer.GetParent<FieldDeclaration>();
210        if (field != null) {
211          var rr = resolver.Resolve(field.ReturnType);
212          if (!rr.IsError)
213            return new [] { rr.Type };
214        }
215        var varStmt = initializer.GetParent<VariableDeclarationStatement>();
216        if (varStmt != null) {
217          var rr = resolver.Resolve(varStmt.Type);
218          if (!rr.IsError)
219            return new [] { rr.Type };
220        }
221        return new [] { resolver.Resolve(initializer).Type };
222      }
223
224      if (expr.Parent is CastExpression) {
225        var cast = (CastExpression)expr.Parent;
226        return new [] { resolver.Resolve(cast.Type).Type };
227      }
228
229      if (expr.Parent is AsExpression) {
230        var cast = (AsExpression)expr.Parent;
231        return new [] { resolver.Resolve(cast.Type).Type };
232      }
233
234      if (expr.Parent is AssignmentExpression) {
235        var assign = (AssignmentExpression)expr.Parent;
236        var other = assign.Left == expr ? assign.Right : assign.Left;
237        return new [] { resolver.Resolve(other).Type };
238      }
239
240      if (expr.Parent is BinaryOperatorExpression) {
241        var assign = (BinaryOperatorExpression)expr.Parent;
242        var other = assign.Left == expr ? assign.Right : assign.Left;
243        return new [] { resolver.Resolve(other).Type };
244      }
245
246      if (expr.Parent is ReturnStatement) {
247        var parent = expr.Ancestors.FirstOrDefault(n => n is EntityDeclaration || n is AnonymousMethodExpression|| n is LambdaExpression);
248        if (parent != null) {
249          var rr = resolver.Resolve(parent);
250          if (!rr.IsError)
251            return new [] { rr.Type };
252        }
253        var e = parent as EntityDeclaration;
254        if (e != null) {
255          var rt = resolver.Resolve(e.ReturnType);
256          if (!rt.IsError)
257            return new [] { rt.Type };
258        }
259      }
260
261      if (expr.Parent is YieldReturnStatement) {
262        ParameterizedType pt = null;
263        var parent = expr.Ancestors.FirstOrDefault(n => n is EntityDeclaration || n is AnonymousMethodExpression|| n is LambdaExpression);
264        if (parent != null) {
265          var rr = resolver.Resolve(parent);
266          if (!rr.IsError)
267            pt = rr.Type as ParameterizedType;
268        }
269        var e = parent as EntityDeclaration;
270        if (e != null) {
271          var rt = resolver.Resolve(e.ReturnType);
272          if (!rt.IsError)
273            pt = rt.Type as ParameterizedType;
274        }
275        if (pt != null) {
276          if (pt.FullName == "System.Collections.Generic.IEnumerable") {
277            return new [] { pt.TypeArguments.First() };
278          }
279        }
280      }
281
282      if (expr.Parent is UnaryOperatorExpression) {
283        var uop = (UnaryOperatorExpression)expr.Parent;
284        switch (uop.Operator) {
285          case UnaryOperatorType.Not:
286            return new [] { resolver.Compilation.FindType(KnownTypeCode.Boolean) };
287            case UnaryOperatorType.Minus:
288            case UnaryOperatorType.Plus:
289            case UnaryOperatorType.Increment:
290            case UnaryOperatorType.Decrement:
291            case UnaryOperatorType.PostIncrement:
292            case UnaryOperatorType.PostDecrement:
293            return new [] { resolver.Compilation.FindType(KnownTypeCode.Int32) };
294        }
295      }
296
297      if (expr.Parent is ConstructorInitializer)
298        return GuessFromConstructorInitializer(resolver, expr);
299
300      if (expr.Parent is NamedExpression) {
301        var rr = resolver.Resolve(expr.Parent);
302        if (!rr.IsError) {
303          return new [] { rr.Type };
304        }
305      }
306
307      return Enumerable.Empty<IType>();
308    }
309    static readonly IType[] emptyTypes = new IType[0];
310    public static AstType GuessAstType(RefactoringContext context, AstNode expr)
311    {
312      var type = GetValidTypes(context.Resolver, expr).ToArray();
313      var typeInference = new TypeInference(context.Compilation);
314      typeInference.Algorithm = TypeInferenceAlgorithm.Improved;
315      var inferedType = typeInference.FindTypeInBounds(type, emptyTypes);
316      if (inferedType.Kind == TypeKind.Unknown)
317        return new PrimitiveType("object");
318      return context.CreateShortType(inferedType);
319    }
320
321    public static IType GuessType(BaseRefactoringContext context, AstNode expr)
322    {
323      if (expr is SimpleType && expr.Role == Roles.TypeArgument) {
324        if (expr.Parent is MemberReferenceExpression || expr.Parent is IdentifierExpression) {
325          var rr = context.Resolve (expr.Parent);
326          var argumentNumber = expr.Parent.GetChildrenByRole (Roles.TypeArgument).TakeWhile (c => c != expr).Count ();
327
328          var mgrr = rr as MethodGroupResolveResult;
329          if (mgrr != null && mgrr.Methods.Any () && mgrr.Methods.First ().TypeArguments.Count > argumentNumber)
330            return mgrr.Methods.First ().TypeParameters[argumentNumber];
331        } else if (expr.Parent is MemberType || expr.Parent is SimpleType) {
332          var rr = context.Resolve (expr.Parent);
333          var argumentNumber = expr.Parent.GetChildrenByRole (Roles.TypeArgument).TakeWhile (c => c != expr).Count ();
334          var mgrr = rr as TypeResolveResult;
335          if (mgrr != null &&  mgrr.Type.TypeParameterCount > argumentNumber) {
336            return mgrr.Type.GetDefinition ().TypeParameters[argumentNumber];
337          }
338        }
339      }
340
341      var type = GetValidTypes(context.Resolver, expr).ToArray();
342      var typeInference = new TypeInference(context.Compilation);
343      typeInference.Algorithm = TypeInferenceAlgorithm.Improved;
344      var inferedType = typeInference.FindTypeInBounds(type, emptyTypes);
345      return inferedType;
346    }
347  }
348}
349
Note: See TracBrowser for help on using the repository browser.