5
// Mike KrĆ¼ger <mkrueger@novell.com>
7
// Copyright (c) 2011 Novell, Inc (http://www.novell.com)
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:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
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
28
using ICSharpCode.NRefactory.PatternMatching;
30
using ICSharpCode.NRefactory.TypeSystem;
31
using System.Threading;
32
using System.Collections.Generic;
33
using ICSharpCode.NRefactory.CSharp.Resolver;
34
using ICSharpCode.NRefactory.Semantics;
36
namespace ICSharpCode.NRefactory.CSharp.Refactoring
38
[ContextAction("Create field", Description = "Creates a field for a undefined variable.")]
39
public class CreateFieldAction : ICodeActionProvider
41
internal static bool IsInvocationTarget(AstNode node)
43
var invoke = node.Parent as InvocationExpression;
44
return invoke != null && invoke.Target == node;
47
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
49
var identifier = context.GetNode<IdentifierExpression>();
50
if (identifier == null)
52
if (IsInvocationTarget(identifier))
54
var statement = identifier.GetParent<Statement>();
55
if (statement == null)
57
if (!(context.Resolve(identifier).IsError))
59
var guessedType = CreateFieldAction.GuessAstType(context, identifier);
60
if (guessedType == null)
62
var state = context.GetResolverStateBefore(identifier);
63
if (state.CurrentMember == null || state.CurrentTypeDefinition == null)
66
bool isStatic = state.CurrentMember.IsStatic | state.CurrentTypeDefinition.IsStatic;
68
// var service = (NamingConventionService)context.GetService(typeof(NamingConventionService));
69
// if (service != null && !service.IsValidName(identifier.Identifier, AffectedEntity.Field, Modifiers.Private, isStatic)) {
73
yield return new CodeAction(context.TranslateString("Create field"), script => {
74
var decl = new FieldDeclaration() {
75
ReturnType = guessedType,
76
Variables = { new VariableInitializer(identifier.Identifier) }
79
decl.Modifiers |= Modifiers.Static;
80
script.InsertWithCursor(context.TranslateString("Create field"), Script.InsertPosition.Before, decl);
86
static int GetArgumentIndex(IEnumerable<Expression> arguments, AstNode parameter)
88
int argumentNumber = 0;
89
foreach (var arg in arguments) {
90
if (arg == parameter) {
91
return argumentNumber;
98
static IEnumerable<IType> GetAllValidTypesFromInvokation(CSharpAstResolver resolver, InvocationExpression invoke, AstNode parameter)
100
int index = GetArgumentIndex(invoke.Arguments, parameter);
104
var targetResult = resolver.Resolve(invoke.Target);
105
if (targetResult is MethodGroupResolveResult) {
106
foreach (var method in ((MethodGroupResolveResult)targetResult).Methods) {
107
if (index < method.Parameters.Count) {
108
yield return method.Parameters [index].Type;
114
static IEnumerable<IType> GetAllValidTypesFromObjectCreation(CSharpAstResolver resolver, ObjectCreateExpression invoke, AstNode parameter)
116
int index = GetArgumentIndex(invoke.Arguments, parameter);
120
var targetResult = resolver.Resolve(invoke.Type);
121
if (targetResult is TypeResolveResult) {
122
var type = ((TypeResolveResult)targetResult).Type;
123
if (type.Kind == TypeKind.Delegate && index == 0) {
127
foreach (var constructor in type.GetConstructors ()) {
128
if (index < constructor.Parameters.Count)
129
yield return constructor.Parameters [index].Type;
134
static IType GetElementType(CSharpAstResolver resolver, IType type)
136
// TODO: A better get element type method.
137
if (type.Kind == TypeKind.Array || type.Kind == TypeKind.Dynamic) {
138
if (type.Kind == TypeKind.Array)
139
return ((ArrayType)type).ElementType;
140
return resolver.Compilation.FindType(KnownTypeCode.Object);
143
foreach (var method in type.GetMethods (m => m.Name == "GetEnumerator")) {
144
var pr = method.ReturnType.GetProperties(p => p.Name == "Current").FirstOrDefault();
146
return pr.ReturnType;
149
return resolver.Compilation.FindType(KnownTypeCode.Object);
152
internal static IEnumerable<IType> GetValidTypes(CSharpAstResolver resolver, Expression expr)
154
if (expr.Parent is DirectionExpression) {
155
var parent = expr.Parent.Parent;
156
if (parent is InvocationExpression) {
157
var invoke = (InvocationExpression)parent;
158
return GetAllValidTypesFromInvokation(resolver, invoke, expr.Parent);
162
if (expr.Parent is ArrayInitializerExpression) {
163
var aex = expr.Parent as ArrayInitializerExpression;
164
if (aex.IsSingleElement)
165
aex = aex.Parent as ArrayInitializerExpression;
166
var type = GetElementType(resolver, resolver.Resolve(aex.Parent).Type);
167
if (type.Kind != TypeKind.Unknown)
168
return new [] { type };
171
if (expr.Parent is ObjectCreateExpression) {
172
var invoke = (ObjectCreateExpression)expr.Parent;
173
return GetAllValidTypesFromObjectCreation(resolver, invoke, expr);
176
if (expr.Parent is ArrayCreateExpression) {
177
var ace = (ArrayCreateExpression)expr.Parent;
178
if (!ace.Type.IsNull) {
179
return new [] { resolver.Resolve(ace.Type).Type };
183
if (expr.Parent is InvocationExpression) {
184
var parent = expr.Parent;
185
if (parent is InvocationExpression) {
186
var invoke = (InvocationExpression)parent;
187
return GetAllValidTypesFromInvokation(resolver, invoke, expr);
191
if (expr.Parent is VariableInitializer) {
192
var initializer = (VariableInitializer)expr.Parent;
193
var field = initializer.GetParent<FieldDeclaration>();
195
return new [] { resolver.Resolve(field.ReturnType).Type };
196
return new [] { resolver.Resolve(initializer).Type };
199
if (expr.Parent is CastExpression) {
200
var cast = (CastExpression)expr.Parent;
201
return new [] { resolver.Resolve(cast.Type).Type };
204
if (expr.Parent is AsExpression) {
205
var cast = (AsExpression)expr.Parent;
206
return new [] { resolver.Resolve(cast.Type).Type };
209
if (expr.Parent is AssignmentExpression) {
210
var assign = (AssignmentExpression)expr.Parent;
211
var other = assign.Left == expr ? assign.Right : assign.Left;
212
return new [] { resolver.Resolve(other).Type };
215
if (expr.Parent is BinaryOperatorExpression) {
216
var assign = (BinaryOperatorExpression)expr.Parent;
217
var other = assign.Left == expr ? assign.Right : assign.Left;
218
return new [] { resolver.Resolve(other).Type };
221
if (expr.Parent is ReturnStatement) {
222
var state = resolver.GetResolverStateBefore(expr.Parent);
223
if (state != null && state.CurrentMember != null)
224
return new [] { state.CurrentMember.ReturnType };
227
if (expr.Parent is YieldReturnStatement) {
228
var state = resolver.GetResolverStateBefore(expr);
229
if (state != null && (state.CurrentMember.ReturnType is ParameterizedType)) {
230
var pt = (ParameterizedType)state.CurrentMember.ReturnType;
231
if (pt.FullName == "System.Collections.Generic.IEnumerable") {
232
return new [] { pt.TypeArguments.First() };
237
if (expr.Parent is UnaryOperatorExpression) {
238
var uop = (UnaryOperatorExpression)expr.Parent;
239
switch (uop.Operator) {
240
case UnaryOperatorType.Not:
241
return new [] { resolver.Compilation.FindType(KnownTypeCode.Boolean) };
242
case UnaryOperatorType.Minus:
243
case UnaryOperatorType.Plus:
244
case UnaryOperatorType.Increment:
245
case UnaryOperatorType.Decrement:
246
case UnaryOperatorType.PostIncrement:
247
case UnaryOperatorType.PostDecrement:
248
return new [] { resolver.Compilation.FindType(KnownTypeCode.Int32) };
251
return Enumerable.Empty<IType>();
253
static readonly IType[] emptyTypes = new IType[0];
254
internal static AstType GuessAstType(RefactoringContext context, Expression expr)
256
var type = GetValidTypes(context.Resolver, expr).ToArray();
257
var typeInference = new TypeInference(context.Compilation);
258
typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults;
259
var inferedType = typeInference.FindTypeInBounds(type, emptyTypes);
260
if (inferedType.Kind == TypeKind.Unknown)
261
return new PrimitiveType("object");
262
return context.CreateShortType(inferedType);
265
internal static IType GuessType(RefactoringContext context, Expression expr)
267
var type = GetValidTypes(context.Resolver, expr).ToArray();
268
var typeInference = new TypeInference(context.Compilation);
269
typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults;
270
var inferedType = typeInference.FindTypeInBounds(type, emptyTypes);