Пользовательское правило в FxCop для применения только к методам, вызываемым методом определенного типа
Я хочу создать FxRule, который применяет метод, только если метод вызывается из определенного класса.
ПРИМЕЧАНИЕ: я не хочу просто применять правило к методу определенного класса, я хочу иметь возможность обрабатывать методы, вызывающие другие методы, вызывающие другие методы, которые выполняют упаковку.
Я хотел бы получить FxCop, чтобы сообщить о проблемах, связанных с методом, который делает бокс.
Ниже приведен код, который у меня есть:
using System;
using System.Linq;
using Microsoft.FxCop.Sdk;
using System.Collections.Generic;
class CheckUpdatableComponents : BaseIntrospectionRule
{
private string[] MethodsToCheck = new string[] { "BeginDraw", "BeginRun", "Draw", "EndRun", "EndDraw", "Update" };
/// <summary>Gets the base class hooked up.</summary>
public CheckUpdatableComponents()
: base("CheckUpdatableComponents", "FxCopRules.Rules", typeof(CheckUpdatableComponents).Assembly)
{
}
public override ProblemCollection Check(string namespaceName, TypeNodeCollection types)
{
foreach (var type in types.Where(T => IsSubClassOf(T, "Microsoft.Xna.Framework.Game")))
{
foreach (var MethodToCheck in MethodsToCheck)
{
Method RunMethod = type.GetMethod(Identifier.For(MethodToCheck));
if (RunMethod != null)
{
Visit(RunMethod);
}
}
}
return Problems;
}
public override void VisitMethod(Method method)
{
Problems.Add(new Problem(GetResolution(), method, method.ToString())); // This problem only appears for each of the RunMethods, and doesn't seem to be recursing down the tree.
foreach (var Instruction in method.Instructions)
{
if (Instruction.NodeType == NodeType.Box ||
Instruction.NodeType == NodeType.Unbox ||
Instruction.NodeType == NodeType.UnboxAny ||
Instruction.OpCode == OpCode.Box ||
Instruction.OpCode == OpCode.Unbox ||
Instruction.OpCode == OpCode.Unbox_Any)
{
}
}
base.VisitMethod(method);
}
private bool IsSubClassOf(TypeNode type, string typeName)
{
if (type.FullName == typeName)
return true;
if (type.BaseType == null)
return false;
else
return IsSubClassOf(type.BaseType, typeName);
}
}
Моя проблема с приведенным выше кодом, во-первых, в том, что он не повторяется. Во-вторых, этот FxCop сообщает о проблемах, связанных с пространством имен (вероятно, потому, что я начинаю посещение с использованием части Check(namespace....).
Моя проблема в том, что я хочу, чтобы FxCop сообщал о методе, для которого бокс является проблемой, но только если он вызывается определенным методом, однако у меня нет возможности пройтись по дереву вызовов, я могу посещать только нижние узлы, чтобы проверить проблемы с моим стартовым местоположением.
Кто-нибудь делал такие вещи раньше?
Как я могу узнать, какие методы вызывают данный метод?
1 ответ
РЕДАКТИРОВАТЬ: Это не работает в случае вызовов виртуальных методов, где IL callvirt
, Смотрите мой вопрос здесь.
Мне удалось решить это, обнаружив метод CallGraph.CallersFor()
, Сейчас я ищу методы, объявленные с заданным атрибутом или объявленные классом с заданным атрибутом, но принципал тот же.
using System;
using System.Linq;
using Microsoft.FxCop.Sdk;
using System.Collections.Generic;
class CheckUpdatableComponents : BaseIntrospectionRule
{
// private string[] MethodsToCheckNames = new string[] { "BeginDraw", "BeginRun", "Draw", "EndRun", "EndDraw", "Update" };
/// <summary>Gets the base class hooked up.</summary>
public CheckUpdatableComponents()
: base("CheckUpdatableComponents", "FxCopRules.Rules", typeof(CheckUpdatableComponents).Assembly)
{
}
public override ProblemCollection Check(Member member)
{
Method method = member as Method;
if (method != null)
{
if (ShouldCheckMethod(method))
{
foreach (var Instruction in method.Instructions)
{
if (Instruction.NodeType == NodeType.Box ||
Instruction.NodeType == NodeType.Unbox ||
Instruction.NodeType == NodeType.UnboxAny ||
Instruction.OpCode == OpCode.Box ||
Instruction.OpCode == OpCode.Unbox ||
Instruction.OpCode == OpCode.Unbox_Any)
{
Problems.Add(new Problem(GetResolution(), Instruction, Instruction.SourceContext.StartLine.ToString()));
}
}
}
}
return Problems;
}
public bool ShouldCheckMethod(Method method)
{
Queue<Method> MethodsToCheck = new Queue<Method>();
List<Method> MethodsChecked = new List<Method>();
MethodsToCheck.Enqueue(method);
while (MethodsToCheck.Count != 0)
{
Method MethodToCheck = MethodsToCheck.Dequeue();
if (!MethodsChecked.Contains(MethodToCheck) && MethodToCheck != null)
{
/*if (IsSubClassOf(MethodToCheck.DeclaringType, "Microsoft.Xna.Framework.Game") &&
MethodsToCheckNames.Contains(MethodToCheck.Name.Name))
{
return true;
}*/
foreach (var attribute in MethodToCheck.Attributes.Union(MethodToCheck.DeclaringType.Attributes))
{
if (attribute.Type != null &&
attribute.Type.FullName == "GridEngine.Components.Debugging.Attributes.FxCop.PerformanceCriticalAttribute")
{
return true;
}
}
// Add methods up the class tree
MethodsToCheck.Enqueue(MethodToCheck.OverriddenMethod);
MethodsToCheck.Enqueue(MethodToCheck.HiddenMethod);
// Add calling methods
foreach (var CallingMethod in CallGraph.CallersFor(MethodToCheck))
{
MethodsToCheck.Enqueue(CallingMethod);
}
}
MethodsChecked.Add(MethodToCheck);
}
return false;
}
private bool IsSubClassOf(TypeNode type, string typeName)
{
if (type.FullName == typeName)
return true;
if (type.BaseType == null)
return false;
else
return IsSubClassOf(type.BaseType, typeName);
}
}