I am working on some custom rules looking at resource management, specifically disposing of local variables that implement IDisposable. For some reason, when looking at my teams entire project, I get false hits. When I limit FxCop to just a few types, the false hits disappear. I've been looking at this off and on for a few days and am getting nowhere. Am I missing something obvious
Also, what's with IDataReader It claims it implements IDisposable, but there is no public Dispose method. FxCop sees it as IDisposable also.
Finally, can anyone tell me a better way to determine if a local variable is the return value, other than my IsVariableReturnValue method MethodsShouldDisposeOfDisposableTypes is the class of interest, but most a good deal of the implementation is in LocalResourceRuleBase, as I am working on another rule that shares some of the same logic.
using
System;using
System.Diagnostics;using
System.IO;using
System.Runtime.InteropServices;using
Microsoft.Cci;using
Microsoft.FxCop.Sdk;using
Microsoft.FxCop.Sdk.Introspection;
namespace
ReflectSystems.FxCopRules.ResourceManagement{
[CLSCompliant(
false)][ComVisible(
false)] public sealed class MethodsShouldDisposeOfDisposableTypes : LocalResourceRuleBase{
public MethodsShouldDisposeOfDisposableTypes() : base("MethodsShouldDisposeOfDisposableTypes") {}public override TargetVisibilities TargetVisibility
{
get { return TargetVisibilities.All; }}
public override ProblemCollection Check(Member member)
{
Method = member
as Method; if(Method == null || Method.Name.Name == ".ctor"){
return null;}
if(CheckForLocals() == false){
return null;}
SetMemberName(member);
LocalList locals = Method.Instructions[0].Value
as LocalList;ParameterList parameters = Method.Parameters;
bool checkParameters = (parameters != null && parameters.Length > 0), isParameter;Local variable;
string name; int upperBound = locals.Length; for(int x = 0; x < upperBound; x++){
variable = locals[x];
name = variable.Name.Name;
if(ProblemVariables.ContainsKey(name) == false){
isParameter =
false; if(checkParameters == true){
for(int y = 0; y < parameters.Length; y++){
if(name == parameters{
isParameter =
true; break;}
}
}
if( isParameter == false &&variable.Type.IsAssignableTo(SystemTypes.IDisposable) &&
RuleUtilities.IsCompilerGenerated(variable) ==
false &&IsVariableReturnValue(Method, variable) ==
false){
if( (variable.Type.GetMethod(Identifier.For("Dispose"), new TypeNode[0] {}) != null) ||(variable.Type.BaseType !=
null &&variable.Type.BaseType.GetMethod(Identifier.For("Dispose"),
new TypeNode[0] {}) != null)){
ProblemVariables.Add(name, variable);
}
}
}
}
VisitBlock(Method.Body);
if(ProblemVariables.Count == 0){
return null;}
FormulateProblemsFromVariables();
return Problems;}
public override Expression VisitMethodCall(MethodCall call){
CheckForMethodCall(call, "Dispose");
return base.VisitMethodCall(call);}
}
}
using
System;using
System.Collections;using
System.Diagnostics;using
System.IO;using
System.Runtime.InteropServices;using
Microsoft.Cci;using
Microsoft.FxCop.Sdk;using
Microsoft.FxCop.Sdk.Introspection;
namespace
ReflectSystems.FxCopRules.ResourceManagement{
[CLSCompliant(
false)][ComVisible(
false)] public abstract class LocalResourceRuleBase : ResourceManagementRuleBase{
private Hashtable _problemVariables = new Hashtable(); private Method _method; private string _memberName; protected LocalResourceRuleBase(string name) : base(name) {}protected Hashtable ProblemVariables
{
get { return _problemVariables; }}
protected Method Method{
get { return _method; } set{
_method =
value;SetMemberName(_method
as Member);}
}
protected string MemberName{
get { return _memberName; } set { _memberName = value; }}
protected void SetMemberName(Member member)
{
if(member == null){
_memberName =
string.Empty;}
else{
_memberName = RuleUtilities.Format(member,
true);}
}
protected virtual void CheckForMethodCall(MethodCall call, string methodName){
MemberBinding callBinding = call.Callee
as MemberBinding; if(callBinding != null){
Method calledMethod = callBinding.BoundMember
as Method; if(calledMethod == null){
return;}
Local variable = callBinding.TargetObject
as Local; if(variable != null){
string name = variable.Name.Name; if(_problemVariables.Contains(name) == true){
if(calledMethod.Name.Name == methodName){
_problemVariables.Remove(name);
}
}
}
}
}
protected static bool IsVariableReturnValue(Method method, Variable variable){
string returnTypeName = variable.Type.Name.Name; if(method.ReturnType.Name != null && method.ReturnType.Name.Name != returnTypeName){
return false;}
LocalList locals = method.Instructions[0].Value
as LocalList; if(locals != null){
int index = -1;ArrayList variableIndexes =
new ArrayList(locals.Length); bool searchForStLoc = false, stLocFound = false; for(int x = 0; x < locals.Length; x++){
if(RuleUtilities.IsCompilerGenerated(locals[x]) == false && locals[x].Type.Name.Name == returnTypeName){
variableIndexes.Add(x);
}
}
if(variableIndexes.Count == 0){
return false;}
OpCode op;
Instruction instruction;
for(int x = method.Instructions.Length - 2; x > 0; x--){
instruction = method.Instructions[x];
op = instruction.OpCode;
if( op == OpCode.Ldloc ||op == OpCode.Ldloc_0 ||
op == OpCode.Ldloc_1 ||
op == OpCode.Ldloc_2 ||
op == OpCode.Ldloc_3 ||
op == OpCode.Ldloc_S)
{
if(stLocFound == true){
switch(op){
case OpCode.Ldloc_0:{
index = 0;
break;}
case OpCode.Ldloc_1:{
index = 1;
break;}
case OpCode.Ldloc_2:{
index = 2;
break;}
case OpCode.Ldloc_3:{
index = 3;
break;}
default: // OpCode.Ldloc || OpCode.Ldloc_S{
if(instruction.Value != null){
try{
index = Convert.ToInt32(instruction.Value);
}
catch {} // do nothing}
break;}
}
if(variableIndexes.Contains(index) == true && locals[index].Name.Name == variable.Name.Name){
return true;}
}
else{
searchForStLoc =
true;}
}
else{
if(searchForStLoc == true){
if( op == OpCode.Stloc ||op == OpCode.Stloc_0 ||
op == OpCode.Stloc_1 ||
op == OpCode.Stloc_2 ||
op == OpCode.Stloc_3 ||
op == OpCode.Stloc_S)
{
stLocFound =
true;}
}
}
}
}
return false;}
protected void FormulateProblemsFromVariables(){
Resolution resolution;
Problem problem;
Variable var;
foreach(string variableName in ProblemVariables.Keys){
var = ProblemVariables[variableName]
as Variable;resolution = GetResolution(
new string[3] { _memberName, variableName, var.Type.FullName });problem =
new Problem(resolution);Problems.Add(problem);
}
}
protected bool CheckForLocals(){
if(Method == null || Method.Body.HasLocals == false || Method.Instructions == null || Method.Instructions.Length == 0){
return false;}
if((Method.Instructions[0].Value as LocalList) != null){
return true;}
return false;}
}
}

False hits on large project, other questions
DoubleM
Keith,
A couple of things:
Also in regards to the IDataReader confusion; interfaces can implement other interfaces without requiring to re-implement that interface's methods. For example the following declaration perfectly valid:
interface IMyInterface : IDisposable
{
}
Steve Falzon
Hi,
Well i can try to anwser the IDataReader question for you...
IDataReader it self does implement a Public Dispose, you are propably talking about a type that implements IDataReader
the public Dispose of IDataReader is mostlikely explicitly implemented, that means you have to cast the type that implements IDataReader to IDataReader, the public dispose will then show up.
IDataReader idr = (IDataReader)SomeTypeThatImplementsIDataReader;
idr.dispose;
Greetings.