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

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/DuplicateBodyMethodIssue.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
// DuplicateBodyMethodIssue.cs
 
3
//
 
4
// Author:
 
5
//       Ciprian Khlud <ciprian.mustiata@yahoo.com>
 
6
//
 
7
// Copyright (c) 2013 Ciprian Khlud
 
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
 
 
27
using System.Collections.Generic;
 
28
using System.Text;
 
29
using ICSharpCode.NRefactory.PatternMatching;
 
30
using ICSharpCode.NRefactory.Semantics;
 
31
using System.Linq;
 
32
 
 
33
namespace ICSharpCode.NRefactory.CSharp.Refactoring
 
34
{
 
35
    [IssueDescription("Methods have duplicate body",
 
36
                      Description = "One method has the same body as other method",
 
37
                      Severity = Severity.Hint,
 
38
                      IssueMarker = IssueMarker.Underline)]
 
39
    public class DuplicateBodyMethodIssue : ICodeIssueProvider
 
40
    {
 
41
        #region ICodeIssueProvider implementation
 
42
 
 
43
        public IEnumerable<CodeIssue> GetIssues(BaseRefactoringContext context)
 
44
        {
 
45
            var visitor = new GatherVisitor(context);
 
46
            visitor.GetMethods();
 
47
            visitor.ComputeConflicts();
 
48
            return visitor.GetIssues();
 
49
        }
 
50
 
 
51
        #endregion
 
52
 
 
53
        private class GatherVisitor : GatherVisitorBase<DuplicateBodyMethodIssue>
 
54
        {
 
55
            public List<MethodDeclaration> DeclaredMethods;
 
56
 
 
57
            public GatherVisitor(BaseRefactoringContext context)
 
58
                : base(context)
 
59
            {
 
60
                DeclaredMethods = new List<MethodDeclaration>();
 
61
            }
 
62
 
 
63
 
 
64
            static string GetMethodDescriptor(MethodDeclaration methodDeclaration) {
 
65
                var sb = new StringBuilder();
 
66
                sb.Append(methodDeclaration.ReturnType);
 
67
                sb.Append(";");
 
68
                foreach (var parameter in methodDeclaration.Parameters) {
 
69
                    sb.AppendFormat("{0}:{1};", parameter.Name, parameter.Type);
 
70
                }
 
71
                sb.Append(methodDeclaration.Modifiers);
 
72
                return sb.ToString();
 
73
            }
 
74
 
 
75
            public void GetMethods()
 
76
            {
 
77
                ctx.RootNode.AcceptVisitor(this);
 
78
            }
 
79
 
 
80
            internal void ComputeConflicts()
 
81
            {
 
82
                var dict = new Dictionary<string, List<MethodDeclaration>>();
 
83
                foreach (var declaredMethod in DeclaredMethods)
 
84
                {
 
85
                    var methodDescriptor = GetMethodDescriptor(declaredMethod);
 
86
                    List<MethodDeclaration> listMethods;
 
87
                    if (!dict.TryGetValue(methodDescriptor, out listMethods))
 
88
                    {
 
89
                        listMethods = new List<MethodDeclaration>();
 
90
                        dict[methodDescriptor] = listMethods;
 
91
                    }
 
92
                    listMethods.Add(declaredMethod);
 
93
                }
 
94
                DeclaredMethods.Clear();
 
95
 
 
96
                foreach (var list in dict.Values.Where(list => list.Count >= 2))
 
97
                {
 
98
                    for (var i = 0; i < list.Count - 1; i++)
 
99
                    {
 
100
                        var firstMethod = list[i];
 
101
                        for (var j = i + 1; j < list.Count; j++)
 
102
                        {
 
103
                            var secondMethod = list[j];
 
104
                            if (firstMethod.Body.IsMatch(secondMethod.Body))
 
105
                            {
 
106
                                AddIssue(secondMethod.NameToken,
 
107
                                         string.Format("Method '{0}' has the same with '{1}' ", secondMethod.Name,
 
108
                                                       firstMethod.Name),
 
109
                                         script => { InvokeMethod(script, firstMethod, secondMethod); }
 
110
                                    );
 
111
                            }
 
112
                        }
 
113
                    }
 
114
                }
 
115
            }
 
116
 
 
117
            readonly InsertParenthesesVisitor _insertParenthesesVisitor = new InsertParenthesesVisitor();
 
118
            private TypeDeclaration _parentType;
 
119
 
 
120
            private void InvokeMethod(Script script, MethodDeclaration firstMethod, MethodDeclaration secondMethod)
 
121
            {
 
122
                var statement =
 
123
                    new ExpressionStatement(new InvocationExpression(new IdentifierExpression(firstMethod.Name),
 
124
                                                                     firstMethod.Parameters.Select(
 
125
                                                                         declaration =>
 
126
                                                                         GetArgumentExpression(declaration).Clone())));
 
127
                statement.AcceptVisitor(_insertParenthesesVisitor);
 
128
                if(firstMethod.ReturnType.ToString()!="System.Void"){
 
129
                    var returnStatement = new ReturnStatement(statement.Expression.Clone());
 
130
 
 
131
                    script.Replace(secondMethod.Body, new BlockStatement { returnStatement });
 
132
                }
 
133
                else {
 
134
                    script.Replace(secondMethod.Body, new BlockStatement { statement });
 
135
                }
 
136
            }
 
137
 
 
138
            static Expression GetArgumentExpression(ParameterDeclaration parameter)
 
139
            {
 
140
                var identifierExpr = new IdentifierExpression(parameter.Name);
 
141
                switch (parameter.ParameterModifier)
 
142
                {
 
143
                    case ParameterModifier.Out:
 
144
                        return new DirectionExpression(FieldDirection.Out, identifierExpr);
 
145
                    case ParameterModifier.Ref:
 
146
                        return new DirectionExpression(FieldDirection.Ref, identifierExpr);
 
147
                }
 
148
                return identifierExpr;
 
149
            }
 
150
 
 
151
            public override void VisitMethodDeclaration(MethodDeclaration declaration)
 
152
            {
 
153
                var context = ctx;
 
154
                var methodDeclaration = declaration;
 
155
 
 
156
                var resolved = context.Resolve(methodDeclaration) as MemberResolveResult;
 
157
                if (resolved == null)
 
158
                    return;
 
159
                var isImplementingInterface = resolved.Member.ImplementedInterfaceMembers.Any();
 
160
 
 
161
                if (isImplementingInterface)
 
162
                    return;
 
163
                if (declaration.Body.IsNull)
 
164
                    return;
 
165
                
 
166
                var parentType = declaration.Parent as TypeDeclaration;
 
167
                if (parentType == null)
 
168
                    return;
 
169
                if (_parentType == null)
 
170
                    _parentType = parentType;
 
171
                else
 
172
                {
 
173
                    //if we are here, it means that we switched from one class to another, so it means that we should compute 
 
174
                    //the duplicates up-to now, then reset the list of methods
 
175
                    if (parentType != _parentType)
 
176
                    {
 
177
                        ComputeConflicts();
 
178
                        DeclaredMethods.Add(declaration); 
 
179
                        _parentType = parentType;
 
180
                        return;
 
181
                    }
 
182
                }
 
183
 
 
184
                DeclaredMethods.Add(declaration);
 
185
            }
 
186
        }
 
187
    }
 
188
}