2
// CallToStaticMemberViaDerivedTypeIssue.cs
5
// Simon Lindgren <simon.n.lindgren@gmail.com>
7
// Copyright (c) 2012 Simon Lindgren
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
26
using System.Collections.Generic;
27
using ICSharpCode.NRefactory.Semantics;
28
using ICSharpCode.NRefactory.TypeSystem;
30
namespace ICSharpCode.NRefactory.CSharp.Refactoring
32
[IssueDescription("Call to static member via a derived class",
33
Description = "Suggests using the class declaring a static function when calling it.",
34
Category = IssueCategories.CodeQualityIssues,
35
Severity = Severity.Suggestion,
36
ResharperDisableKeyword = "AccessToStaticMemberViaDerivedType")]
37
public class ReferenceToStaticMemberViaDerivedTypeIssue : ICodeIssueProvider
39
#region ICodeIssueProvider implementation
40
public IEnumerable<CodeIssue> GetIssues(BaseRefactoringContext context)
42
return new GatherVisitor(context).GetIssues();
45
class GatherVisitor : GatherVisitorBase<ReferenceToStaticMemberViaDerivedTypeIssue>
47
readonly BaseRefactoringContext context;
49
public GatherVisitor(BaseRefactoringContext context) : base (context)
51
this.context = context;
54
public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
56
base.VisitMemberReferenceExpression(memberReferenceExpression);
57
if (memberReferenceExpression == null || memberReferenceExpression.Target is ThisReferenceExpression)
58
// Call within current class scope using 'this' or 'base'
60
var memberResolveResult = context.Resolve(memberReferenceExpression) as MemberResolveResult;
61
if (memberResolveResult == null)
63
if (!memberResolveResult.Member.IsStatic)
65
HandleMember(memberReferenceExpression, memberReferenceExpression.Target, memberResolveResult.Member, memberResolveResult.TargetResult);
68
public override void VisitInvocationExpression(InvocationExpression invocationExpression)
70
base.VisitInvocationExpression(invocationExpression);
71
if (invocationExpression.Target is IdentifierExpression)
72
// Call within current class scope without 'this' or 'base'
74
var memberReference = invocationExpression.Target as MemberReferenceExpression;
75
if (memberReference == null || memberReference.Target is ThisReferenceExpression)
76
// Call within current class scope using 'this' or 'base'
78
var invocationResolveResult = context.Resolve(invocationExpression) as InvocationResolveResult;
79
if (invocationResolveResult == null)
81
HandleMember(invocationExpression, memberReference.Target, invocationResolveResult.Member, invocationResolveResult.TargetResult);
84
void HandleMember(Expression issueAnchor, Expression targetExpression, IMember member, ResolveResult targetResolveResult)
86
var typeResolveResult = targetResolveResult as TypeResolveResult;
87
if (typeResolveResult == null)
91
if (typeResolveResult.Type.Equals(member.DeclaringType))
93
// check whether member.DeclaringType contains the original type
94
// (curiously recurring template pattern)
95
var v = new ContainsTypeVisitor(typeResolveResult.Type.GetDefinition());
96
member.DeclaringType.AcceptVisitor(v);
99
AddIssue(issueAnchor, context.TranslateString("Static method invoked via derived type"),
100
GetAction(context, targetExpression, member));
103
CodeAction GetAction(BaseRefactoringContext context, Expression targetExpression,
106
var builder = context.CreateTypeSytemAstBuilder(targetExpression);
107
var newType = builder.ConvertType(member.DeclaringType);
108
string description = string.Format("{0} '{1}'", context.TranslateString("Use base class"), newType.GetText());
109
return new CodeAction(description, script => {
110
script.Replace(targetExpression, newType);
111
}, targetExpression);
114
sealed class ContainsTypeVisitor : TypeVisitor
116
readonly ITypeDefinition searchedType;
117
internal bool IsContained;
119
public ContainsTypeVisitor(ITypeDefinition searchedType)
121
this.searchedType = searchedType;
124
public override IType VisitTypeDefinition(ITypeDefinition type)
126
if (type.Equals(searchedType))
128
return base.VisitTypeDefinition(type);