~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/OptionalParameterCouldBeSkippedIssue.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// OptionalParameterCouldBeSkippedIssue.cs
 
3
//
 
4
// Author:
 
5
//       Simon Lindgren <simon.n.lindgren@gmail.com>
 
6
//
 
7
// Copyright (c) 2012 Simon Lindgren
 
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.
 
26
using System.Collections.Generic;
 
27
using ICSharpCode.NRefactory.CSharp.Resolver;
 
28
using System.Linq;
 
29
using ICSharpCode.NRefactory.Semantics;
 
30
using ICSharpCode.NRefactory.TypeSystem;
 
31
using System;
 
32
 
 
33
namespace ICSharpCode.NRefactory.CSharp.Refactoring
 
34
{
 
35
        [IssueDescription("Optional argument has default value and can be skipped",
 
36
                          Description = "Finds calls to functions where optional parameters are used and the passed argument is the same as the default.",
 
37
                          Category = IssueCategories.Redundancies,
 
38
                          Severity = Severity.Hint,
 
39
                          IssueMarker = IssueMarker.GrayOut)]
 
40
        public class OptionalParameterCouldBeSkippedIssue : ICodeIssueProvider
 
41
        {
 
42
                public IEnumerable<CodeIssue> GetIssues(BaseRefactoringContext context)
 
43
                {
 
44
                        return new GatherVisitor(context).GetIssues();
 
45
                }
 
46
                
 
47
                class GatherVisitor : GatherVisitorBase<OptionalParameterCouldBeSkippedIssue>
 
48
                {
 
49
                        public GatherVisitor(BaseRefactoringContext context) : base (context)
 
50
                        {
 
51
                        }
 
52
 
 
53
                        public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
 
54
                        {
 
55
                                base.VisitObjectCreateExpression(objectCreateExpression);
 
56
                                
 
57
                                CheckMethodCall(objectCreateExpression, objectCreateExpression.Arguments,
 
58
                                                (objectCreation, args) => new ObjectCreateExpression(objectCreation.Type.Clone(), args));
 
59
                        }
 
60
 
 
61
                        public override void VisitInvocationExpression(InvocationExpression invocationExpression)
 
62
                        {
 
63
                                base.VisitInvocationExpression(invocationExpression);
 
64
                                
 
65
                                CheckMethodCall(invocationExpression, invocationExpression.Arguments,
 
66
                                                (invocation, args) => new InvocationExpression(invocation.Target.Clone(), args));
 
67
                        }
 
68
 
 
69
                        void CheckMethodCall<T> (T node, IEnumerable<Expression> args, Func<T, IEnumerable<Expression>, T> generateReplacement) where T: AstNode
 
70
                        {
 
71
                                // The first two checks are unnecessary, but eliminates the majority of calls early,
 
72
                                // improving performance.
 
73
                                var arguments = args.ToArray();
 
74
                                if (arguments.Length == 0)
 
75
                                        return;
 
76
                                var lastArg = arguments[arguments.Length - 1];
 
77
                                if (!(lastArg is PrimitiveExpression || lastArg is NamedArgumentExpression))
 
78
                                        return;
 
79
 
 
80
                                var invocationResolveResult = ctx.Resolve(node) as CSharpInvocationResolveResult;
 
81
                                if (invocationResolveResult == null)
 
82
                                        return;
 
83
                                
 
84
                                string actionMessage = ctx.TranslateString("Remove redundant arguments");
 
85
                                
 
86
                                var redundantArguments = GetRedundantArguments(arguments, invocationResolveResult);
 
87
                                var action = new CodeAction(actionMessage, script => {
 
88
                                        var newArgumentList = arguments
 
89
                                                .Where(arg => !redundantArguments.Contains(arg))
 
90
                                                .Select(arg => arg.Clone());
 
91
                                        var newInvocation = generateReplacement(node, newArgumentList);
 
92
                                        script.Replace(node, newInvocation);
 
93
                                }, node);
 
94
                                var issueMessage = ctx.TranslateString("Argument is identical to the default value");
 
95
                                var lastPositionalArgument = redundantArguments.FirstOrDefault(expression => !(expression is NamedArgumentExpression));
 
96
 
 
97
                                foreach (var argument in redundantArguments) {
 
98
                                        var localArgument = argument;
 
99
                                        var actions = new List<CodeAction>();
 
100
                                        actions.Add(action);
 
101
 
 
102
                                        if (localArgument is NamedArgumentExpression || localArgument == lastPositionalArgument) {
 
103
                                                var title = ctx.TranslateString("Remove this argument");
 
104
                                                actions.Add(new CodeAction(title, script => {
 
105
                                                        var newArgumentList = arguments
 
106
                                                                .Where(arg => arg != localArgument)
 
107
                                                                .Select(arg => arg.Clone());
 
108
                                                        var newInvocation = generateReplacement(node, newArgumentList);
 
109
                                                        script.Replace(node, newInvocation);
 
110
                                                }, node));
 
111
                                        } else {
 
112
                                                var title = ctx.TranslateString("Remove this and the following positional arguments");
 
113
                                                actions.Add(new CodeAction(title, script => {
 
114
                                                        var newArgumentList = arguments
 
115
                                                                .Where(arg => arg.StartLocation < localArgument.StartLocation && !(arg is NamedArgumentExpression))
 
116
                                                                .Select(arg => arg.Clone());
 
117
                                                        var newInvocation = generateReplacement(node, newArgumentList);
 
118
                                                        script.Replace(node, newInvocation);
 
119
                                                }, node));
 
120
                                        }
 
121
 
 
122
                                        AddIssue(localArgument, issueMessage, actions);
 
123
                                }
 
124
                        }
 
125
 
 
126
                        IList<Expression> GetRedundantArguments(Expression[] arguments, CSharpInvocationResolveResult invocationResolveResult)
 
127
                        {
 
128
                                var argumentToParameterMap = invocationResolveResult.GetArgumentToParameterMap();
 
129
                                var resolvedParameters = invocationResolveResult.Member.Parameters;
 
130
 
 
131
                                IList<Expression> redundantArguments = new List<Expression>();
 
132
 
 
133
                                for (int i = arguments.Length - 1; i >= 0; i--) {
 
134
                                        var parameterIndex = argumentToParameterMap[i];
 
135
                                        if (parameterIndex == -1)
 
136
                                                // This particular parameter is an error, but keep trying the other ones
 
137
                                                continue;
 
138
                                        var parameter = resolvedParameters[parameterIndex];
 
139
                                        var argument = arguments[i];
 
140
                                        if (argument is PrimitiveExpression) {
 
141
                                                if (parameter.IsParams)
 
142
                                                        // before positional params arguments all optional arguments are needed, otherwise some of the
 
143
                                                        // param arguments will be shifted out of the params into the fixed parameters
 
144
                                                        break;
 
145
                                                if (!parameter.IsOptional)
 
146
                                                        // There can be no optional parameters preceding a required one
 
147
                                                        break;
 
148
                                                var argumentResolveResult = ctx.Resolve(argument) as ConstantResolveResult;
 
149
                                                if (argumentResolveResult == null || parameter.ConstantValue != argumentResolveResult.ConstantValue)
 
150
                                                        // Stop here since any arguments before this one has to be there
 
151
                                                        // to enable the passing of this argument
 
152
                                                        break;
 
153
                                                redundantArguments.Add(argument);
 
154
                                        } else if (argument is NamedArgumentExpression) {
 
155
                                                var expression = ((NamedArgumentExpression)argument).Expression as PrimitiveExpression;
 
156
                                                if (expression == null)
 
157
                                                        continue;
 
158
                                                var expressionResolveResult = ctx.Resolve(expression) as ConstantResolveResult;
 
159
                                                if (expressionResolveResult == null || parameter.ConstantValue != expressionResolveResult.ConstantValue)
 
160
                                                        // continue, since there can still be more arguments that are redundant
 
161
                                                        continue;
 
162
                                                redundantArguments.Add(argument);
 
163
                                        } else {
 
164
                                                // This is a non-constant positional argument => no more redundancies are possible
 
165
                                                break;
 
166
                                        }
 
167
                                }
 
168
                                return redundantArguments;
 
169
                        }
 
170
                }
 
171
        }
 
172
}
 
173