Recently I made a very large update to our code base. Our code base lacked a standard way of guarding entry and exit points into the various components. Having said guards is useful for error handling, tracing, reducing redundancy, etc … The edit standardized our entry points by adding start/end macros to our entry point functions. In addition to other house keeping, the macros also created an HRESULT variable named “hr”. Example below.

    #define MY_ENTRY_GUARD() HRESULT hr
    #define MY_ENTRY_EXIT() return hr

Ran suites, everything passed, checked in. Then I got an email from another dev who spent some time tracking down a bug related to this check-in (sorry Calvin). He discovered one scenario my fix did not take into account.

SomeMethod
{
  MY_ENTRY_GUARD();
  if ( somecondition ) {
    HRESULT hr = E_FAIL; // shadows the first hr
  }
  MY_ENTRY_EXIT(); // returns unmodified hr
}

The double declaration of the variable “hr” is not an error or even a warning in C++. Instead the inner “hr” shadows the outer and hence the rest of the method doesn’t update the “hr” which is actually returned. So now I had to find every place in this change where a nested hr was declared. Did I mention this edit was huge’ Going through by hand would not only be time consuming, it would also be very error prone.

At first I considered parsing out the C++ and doing basic brace matching to look for shadowing “hr” variables. I ruled that out due to the amount of time I would need to invest in the script to take into account comments, string literals, etc … Really I didn’t need brace matching, I really just needed to know when I entered and left a method. Almost all C++ methods have their opening and closing braces on the first column. Writing a script to detect this is trivial.

Script took about 5 minutes to write and 10 to run in the code base. Saved me countless hours of error prone reviews. Thank you PowerShell.

Find-DoubleHr.ps1:

param ( $argFileName = $(throw "Need a file name") )

function Do-Work() {  
   $i = 0  
   foreach ( $line in (gc $argFileName) )   {  
       new-tuple "Text",$line,"LineNumber",$i  
       $i++  
   }  
}

function Do-Parse() {  
   begin {  
       $inMethod = $false  
       $seenHresult = 0  
       $seenMacro = 0  
   }  
   process {  
       $tuple = $_  
       if ( $inMethod ) {  
        switch -regex ($tuple.Text) {  
           "^}" {  
               $inMethod = $false  
               if ( ($seenHresult -ne 0 )-and ($seenMacro -ne 0) ) {  
                "Found a double {0},{1}" -f $seenHResult,$seenMacro  
               }  
               $seenHresult = 0  
               $seenMacro = 0  
               break  
           }  
           ".*MY_ENTRY_GUARD.*" {  
               write-debug ("Macro: {0} " -f $tuple.Text)  
               $seenMacro = $tuple.LineNumber  
               break  
           }  
           "HRESULT.*\Whr\W" {  
               write-debug ("HResult: {0}" -f $tuple.Text)  
               $seenHresult = $tuple.LineNumber  
               break  
           }  
        }  
       }  
       elseif ( $tuple.Text -match "^{" ) {  
        $inMethod = $true  
       }  
   }  
}

"Processing $argFileName"  
Do-Work | Do-Parse

Share Post

Google+

comments powered by Disqus