False hits on large project, other questions

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 == parametersYes.Name.Name)

{

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;

}

}

}



Answer this question

False hits on large project, other questions

  • DoubleM

    Keith,

    A couple of things:

    1. You shouldn't exclude constructors as these can have disposable locals just like methods
    2. I'm not sure if you have Visual Studio Team System, however, FxCop already has a rule that does this exact checking called DisposeObjectsBeforeLosingScope
    3. RuleUtilities.IsCompilerGenerated should exclude any compiler generated locals, including any generated to temporary store the return value.

    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.


  • False hits on large project, other questions