Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //FxCopMeta.xml
- <?xml version="1.0" encoding="utf-8" ?>
- <Rules FriendlyName="alexander-koepke.de custom rules">
- <Rule TypeName="LocalDisposeRule" Category="Performance" CheckId="42">
- <Name>Dispose local variables</Name>
- <Description>Local variables which implement IDisposable should Disposed before losing scope.</Description>
- <Url></Url>
- <Resolution Name="LocalDispose">Call Dispose method for local variable(s): {0} or use using statement</Resolution>
- <MessageLevel Certainty="95">CriticalError</MessageLevel>
- <Email></Email>
- <FixCategories>Breaking</FixCategories>
- <Owner></Owner>
- </Rule>
- </Rules>
- // LocalDisposeRole.cs
- class LocalDisposeRule : BaseIntrospectionRule
- {
- ///////////////////////////////////////////////////////////////
- #region Fields
- private IDictionary<int, string> m_DisposeableLocals;
- private Instruction m_LastInstruction;
- private int m_RetOffset;
- private int m_WellKnownJumpOffset;
- #endregion
- ////////////////////////////////////////////////////////////////
- #region C'tor
- public LocalDisposeRule()
- : base ("LocalDisposeRule",
- "FxCopCustomRule.FxCopMeta",
- typeof(LocalDisposeRule).Assembly)
- {
- m_DisposeableLocals = new Dictionary<int, string>(10);
- m_RetOffset = -1;
- m_WellKnownJumpOffset = -1;
- }
- #endregion
- ////////////////////////////////////////////////////////////////
- #region Overrides and overriable
- public override TargetVisibilities TargetVisibility
- { get { return TargetVisibilities.All; } }
- public override ProblemCollection Check(Member member)
- {
- m_DisposeableLocals.Clear();
- var method = member as Method;
- if (method != null)
- {
- if (method.Instructions.Count > 0)
- {
- Instruction instr;
- for (int i = 0; i < method.Instructions.Count; i++)
- {
- instr = method.Instructions[i];
- // counting all local variables
- // which implement IDisposable
- SearchDisposableLocals(instr);
- // counting all Dispose calls
- SearchCallOfDisposeMethod(instr);
- SaveLastLoadLocalInstruction(instr);
- }
- SearchReturnOfDisposeableLocal(method.Instructions);
- //more locals which can disposed as dispose calls,
- //pointed to some errors
- if (m_DisposeableLocals.Count > 0)
- {
- string parameterList = PrettifyLocalVariableNames();
- Problems.Add(
- new Problem(GetNamedResolution(
- "LocalDispose", parameterList)));
- }
- }
- }
- return Problems;
- }
- #endregion
- ///////////////////////////////////////////////////////////////////
- #region Private Implementation
- private void SearchDisposableLocals(Instruction instr)
- {
- if (FxCopRuleExtensions.IsLocalInstruction(instr.OpCode))
- {
- //find local variables which are disposable
- var locals = instr.Value as LocalCollection;
- if (locals != null)
- {
- Local local;
- for (int i = 0; i < locals.Count; i++)
- {
- local = locals[i];
- if (ShouldDisposed(local)
- && !RuleUtilities.IsCompilerGenerated(local))
- {
- m_DisposeableLocals.Add(local.UniqueKey,
- local.Name.Name);
- }
- }
- }
- }
- }
- private bool ShouldDisposed(Local local)
- {
- return ImplementIDisposable(local.Type);
- }
- private void SearchCallOfDisposeMethod(Instruction instr)
- {
- if (DisposeCalled(instr))
- {
- LastLocalWasDisposed();
- }
- }
- private bool DisposeCalled(Instruction instr)
- {
- bool result = false;
- if (FxCopRuleExtensions.IsCallInstruction(instr.OpCode))
- {
- var method = instr.Value as Method;
- if (method != null)
- {
- if (method.FullName.EndsWith("Dispose",
- StringComparison.OrdinalIgnoreCase))
- {
- if (ImplementIDisposable(method.DeclaringType))
- {
- result = true;
- }
- }
- }
- }
- return result;
- }
- private bool ImplementIDisposable(TypeNode startNode)
- {
- bool result = startNode.FullName.Equals("System.IDisposable",
- StringComparison.OrdinalIgnoreCase);
- TypeNode current = startNode;
- do
- {
- InterfaceCollection interfaces = current.Interfaces;
- InterfaceNode itf;
- for (int i = 0; !result && i < interfaces.Count; i++)
- {
- itf = interfaces[i];
- if (itf.FullName.Equals("System.IDisposable",
- StringComparison.OrdinalIgnoreCase))
- {
- result = true;
- }
- }
- current = current.BaseType;
- } while (!result && current != null);
- return result;
- }
- private void SaveLastLoadLocalInstruction(Instruction instr)
- {
- // save last instruction only whether
- // local variable is pushed to method stack
- if (FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode))
- {
- m_LastInstruction = instr;
- }
- }
- private void SearchReturnOfDisposeableLocal(
- InstructionCollection instructions)
- {
- var pattern = new Stack<Func<Instruction, bool>>();
- // ldloc or ldarg loading variable to stack which should be returned
- pattern.Push(instr =>
- {
- bool result =
- FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode);
- if (result)
- {
- var local = instr.Value as Local;
- if (local != null)
- {
- if (m_DisposeableLocals.ContainsKey(local.UniqueKey))
- {
- m_DisposeableLocals.Remove(local.UniqueKey);
- }
- }
- }
- return result;
- });
- // stloc.0 save last element of stack as local variable
- pattern.Push(instr =>
- FxCopRuleExtensions.IsStoreLocalInstruction(instr.OpCode));
- // br.s IL_005 jump to next statement
- pattern.Push(instr =>
- {
- bool result = FxCopRuleExtensions.IsBrSInstruction(instr.OpCode);
- if (result)
- {
- m_WellKnownJumpOffset = instr.Offset;
- }
- return result;
- });
- // ldloc.0 local variable was loaded to return
- pattern.Push(instr =>
- FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode));
- // ret
- pattern.Push(instr =>
- {
- bool result = FxCopRuleExtensions.IsRetInstruction(instr.OpCode);
- if (result)
- {
- m_RetOffset = instr.Offset;
- // LastLocalWasDisposed();
- }
- return result;
- });
- var revInstructions = instructions.Reverse();
- //running structure
- InstructionPatternMatching(revInstructions, pattern);
- //searching all jumps to return
- IEnumerable<int> jumpsToReturn = SearchJumpsToReturn(instructions);
- SearchAdditionalReturnsOfDisposableLocals(instructions, jumpsToReturn);
- }
- private IEnumerable<int> SearchJumpsToReturn(
- InstructionCollection instructions)
- {
- var result = new List<int>();
- foreach (var instr in instructions)
- {
- if (FxCopRuleExtensions.IsJumpInstruction(instr.OpCode))
- {
- int targetOffset = (int)instr.Value;
- if (targetOffset == (m_RetOffset -1))
- {
- if (instr.Offset != m_WellKnownJumpOffset)
- {
- result.Add(instr.Offset);
- }
- }
- }
- }
- return result;
- }
- private void SearchAdditionalReturnsOfDisposableLocals(
- InstructionCollection instructions,
- IEnumerable<int> jumpsToReturnOffsets)
- {
- var pattern = new Stack<Func<Instruction, bool>>();
- foreach (int offset in jumpsToReturnOffsets)
- {
- pattern.Push(instr =>
- {
- bool result =
- FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode);
- if (result)
- {
- m_LastInstruction = instr;
- LastLocalWasDisposed();
- }
- return result;
- });
- pattern.Push(instr =>
- FxCopRuleExtensions.IsStoreLocalInstruction(instr.OpCode));
- var query = (from instr in instructions
- where instr.Offset < offset && instr.Offset >= (offset - 2)
- select instr).Reverse();
- InstructionPatternMatching(query, pattern);
- }
- }
- private void InstructionPatternMatching (
- IEnumerable<Instruction> instructions,
- Stack<Func<Instruction, bool>> pattern)
- {
- foreach (Instruction instr in instructions)
- {
- if (pattern.Count == 0) break;
- var function = pattern.Pop();
- if (!function.Invoke(instr))
- {
- break;
- }
- }
- }
- private void LastLocalWasDisposed()
- {
- var local = m_LastInstruction == null
- ? null
- : (m_LastInstruction.Value as Local);
- if (local != null)
- {
- if (m_DisposeableLocals.ContainsKey(local.UniqueKey))
- {
- m_DisposeableLocals.Remove(local.UniqueKey);
- }
- }
- }
- private string PrettifyLocalVariableNames()
- {
- bool initial = true;
- var sb = new StringBuilder(30);
- foreach (var name in m_DisposeableLocals.Values)
- {
- if (initial)
- {
- initial = false;
- }
- else
- {
- sb.Append(", ");
- }
- sb.Append(name);
- }
- return sb.ToString();
- }
- #endregion
- }
- // FxCopRuleExtension.cs
- public static class FxCopRuleExtensions
- {
- public static bool IsJumpInstruction(OpCode opCode)
- {
- bool result = false;
- switch (opCode)
- {
- case OpCode.Br:
- case OpCode.Br_S:
- case OpCode.Brfalse:
- case OpCode.Brfalse_S:
- case OpCode.Brtrue:
- case OpCode.Brtrue_S:
- result = true;
- break;
- }
- return result;
- }
- public static bool IsCallInstruction(OpCode opCode)
- {
- bool result = false;
- switch (opCode)
- {
- case OpCode.Call:
- case OpCode.Calli:
- case OpCode.Callvirt:
- result = true;
- break;
- }
- return result;
- }
- public static bool IsLocalInstruction(OpCode opCode)
- {
- return opCode == OpCode._Locals;
- }
- public static bool IsLoadLocalInstruction(OpCode opCode)
- {
- bool result = false;
- switch (opCode)
- {
- case OpCode.Ldloc:
- case OpCode.Ldloc_0:
- case OpCode.Ldloc_1:
- case OpCode.Ldloc_2:
- case OpCode.Ldloc_3:
- case OpCode.Ldloc_S:
- result = true;
- break;
- }
- return result;
- }
- public static bool IsBrSInstruction(OpCode opCode)
- {
- return opCode == OpCode.Br_S;
- }
- public static bool IsStoreLocalInstruction(OpCode opCode)
- {
- bool result = false;
- switch (opCode)
- {
- case OpCode.Stloc:
- case OpCode.Stloc_0:
- case OpCode.Stloc_1:
- case OpCode.Stloc_2:
- case OpCode.Stloc_3:
- case OpCode.Stloc_S:
- result = true;
- break;
- }
- return result;
- }
- public static bool IsRetInstruction(OpCode opCode)
- {
- return opCode == OpCode.Ret;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement