Advertisement
Guest User

FxCop Rule: Dispose local variables

a guest
May 13th, 2012
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.20 KB | None | 0 0
  1. //FxCopMeta.xml
  2. <?xml version="1.0" encoding="utf-8" ?>
  3. <Rules FriendlyName="alexander-koepke.de custom rules">
  4.   <Rule TypeName="LocalDisposeRule" Category="Performance" CheckId="42">
  5.     <Name>Dispose local variables</Name>
  6.     <Description>Local variables which implement IDisposable should Disposed before losing scope.</Description>
  7.     <Url></Url>
  8.     <Resolution Name="LocalDispose">Call Dispose method for local variable(s): {0} or use using statement</Resolution>
  9.     <MessageLevel Certainty="95">CriticalError</MessageLevel>
  10.     <Email></Email>
  11.     <FixCategories>Breaking</FixCategories>
  12.     <Owner></Owner>
  13.   </Rule>
  14. </Rules>
  15.  
  16. // LocalDisposeRole.cs
  17. class LocalDisposeRule : BaseIntrospectionRule
  18. {        
  19.     ///////////////////////////////////////////////////////////////
  20.     #region Fields
  21.     private IDictionary<int, string> m_DisposeableLocals;
  22.     private Instruction m_LastInstruction;
  23.     private int m_RetOffset;
  24.     private int m_WellKnownJumpOffset;
  25.     #endregion
  26.     ////////////////////////////////////////////////////////////////
  27.     #region C'tor        
  28.     public LocalDisposeRule()
  29.     : base ("LocalDisposeRule",
  30.         "FxCopCustomRule.FxCopMeta",
  31.         typeof(LocalDisposeRule).Assembly)
  32.     {
  33.         m_DisposeableLocals = new Dictionary<int, string>(10);
  34.         m_RetOffset = -1;
  35.         m_WellKnownJumpOffset = -1;
  36.     }
  37.     #endregion
  38.     ////////////////////////////////////////////////////////////////
  39.     #region Overrides and overriable
  40.     public override TargetVisibilities TargetVisibility
  41.     { get { return TargetVisibilities.All;  } }        
  42.  
  43.     public override ProblemCollection Check(Member member)
  44.     {      
  45.         m_DisposeableLocals.Clear();
  46.         var method = member as Method;
  47.         if (method != null)
  48.         {
  49.             if (method.Instructions.Count > 0)
  50.             {                                        
  51.                 Instruction instr;
  52.                 for (int i = 0; i < method.Instructions.Count; i++)
  53.                 {
  54.                     instr = method.Instructions[i];                        
  55.                     // counting all local variables
  56.                     // which implement IDisposable                        
  57.                     SearchDisposableLocals(instr);
  58.                     // counting all Dispose calls
  59.                     SearchCallOfDisposeMethod(instr);                        
  60.                     SaveLastLoadLocalInstruction(instr);                        
  61.                 }
  62.  
  63.                 SearchReturnOfDisposeableLocal(method.Instructions);
  64.  
  65.                 //more locals which can disposed as dispose calls,
  66.                 //pointed to some errors
  67.                 if (m_DisposeableLocals.Count > 0)
  68.                 {
  69.                     string parameterList = PrettifyLocalVariableNames();                        
  70.                     Problems.Add(
  71.                         new Problem(GetNamedResolution(
  72.                             "LocalDispose", parameterList)));
  73.                 }                    
  74.             }                
  75.         }
  76.         return Problems;
  77.     }      
  78.                                
  79.     #endregion
  80.     ///////////////////////////////////////////////////////////////////
  81.     #region Private Implementation
  82.     private void SearchDisposableLocals(Instruction instr)
  83.     {
  84.         if (FxCopRuleExtensions.IsLocalInstruction(instr.OpCode))
  85.         {
  86.             //find local variables which are disposable
  87.             var locals = instr.Value as LocalCollection;
  88.             if (locals != null)
  89.             {
  90.                 Local local;
  91.                 for (int i = 0; i < locals.Count; i++)
  92.                 {
  93.                     local = locals[i];
  94.                     if (ShouldDisposed(local)
  95.                         && !RuleUtilities.IsCompilerGenerated(local))
  96.                     {
  97.                         m_DisposeableLocals.Add(local.UniqueKey,
  98.                         local.Name.Name);
  99.                     }
  100.                 }
  101.             }
  102.         }
  103.     }
  104.        
  105.     private bool ShouldDisposed(Local local)
  106.     {
  107.         return ImplementIDisposable(local.Type);            
  108.     }
  109.  
  110.     private void SearchCallOfDisposeMethod(Instruction instr)
  111.     {
  112.         if (DisposeCalled(instr))
  113.         {
  114.             LastLocalWasDisposed();
  115.         }
  116.     }
  117.  
  118.     private bool DisposeCalled(Instruction instr)
  119.     {
  120.         bool result = false;
  121.  
  122.         if (FxCopRuleExtensions.IsCallInstruction(instr.OpCode))
  123.         {                
  124.             var method = instr.Value as Method;
  125.             if (method != null)
  126.             {
  127.                 if (method.FullName.EndsWith("Dispose",
  128.                     StringComparison.OrdinalIgnoreCase))
  129.                 {
  130.                     if (ImplementIDisposable(method.DeclaringType))
  131.                     {
  132.                         result = true;
  133.                     }
  134.                 }
  135.             }                
  136.         }
  137.         return result;
  138.     }
  139.  
  140.     private bool ImplementIDisposable(TypeNode startNode)
  141.     {
  142.         bool result = startNode.FullName.Equals("System.IDisposable",
  143.             StringComparison.OrdinalIgnoreCase);
  144.         TypeNode current = startNode;            
  145.         do
  146.         {
  147.             InterfaceCollection interfaces = current.Interfaces;
  148.             InterfaceNode itf;
  149.             for (int i = 0; !result && i < interfaces.Count; i++)
  150.             {
  151.                 itf = interfaces[i];
  152.                 if (itf.FullName.Equals("System.IDisposable",
  153.                     StringComparison.OrdinalIgnoreCase))
  154.                 {
  155.                     result = true;
  156.                 }
  157.             }
  158.             current = current.BaseType;
  159.         } while (!result && current != null);
  160.  
  161.         return result;
  162.     }
  163.  
  164.     private void SaveLastLoadLocalInstruction(Instruction instr)
  165.     {
  166.         // save last instruction only whether
  167.         // local variable is pushed to method stack
  168.         if (FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode))
  169.         {
  170.             m_LastInstruction = instr;
  171.         }
  172.     }        
  173.        
  174.     private void SearchReturnOfDisposeableLocal(
  175.         InstructionCollection instructions)
  176.     {
  177.         var pattern = new Stack<Func<Instruction, bool>>();
  178.                                    
  179.         // ldloc or ldarg loading variable to stack which should be returned
  180.         pattern.Push(instr =>
  181.         {
  182.             bool result =
  183.                 FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode);
  184.             if (result)
  185.             {
  186.                 var local = instr.Value as Local;
  187.                 if (local != null)
  188.                 {
  189.                     if (m_DisposeableLocals.ContainsKey(local.UniqueKey))
  190.                     {
  191.                         m_DisposeableLocals.Remove(local.UniqueKey);
  192.                     }
  193.                 }    
  194.             }
  195.             return result;
  196.         });
  197.         // stloc.0 save last element of stack as local variable
  198.         pattern.Push(instr =>
  199.             FxCopRuleExtensions.IsStoreLocalInstruction(instr.OpCode));
  200.         // br.s IL_005 jump to next statement
  201.         pattern.Push(instr =>
  202.         {
  203.             bool result = FxCopRuleExtensions.IsBrSInstruction(instr.OpCode);
  204.             if (result)
  205.             {
  206.                 m_WellKnownJumpOffset = instr.Offset;
  207.             }
  208.             return result;
  209.         });
  210.         // ldloc.0 local variable was loaded to return
  211.         pattern.Push(instr =>
  212.             FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode));
  213.         // ret
  214.         pattern.Push(instr =>
  215.         {
  216.             bool result = FxCopRuleExtensions.IsRetInstruction(instr.OpCode);
  217.             if (result)
  218.             {
  219.                 m_RetOffset = instr.Offset;
  220.                 //   LastLocalWasDisposed();
  221.             }
  222.             return result;
  223.         });            
  224.         var revInstructions = instructions.Reverse();
  225.         //running structure
  226.         InstructionPatternMatching(revInstructions, pattern);                      
  227.         //searching all jumps to return
  228.         IEnumerable<int> jumpsToReturn = SearchJumpsToReturn(instructions);
  229.         SearchAdditionalReturnsOfDisposableLocals(instructions, jumpsToReturn);
  230.     }
  231.  
  232.     private IEnumerable<int> SearchJumpsToReturn(
  233.         InstructionCollection instructions)
  234.     {
  235.         var result = new List<int>();
  236.         foreach (var instr in instructions)
  237.         {
  238.             if (FxCopRuleExtensions.IsJumpInstruction(instr.OpCode))
  239.             {
  240.                 int targetOffset = (int)instr.Value;
  241.                 if (targetOffset == (m_RetOffset -1))
  242.                 {
  243.                     if (instr.Offset != m_WellKnownJumpOffset)
  244.                     {
  245.                         result.Add(instr.Offset);
  246.                     }
  247.                 }
  248.             }
  249.         }
  250.         return result;
  251.     }
  252.  
  253.     private void SearchAdditionalReturnsOfDisposableLocals(
  254.         InstructionCollection instructions,
  255.         IEnumerable<int> jumpsToReturnOffsets)
  256.     {
  257.         var pattern = new Stack<Func<Instruction, bool>>();
  258.            
  259.         foreach (int offset in jumpsToReturnOffsets)
  260.         {
  261.             pattern.Push(instr =>
  262.             {
  263.                 bool result =
  264.                     FxCopRuleExtensions.IsLoadLocalInstruction(instr.OpCode);
  265.                 if (result)
  266.                 {
  267.                     m_LastInstruction = instr;
  268.                     LastLocalWasDisposed();
  269.                 }
  270.                 return result;
  271.             });
  272.             pattern.Push(instr =>
  273.                 FxCopRuleExtensions.IsStoreLocalInstruction(instr.OpCode));            
  274.             var query = (from instr in instructions
  275.                         where instr.Offset < offset && instr.Offset >= (offset - 2)
  276.                         select instr).Reverse();
  277.             InstructionPatternMatching(query, pattern);
  278.         }
  279.     }
  280.  
  281.     private void InstructionPatternMatching (
  282.         IEnumerable<Instruction> instructions,
  283.         Stack<Func<Instruction, bool>> pattern)
  284.     {                      
  285.         foreach (Instruction instr in instructions)
  286.         {
  287.             if (pattern.Count == 0) break;
  288.             var function = pattern.Pop();
  289.             if (!function.Invoke(instr))
  290.             {
  291.                 break;
  292.             }
  293.         }            
  294.     }
  295.  
  296.     private void LastLocalWasDisposed()
  297.     {
  298.         var local = m_LastInstruction == null
  299.             ? null
  300.             : (m_LastInstruction.Value as Local);
  301.         if (local != null)
  302.         {
  303.             if (m_DisposeableLocals.ContainsKey(local.UniqueKey))
  304.             {
  305.                 m_DisposeableLocals.Remove(local.UniqueKey);
  306.             }
  307.         }
  308.     }
  309.  
  310.     private string PrettifyLocalVariableNames()
  311.     {
  312.         bool initial = true;
  313.         var sb = new StringBuilder(30);
  314.         foreach (var name in m_DisposeableLocals.Values)
  315.         {
  316.             if (initial)
  317.             {
  318.                 initial = false;
  319.             }
  320.             else
  321.             {
  322.                 sb.Append(", ");
  323.             }
  324.             sb.Append(name);
  325.         }
  326.  
  327.         return sb.ToString();
  328.     }
  329.     #endregion
  330. }
  331.  
  332. // FxCopRuleExtension.cs
  333. public static class FxCopRuleExtensions
  334. {        
  335.     public static bool IsJumpInstruction(OpCode opCode)
  336.     {
  337.         bool result = false;
  338.         switch (opCode)
  339.         {
  340.             case OpCode.Br:
  341.             case OpCode.Br_S:
  342.             case OpCode.Brfalse:
  343.             case OpCode.Brfalse_S:
  344.             case OpCode.Brtrue:
  345.             case OpCode.Brtrue_S:
  346.                 result = true;
  347.                 break;
  348.         }
  349.         return result;
  350.     }
  351.  
  352.     public static bool IsCallInstruction(OpCode opCode)
  353.     {
  354.         bool result = false;
  355.         switch (opCode)
  356.         {
  357.             case OpCode.Call:
  358.             case OpCode.Calli:
  359.             case OpCode.Callvirt:
  360.                 result = true;
  361.                 break;
  362.         }
  363.         return result;
  364.     }
  365.  
  366.     public static bool IsLocalInstruction(OpCode opCode)
  367.     {
  368.         return opCode == OpCode._Locals;
  369.     }
  370.  
  371.  
  372.     public static bool IsLoadLocalInstruction(OpCode opCode)
  373.     {
  374.         bool result = false;
  375.         switch (opCode)
  376.         {
  377.             case OpCode.Ldloc:
  378.             case OpCode.Ldloc_0:
  379.             case OpCode.Ldloc_1:
  380.             case OpCode.Ldloc_2:
  381.             case OpCode.Ldloc_3:
  382.             case OpCode.Ldloc_S:
  383.                 result = true;
  384.                 break;
  385.         }
  386.         return result;
  387.     }
  388.  
  389.     public static bool IsBrSInstruction(OpCode opCode)
  390.     {
  391.         return opCode == OpCode.Br_S;
  392.     }
  393.  
  394.     public static bool IsStoreLocalInstruction(OpCode opCode)
  395.     {
  396.         bool result = false;
  397.         switch (opCode)
  398.         {
  399.             case OpCode.Stloc:
  400.             case OpCode.Stloc_0:
  401.             case OpCode.Stloc_1:
  402.             case OpCode.Stloc_2:
  403.             case OpCode.Stloc_3:
  404.             case OpCode.Stloc_S:
  405.                 result = true;
  406.                 break;
  407.         }
  408.         return result;
  409.     }
  410.  
  411.     public static bool IsRetInstruction(OpCode opCode)
  412.     {
  413.         return opCode == OpCode.Ret;
  414.     }
  415. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement