Authour:

Johnny Lee

Last update: Aug 27 12:00 PDT


LEGAL INFORMATION

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented. This document is for informational purposes only.

MICROSOFT MAKE NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS DOCUMENT.

Microsoft Corporation may have patents or pending patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property rights.

Microsoft does not make any representation or warranty regarding specifications in this document or any product or item developed based on these specifications. Microsoft disclaims all express and implied warranties, including but not limited to the implied warranties of merchantability, fitness for a particular purpose, and freedom from infringement. Without limiting the generality of the foregoing, Microsoft shall not be liable for any damages arising out of or in connection with the use of these specifications, including liability for lost profit, business interruption, or any other damages whatsoever.

Copyright © 1996-2001 Microsoft Corporation. All rights reserved.


NAME

TYPO.PL - scans C/C++ source code for possible errors


DESCRIPTION

Typo.pl is a Perl script which scans C/C++ source code for possible errors.

The script was originally written to locate various typing errors, i.e. X == Y; instead of X = Y;

The list of possible errors reported by the script has grown considerably.

If you have any questions, suggestions, or complaints, please let me know.


VERSION

2.55 (released Aug 27 2001)


LATEST CHANGES


HOW TO GET MORE COMPLETE RESULTS

You'll have to do some work and specify the behaviour of your private functions to the Perl script via one of the options in the Usage section. You should place these options in an optionfile so you don't have to enter them repeatedly.

If you have a function, AllocFoo, that allocates memory and returns the allocated memory, you would use the -cfr option, i.e. -cfr:AllocFoo.


QUICK START

  1. If you don't have Perl installed, install the latest Win32 version from ActiveState

  2. Copy typo.pl to your machine.
  3. Copy either win32.txt or ce.txt to your machine.

  4. Go to the topmost directory in your source and type the following:
     perl c:\bin\typo.pl -optionfile:c:\bin\win32.txt c >c:\temp\typo.out

  5. To view the script's output, use any text editor or the typo viewer app


USAGE

To examine all text files from the current directory down
 perl typo.pl
To examine a list of files from STDIN
 perl typo.pl -

dir /B /A:-R | perl typo.pl - will examine all writable text files in a directory



To examine all C/C++ files in the current directory down
 perl typo.pl c
To examine a single file
 perl typo.pl <path to filename>

the path to filename can be a partial or full path



To disable reporting certain cases, use the -disable option
 perl typo.pl -disable:1,2-5,9

disables reporting about cases 1, 2 to 5, and 9



To enable reporting certain cases, use the -enable option
 perl typo.pl -enable:1,2-5,9

enables reporting about cases 1, 2 to 5, and 9

N.B. last -enable/-disable wins:

-disable:1-30 -enable:1-15 results in cases 16-30 disabled, 1-15 enabled.



To ignore certain files:
 perl typo.pl -ignore:<pattern1>[,<pattern2>[,...]]

ignores filenames that match the given patterns

'*' is only valid wilcard character



To scan files that have a modification date/time later than given
 perl typo.pl -newer:<yr=1970-2038>,<mon=1-12>, <day=1-31>,<hr=0-23>,<minute=0-59>

scans files that have a mod. date/time later than given, i.e. -newer:1998,1,31,4,30 => Jan 31, 1998 04:30



To print out the time when the script starts and stops scanning
 perl typo.pl -showtime
To print out the # of comments and which functions were seen
 perl typo.pl -showstats
To print out the file that the script is currently processing
 perl typo.pl -showprogress
To enable nonbuffered output
 perl typo.pl -nonbuffered
To enable line-by-line disabling of specific cases
 perl typo.pl -notypo

Code must be annotated with the word ``NO_TYPO'' on the line to be disabled for typo reporting. To specify certain cases to be disabled, use the format NO_TYPO:XX,YY,... where XX, YY represent the specific cases that are disabled.



To check the results of additional functions
 perl typo.pl -cfr:<function1>[,<function2>[,...]]

scans results of specified functions to see if they are used before they have been checked for success



To use a text file to specify a list of options
 perl typo.pl -optionfile:<filename>

reads in all the lines of the specified file and parses each line as a possible option

Sample option file
-version:2.47
-showtime
-cfr:GlobalAlloc,LocalAlloc,HeapAlloc,malloc,VirtualAlloc
-cfr:SysAllocString,ExAllocatePool
-newer:1998,6,14,12,30

To specify directories to look for option files
 perl typo.pl -optiondir:<directory1>[,<directory2>[,...]]

specifies directories to be searched if the option file cannot be found in the current directory. This option should be specified before -optionfile. If directories are not specified with trailing backslashes, then backslashes will be appended.



To add to list that checks on function results
 perl typo.pl -checked:<function1>[,<function2>[,...]]

informs script that the specified functions will check the result from previous function calls so there's no need to report use before check typo

i.e. if you specify -checked:foobar, then

 x = malloc(16);
 foobar(x);

won't report a typo.



To add to list that doesn't deref/access function results
 perl typo.pl -noderef:<function1>[,<function2>[,...]]

informs script that the specified functions will not dereference the result from previous function calls so there's no need to report use before check typo yet.

i.e. if you specify -noderef:Output, then

 x = malloc(16);
 Output("%8.8lX\n", x);

won't report a typo yet.



To add to list that checks on function results similar to operator new
 perl typo.pl -new:<function1>[,<function2>[,...]]

informs script that the specified functions behave similar to operator new for case 34.



To check the results of functions that return handles
 perl typo.pl -fn:HANDLE:<function1>[,<function2>[,...]]

scans results of specified functions to see if they're used before they've been checked for success and if the result is compared to INVALID_HANDLE_VALUE.



To check the results of functions that return HRESULTs
 perl typo.pl -fn:HR:<function1>[,<function2>[,...]]

scans results of specified functions to see if they're used before they've been checked for success and if the result is tested via SUCCEEDED or FAILED macros.



To add to the function list that return INVALID_HANDLE_VALUE
 perl typo.pl -fn:INVALID:<function1>[,<function2>[,...]]

scans results of specified functions that behave like CreateFile (case 20)



To add to the function list that return the length of strings
 perl typo.pl -fn:LEN:<function1>[,<function2>[,...]]

scans for use of specified functions that are used for finding the lengths of null-terminated strings



To add to the function list that could overflow buffers
 perl typo.pl -fn:OVERFLOW:<function1>[,<function2>[,...]]

scans for use of specified functions that can overflow buffers passed to them



To add to the function list that behaves like realloc
 perl typo.pl -fn:REALLOC:<function1>[,<function2>[,...]]

scans for use of specified functions that behave like realloc (case 17)



To add to the function list that are registry functions
 perl typo.pl -fn:REG:<function1>[,<function2>[,...]]
        
specifies registry functions whose result need to checked with ERROR_SUCCESS


To add to the function list that are RPC functions
 perl typo.pl -fn:RPC:<function1>[,<function2>[,...]]
        
specifies RPC functions that need to be checked with RPC_* error codes


To add to the function list that are ignorable asserts
 perl typo.pl -fn:SAFEASSERT:<function1>[,<function2>[,...]]
        
specifies assert functions that are ignored for case 4
To add to the function list that can throw/raise exceptions
 perl typo.pl -fn:THROW:<function1>[,<function2>[,...]]

scans for specified functions to see if they're used in a try



To add to the function list that don't return a value
 perl typo.pl -fn:VOID:<function1>[,<function2>[,...]]

prevents reports of case 32 for functions that don't return a value.



To add to the function list that terminate a case statement
 perl typo.pl -endcase:<function1>[,<function2>[,...]]

informs script that the specified functions behave similar to break statement for case 19.



To add to list of #defines that may not be always defined
 perl typo.pl -temp_defined:<define1>[,<define2>[,...]]

informs script that the specified defines may not always be available.



To specify that #if, #ifdef, #ifndef, #elif, #else, and #endif preprocessor directives should be handled
 perl typo.pl -ifdef

There should be accompanying use of the -define option unless you want the script to never check code within #if's, #ifdef's, etc.



To specify symbols and their values for #if, #ifdef, #ifndef, and #elif preprocessor directives, use the -define option:
 perl typo.pl -define:<define1>[=<value1>]

This must be used with the -ifdef option



To print out metrics about the scanned files:
 perl typo.pl -kloc:[1 | 2]
  1. specifies functionality and display similar to Code metric app

  2. specifies functionality and display similar to Code metric app but also displays the number of functions in a file.


To specify that string constants should be extracted from the scanned files:
 perl typo.pl -extract_strings:[c | m | r | s]
c
specifies strings from C/C++ source code files should be extracted

m
specifies strings from message compiler (.MC) files should be extracted

r
specifies strings from resource (.RC, .RCV, .DLG) files should be extracted

s
specifies that the strings should be stripped, mostly removing escaped character constants, Menu accelerators, and strings that look like filenames



To print out help
 perl typo.pl -help
 perl typo.pl -h
 perl typo.pl -?

To specify that results are output in XML format
 perl typo.pl -output_xml

To specify the version of the typo.pl script required
 perl typo.pl -version:<number>

To specify functions whose results should be ignored
 perl typo.pl -ignore_result:<function>[,<function2>[,...]]
To specify that the code is run in kernel-mode and code in try-except blocks should ALWAYS check for non-NULL ptrs
 perl typo.pl -kernel_code

To specify that the script should only scan files that are in build.dat:
 perl typo.pl -use_build_dat

The algorithm that the script uses to locate the build.dat is as follows:


ERRORS

Following potential programming errors are flagged:

1.

semicolon appended to an if statement

 if (x == y);
  exit(1);
2.

use of == instead of = in assignment statements, handles +/- too.

 X == Y;
 X - NULL;
3.

assignment of a number in an if statement, probably meant a comparison

 if (x = 3)
4.

assignment within an Assert

 ASSERT(Z = 4);
5.

increment/decrement of ptr, ptr's contents not modified, may have meant to modify ptr's contents

 *ptr++;
6.

logical AND with a number

 x = y && 1;
7.

logical OR with a number

 x = y || 2;
8.

bitwise-AND/OR/XOR of number compared to another value may have undesired result due to C precedence rules since bitwise-AND/OR/XOR has lower precedence than the comparison operators.

 if (x & 1 == 0)
9.

referencing Release/AddRef instead of invoking them

 punk->Release;
10.

whitespace following a line-continuation character

 #define X stuff \<SPACE>
11.

shift or mod operator ( <<, >>, % ) followed by +,-,*,/ may have undesired result due to C precedence rules. The shift operator has lower precedence.

 x = (y << 1 + 1);

is seen by the compiler as

 x = y << (1 +1);

Some code confused the precedence of % and +/-. % is higher than +/-.

12.

very basic check for uninitialized vars in for-loops

 for (ULONG i; i; ++i)
13.

misspelling Microsoft

 Copyright 1999 Micorsoft Corporation
14.

swapping the last two args of memset may set 0 bytes

 memset(buf, nCount, 0);
15.

swapping the last two args of FillMemory may set 0 bytes

 FillMemory(pAction, 0, sizeof(Action));
16.

LocalReAlloc/GlobalReAlloc may fail without MOVEABLE flag

 pv1 = LocalReAlloc(pv, cbNew, 0);
17.

assigning result of realloc to same var that's realloced may result in leaked memory if realloc fails since NULL will overwrite original value

 p = (char *)realloc(p, 100);
18.

ReAlloc flags in wrong place or using ReAlloc flags for a different realloc API,

 pv1 = LocalReAlloc(pv, cbNew, GMEM_MOVEABLE);

i.e. passing GMEM_MOVEABLE to LocalReAlloc, it's not an error to the compiler, but I'd say you were playing with fire.

19.

case statement without a break/return/goto/exit

 case 2:
  Foo();
 case 3:
  Bar();
  break;

If you add a comment with the text fall through or no break before the next case statement, then the script will not emit a warning.

20.

comparing CreateFile return value vs NULL for failure problem is that CreateFile returns INVALID_HANDLE_VALUE on failure

 hFile = CreateFile(...);
 if (hFile == NULL)
21.

casting a 32-bit number (may not be 64-bit safe)

 if (p == (HANDLE)0xffffffff)
22.

casting a 7-digit hex number with high-bit set of first digit, may have meant to add an extra digit?

 if (p == 0xfff0000)
23.

comparing functions that return handles to INVALID_HANDLE_VALUE for failure, problem is that these functions return NULL on failure

 h = CreateFileMapping(...);
 if (h == INVALID_HANDLE_VALUE)
24.

comparing OpenFile/_lopen/_lclose/_lcreat return value to anything other than HFILE_ERROR, which is the documented return value when a failure occurs.

 fh = OpenFile(...);
 if (fh < 0)
25.

comparing alloca result to NULL is wrong since alloca fails by raising an exception, not returning NULL.

 pv = alloca(10);
 if (pv == NULL)
 {
     goto Done;
 }
26.

alloca fails by raising an exception, so check to see if alloca is within a try {}

 BOOL foo(void)
 {
     PVOID pv;
     pv = alloca(10);
     return bar(pv);
 }

N.B. You will only get one stack overflow exception.

The stack is left in an unstable state (the guard page at the end of the stack has been converted to a normal stack page but there is no room for a new guard page below it).

The next stack fault will walk off the bottom of the stack and the process will be terminated immediately, no debugger, no nothing.



27.

check to see if the result from CreateWindow or CreateThread or some other specified function is checked at the first if-stmt.

 hwnd = CreateWindow(...);
 ShowWindow(hwnd, SW_SHOW);

N.B. I'd like to make this more flexible, as long as the return value is checked before the value is used.

28.

check for multiple inequality comparisons of the same var separated by ``||'',

i.e. if ((x != 0) || (x != 2))

in this case, if x == 0, the second comparison will succeed and the code will enter the if-stmt body.

Programmer probably meant && instead of ||.

29.

similar to 28, check for cases of the form:

 if ((x == 0) && (x == 1))
30.

if a function result is used before it has been checked for success

 pv = LocalAlloc(...);
 strcpy(pv, sz);
31.

check for use of lstrcpy/strcpy

 strcpy(d, s);
32.

check to see if function result was stored somewhere

33.

trying to take the logical inverse of a number

 x = !3;
34.

if the result from the new operator is used before it has been checked for success

 p = new CLASS;
 p->DoIt();
35.

function that raises exception on error is not in a try.

 void foo() 
 {
  InitializeCriticalSection(&crit);
  .
  .
  .
 }

From July 2000 MSDN, EnterCriticalSection:

In low memory situations, EnterCriticalSection can raise a STATUS_INVALID_HANDLE exception. To avoid problems, use structured exception handling, or call the InitializeCriticalSectionAndSpinCount function to preallocate the event used by EnterCriticalSection instead of calling the InitializeCriticalSection function, which forces EnterCriticalSection to allocate the event.

From July 2000 MSDN, InitializeCriticalSectionAndSpinCount:

Windows 2000: If the high-order bit is set, the function preallocates the event used by the EnterCriticalSection function. Do not set this bit if you are creating a large number of critical section objects, because it will consume a significant amount of nonpaged pool.

36.

check for misspelled defined symbols. User must do most of the investigative work. The script will note all the symbols used in #ifdef,#ifndef,#if,#elif statements and print them out at the end.

37.

check for bitwise-XORing one number with another

 x = 10 ^ 2;
38.

wrong flags used with MapViewOfFile

 MapViewOfFile(hfileMap, PAGE_READWRITE, 0, 0, 0x1000);

should be

 MapViewOfFile(hfileMap, FILE_MAP_WRITE, 0, 0, 0x1000);
39.

wrong flags used with CreateFile

 CreateFile(szFile, FILE_MAP_WRITE, ...);

should be

 CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, ...);
40.

duplicate flags passed to CreateFile

 CreateFile(szFile, GENERIC_WRITE | GENERIC_WRITE, ...);
41.

complain about returning unchecked function results

42.

using HRESULT function result w/no check

43.

double semicolon

 x = 0;;
44.

calculating memory needed incorrectly by using strlen(X+1) instead of strlen(X)+1

 c = strlen(sz+1);
 p = malloc(c);
 if (p) strcpy(p, sz);
45.

assigning TRUE to boolVal field of VARIANT should use VARIANT_TRUE (= -1)

 var.boolVarl = TRUE;
46.

empty statement after while/for loop

 while (pFoo);
47.

use of (!x & Y), probably meant (!(x & Y)) C/C++ precedence rules have ! before &

 if (!x & 3)
48.

testing a #define for a value instead of existence

49.

test a char for 0 instead of '\0', i.e. user meant to test for null terminator instead of number 0

50.

use of a disallowed function

 TerminateThread();
51.

use of a disallowed string

52.

filling object with zeros

 memset(this, 0, sizeof(*this));
53.

check for (X | Y) == 0 or (X | Y) != 0, probably meant (X & Y) == 0

54.

check for non-Multimon-safe constructs: pt.x = LOWORD(lParam);, should use pt.x = GET_X_LPARAM(lParam); instead

55.

check for use of sizeof(this) instead of sizeof(*this). this is a pointer to the current object. *this is the current object.

56.

check for use of sizeof(X) instead of sizeof(X)/sizeof(X[0]) when calling an API that wants number of characters in a buffer, instead of number of bytes in a buffer

57.

check that results of registry APIs are compared with ERROR_SUCCESS or other common registry error codes. -fn:REG: option was added to allow user to add to list of registry APIs to check.

58.

check for use of a NULL DACL passed as the third parameter to SetSecurityDescriptorDacl

From January 2001 MSDN, SetSecurityDescriptorDacl:

If this parameter is NULL, a NULL discretionary ACL is assigned to the security descriptor, allowing all access to the object.

59.

check for non-NULL-ptr checks before calling memory deallocator function that can handle NULL ptrs. These memory deallocators include free, delete, delete [], SysFreeString, and LocalFree.

Since code performance usually follows the 80/20 rule (80% of the execution time is spent in 20% of the code), case 59 may help reduce the size of the 80% of code that's not CPU-limited, i.e.

        if (sz) LocalFree(sz);

The if check is extraneous since LocalFree can handle NULL ptrs. If this was CPU-intensive code, you might not want to do a function call just for a NULL check.

But in most cases, the ptr is non-NULL and/or the code is not CPU-intensive.

Looking at some optimized code from VC, removing the extraneous check saves at least four bytes if the ptr is in a register aleady!

60.

check for extremely long expressions [default=2048 chars]. This is usually a sign of code that can be redesigned, either for maintainability or efficiency or both. Or the script's parsing algorithm got confused...

61.

check for using HWND_BROADCAST with SendMessage. SendMessage is a blocking call so if one of the receiving windows is not responding, then your thread will block until the window responds. You can work around this by using SendMessageTimeout, PostMessage, or only sending the windows message to your known windows, if that's what you wanted to do.

62.

check for using NonPagedPoolMustSucceed or NonPagedPoolCacheAlignedMustS flags with the ExAllocatePool class of allocation APIs. If the allocation fails, then the machine will crash.


MICROSOFT VISUAL STUDIO INTEGRATION

  1. Select Tools.Customize menu item from MSDEV

  2. Click the Add button.

  3. Type ``Typo'' in the edit control and hit OK. Ignore the warning about invalid path.

  4. In the Command edit control, enter perl.exe. Specify the full pathname to perl.exe if it isn't on your PATH.

  5. In the Arguments edit control, enter the path to typo.pl.

  6. In the initial directory edit control, select one of the directory entries from the dropdown menu, i.e. File Directory, Current Directory, Target Directory, or Workspace Directory, or enter a directory of your own.

  7. Make sure the ``Redirect to Output Window'' checkbox is set.

  8. Hit Close.

You can now run TYPO.PL from Visual Studio and double-click on the captured output to have it locate the potential error for you.


HISTORY

Jan 06 1996
Created.


Jan 26 1996
Repeatedly replace parenthesized expressions (PE) with '_' for errors 1 and 2.

Tweak == case to remove PEs before checking for unbalanced parentheses or lines that begin with &&,||,==, or = * ==.

Tweak == case to check previous line for unterminated for-loop or line ending with && or ||.



Jan 29 1996
Adjust error 1 and 3 regexp so script can find errors of the form else if (XXXX); or else if (XXX = 1)

Print line number of error too

Released as Version 1.0.



Feb 08 1996
Suggestion: Look for assignment within an Assert

Modify output so one can use typo.pl from Microsoft Visual Studio to locate errors

Released as Version 1.0.1.



Apr 04 1996
Suggestion:

Look for pattern of the form *psz++;

Suggestion:

For assignment statements that use ==, ignore previous lines which look like part of ? : constructs or an assignment.

Check for && # or || #. Authour probably meant to use bitwise versions.

Remove spaces from a copy of the current line to make patterns simpler.

Allow user to specify list of files by piping list of files to perl, i.e. dir /s /b | perl typo.pl -.



Jul 09 1996
Check for & <symbol> ==

C/C++ precedence rules have == higher than &.

So code like if (x & 0x03 == 0x02) is treated as if the programmer wrote: if (x & (0x03 == 0x02))



Oct 09 1996
Add in descriptions of new errors (5-8) that are flagged


Nov 30 1996
Made removal of quoted strings in if cases always happen so I was also able to move code out of special cases and always apply transformations

Check for if that begins on word-boundary rather than doing weak check for else if in body of of if checks.

In error 8, & XXX ==, check for bitwise-XOR and bitwise-OR as well as bitwise-AND.

In error 9, -Release>, add comment describing the typo and check for AddRef case too.

Add new cases from similar perl tool. These cases include, line-continuation char followed by whitespace and then EOL, and an operator precedence typo, A << B + C. The + operator has higher precedence than the shift operator.

Instead of using ``\w'' to match a word, use the character set [A-Za-z0-9_*\->].

Remove strings in the general case.



Jan 24 1997
Check for == <symbol &> C/C++ precedence rules have == higher than & So code like: if (0 == x & 0x03) is treated as if the programmer wrote: if ((0 == x) & 0x03) This is almost the same as the Jul 09 1996 check but the position of operators have been swapped.

Added tweak to case 10 suggested.

Don't complain if \<SP> follows a C++ comment



Feb 07 1998
suggestion from Bryan Krische close the TEXTFILE handle when we're done. This is important because Perl is not spec'ed to reset its line counter when opening a new file in an existing file handle... So running the script as is the current release of Perl (the official one, not the activeware port) comes up with weird line numbers. Doing the close resets the line count and then all is fine.


Mar 09 1998
don't look for typos within multi-line C comments


Mar 22 1998
Suggestion: quick check for uninitialized vars in for-loops

discovered bug in handling of cmd-line switches

add error msg display for invalid cmd-line switches



Apr 04 1998
mistyped ``Microsoft'' => ``Micorsoft''

add check to see if case 8 & XXX == is really taking address of var and casting to ptr of another type and then derefing that ptr - this should reduce # of false positives



Apr 20 1998
Bad usage of memset code had swapped the value and # bytes to set i.e. memset(ptr, 0, bytes) would be memset(ptr, bytes, 0)

do similar check for FillMemory



Apr 23 1998
Suggestion: skip over preprocessor directives


Apr 28 1998
regexp cleanup and use Perl 5 features, i.e. non-greedy parsing for C-comments

Suggestion: look for LocalReAlloc/GlobalReAllocs which don't pass in MOVEABLE flag

Suggestion: look for places where result from realloc is assigned to the same var that was realloced If the realloc fails, the memory has leaked, i.e.

 ptr = realloc(ptr, cb + 1024);

Apr 29 1998
check for ReAlloc flags passed in the wrong order

incorporate checking for break in case stmt code

add ability to scan one file passed on the cmdline



May 21 1998
User noted that the script reports x << y++ as a typo. Make the pattern check that there's no second ``+''.

Users noted problems with the x << y + case if the expression was doing text output via C++ iostreams. Check for strings/character constants on the line. If we find them, then we probably have some C++ iostream usage.



Jun 01 1998
User noted a problem with a quoted double-quote character confusing the code that looked for if (X);. Need to map any quoted double-quote characters to something safer, i.e. ``_''.

Users noted problems with how people dealt with CreateFile's return value. Many expect a failed CreateFile to return NULL but it returns INVALID_HANDLE_VALUE.

Added code to check for CreateFile failure and similar APIs, i.e. FindFirstFile, etc.



Jun 03 1998
User noted a couple of problems in cases 8 & 11 when dealing with C++ code.

Looking at the results from a test run, I saw many cases of (HANDLE)0xFFFFFFFF which may not do the expected thing on 64-bit NT.

Also added check for casting a 7-digit hex number which may need an extra digit



Jun 09 1998
Update the script usage text


Jun 10 1998
Add tweak to case 2 to not report lines of the form: X == Y ? 10 : 20;


Jun 24 1998
User says that the his group defines INVALID_FH to INVALID_HANDLE_VALUE. So add that to the list of acceptable alternatives.

User noted that the script was complaining about CreatFileTypePage. Add that to the list of exceptions.

User noted that case 5 will report delete *i++; as a typo.



Jun 29 1998
Check for CreatFileMapping's return value compared with INVALID_HANDLE_VALUE on failure. One should compare with NULL.

Check OpenFile/_lcreat/_lopen/_lclose's return value with anything other than HFILE_ERROR.



Jul 02 1998
User noted that the explanation for case 19, case stmt without break, was confusing since the offending line printed out was usually the following case stmt, not the case stmt which didn't have the break.


Jul 05 1998
User suggested looking at alloca. alloca fails by raising an exception, so checking the alloca result for NULL doesn't make sense.

User suggested checking to see if code checked the return values from CreateWindow and CreateThread

Cleaned up case 19 (Case w/o break).



Jul 07 1998
User suggested adding an option to disable certain cases. I did the least disruptive thing, specifying ``-disable:#x,#y-#z'' will not print anything for case x and cases y...z.


Jul 08 1998
Suggestion: add support for ignoring certain files. Also noted a problem with the CreatFileMapping case in a private drop.

Report any functions return values that haven't been checked by the end of the enclosing function or by the time another function call is made.

User noted that script was getting CreateFileEntry mixed up with CreateFile. Weed out.



Jul 11 1998
Added ``-showtime'' option to show start/end of scan

Added check for too many options

User suggested checking for (x != 0) || (x != YYY). The || should be &&.



Jul 13 1998
Make ignore files option case-insensitive.


Jul 14 1998
Added ``-newer'' option to only scan files that have been modified after the given date

User checkin mail talked about code not checking the result from RegOpenKey. Add that.



Jul 16 1998
Problem with multi-line C comment code if beginning of C comment appeared within a string, lots of code would be removed as part of comment.


Jul 19 1998
For Invalid handle functions, if the variable that holds the function result doesn't appear in an if statement, then we won't clear the info about the function.

Add check for function result being used before it has been checked.



Jul 21 1998
Ran with ``perl -w'' and fixed a couple of uninitialized variables reports. Whoops.

Add ``-cfr'' option to allow user to specify other functions whose results should be checked.



Jul 23 1998
Move case 27 check so that it's by itself instead in the middle of ``elsif'' tests.

For case 30, assigning to the function result again doesn't count as using the function result before checking it.



Jul 24 1998
Switched to using /ox instead of /x in regexps that interpolate variables. Adding 'o' speeds up the script.


Jul 28 1998
Convert spaces between words to '#' so we can handle some cases better, i.e. 23-27, and 30.

Check for use of lstrcpy/strcpy due to public Outlook Express/Outlook 98 buffer overflow problems. Also NetMeeting 2.1 had a publicisized buffer overflow prob.

Fix ``C++ comment inside string'' problem

Fix problem with above fix if starting double quote is inside a character constant, i.e. '\``'

Need to check if keyword or function collection gets too long. If it does we reset ourselves.

Need to handle case where we get confused by #ifdef's and doesn't see the end of an if stmt If we see an if or else keyword while we're collecting for an if stmt, then we will throw away the previous collection and start with the new if stmt.

Extend limit when we think keyword or function collection is too long. Some people like to code if stmts that are > 2048 chars. Glad I don't have to debug that code.



Aug 05 1998
Suggestion. Check for other side effects inside Asserts, i.e. ++, +=, -=


Aug 08 1998
Added ``-optionfile'' support

Changed enable array init



Aug 13 1998
Fixed problem with case 32 and OpenFile with OF_DELETE flag getting reported

Added expceptions to counting curly braces

Should close option files that are opened

Finally got around to handle if (X = NEW CLASS)



Aug 18 1998
User suggested that the result from the new operator should be checked for success before it's used.

Check for logical inverse of a number, i.e. !4

Found a problem with the try/except code where it wouldn't clear the try info if it found that the parens count was 0 - it also needs to check that it has seen the opening and closing curly parentheses.

Use 'my' to make variables ``local'' lexically.

Fix cases 28 and 29 where ``$arg1[xx]'' was interpreted as an array reference by using ``/x'' regex option and adding appropriate spacing



Aug 23 1998
User noted that case 20 would match with anything that started with CreateFile. added CreateFileA and CreateFileW to pattern. Changed check to disallow multiple alphanumeric characters after CreateFile string.


Sep 03 1998
Added support for checking for functions that return handles (HDC, HBRUSH, etc.) via the ``-fn:HANDLE:'' option. Behaviour is similar to ``-cfr'' option.

Moved CreateFileMapping checks into this category.



Sep 14 1998
Various optimizations including:

- Use hashes instead of regexps w/ alternatives

- Use 'join' instead of '.' string concatentation

- Use 'unpack' instead of 'substr'.

- For empty lines, do only what's necessary and skip to the next line.

- Remove some script that doesn't apply anymore.

- User suggestion: Add support for code that does the checking and turn off any following reports about using var before checking for success. ``-check'' option added.

User suggestion: Add support for adding to list of functions that could overflow a buffer. ``-fn:OVERFLOW:'' option added.

User suggestion: Add support for detecting functions that should be in a try/except. ``-fn:THROW:'' option added.

Add ``-new:'' option to specify functions that are synonymous or similar to ``operator new''.

Add ``-version'' option to specify minimum script version required.



Sep 27 1998
User noted some missing overflow functions: wcscpy, wcscat, etc.

Add case 36, check for misspelled symbols used in #if, #elif, #ifdef, #ifndef. Script just collects all the symbols used, user must determine if there's something wrong.



Oct 04 1998
Minor cleanup

Add ``-fn:VOID:'' option for functions that don't return a value and that case 32 should ignore.



Nov 30 1998
Handle case 1 better, i.e. if (XXX); else

Added -notypo to support NO_TYPO comments which temporarily disable cases on a line-by-line basis

Added -fn:INVALID to support adding functions that return INVALID_HANDLE_VALUE/-1 values

Added ability to reenable cases using -enable option.

Added -noderef option for specifying functions that don't deref values

Added -nonbuffered option for nonbuffered output

Added -showstats option and keep track of # of comments and functions seen.

Added cases 38, 39, amd 40

Made some vars local to the file-processing while loop

Cleaned up some regexp code in case 2.

Keep track of # of comments seen and fns invoked

Determined start of a function with greater accuracy.

Reduce false positives for case 9.

Determined function invocation with greater accuracy, character after the function name should be a ``(''.

Added realloc functions to those function results that we track

Needed to add a couple of aliases to try: __try and TRY

Don't track new function result if it's in an if stmt.

Move Case 28 and 29 to their own functions

Corrected support for comments in option files

Based on Raymond Chen bugfix, extend case 2 to handle +/- operators.

Change file reading algorithm to reduce time that file is opened to minimum

Add ``default:'' to case 19 fall-thru code.

Relax restrictions for case 3.

Handle code that tries to do portable exception handling via (TRY / CATCH).

Check that all args from option files were processed



Dec 14 1998
Documented -endcase option


Feb 25 1999
Fix disabling/enabling case 27 warnings

Reduce false positives for assigning allocations to pointer on same line as pointer declaration

Support adding/using/testing against list of constants

Added -showprogress option

Added -fn:<HR|LEN|SAFEASSERT> options

Added -constant option

Added -temp_defined option for case 48

Support empty lines and end of line comments in option files

Print number of chars scanned at summary

Added cases 41-49

Fix a spelling error in comment

Correct enabling/disabling of cases 38-40

Check for % operator in case 11

Get sorted list of files from DIR command



Mar 24 1999
Added disallowed string option and case 51

Added disallow/allow functions

Use parentheses with chomp for consistency

Keep track of stats for Code metric app functionality

Batch up lines until we have a semicolon, then perform checks for typos

Fix script so you can disable case 44

Added -help, -h, and -? options to print help without innocuous warning



May 07 1999
Added cases 50, 51, 52.

Centralized removal of string and character constants

Support for extracting strings from code, resources, and message compiler files (.mc)

Added more Code metric app-type functionality - shows number of functions in code

Handle lines that have continuations chars at the end

Able to process preprocessor directives

Fix some cases that were broken due to code movement

Cleaned up/centralized function handling with its own hash object.

Moved scanning for several cases to their own function


Jun 16 1999
Cleanup


Jul 31 1999
Convert comments to pod-style

optimize scan for case 51 by disabling case 51 if disallowed_strings is empty

move cases 10, 13, and 51 to before the check for being in the middle of a multi-line comment

consolidate handling comments (both new C++-type and old C-type comments)

use NAME instead of FUNCTION in $function_call hash

don't include keyword when passing on text before keyword for scanning

call clear_statement when finished scanning file to clear out info

use hash for processing ``-fn:'' option

weed out ``throw'' for case 2

do not include strings with colon, ':', for case 2 to prevent including case labels

add more sample code to descriptions of typos


Aug 02 1999
Move system command to get list of files into its own variable so it can be changed in one spot instead of requiring a global search and replace.

Add -optiondir option to specify directories to search for option files so you don't have to specify absolute paths.

Reduce case 2 false positives from bitfield declarations

Catch as part of case 3:

 if (x |= Y)

Aug 07 1999
Keep count of number of files scanned and print out total in -showtime output


Aug 09 1999
Add FILES scanned count


Aug 15 1999
Clean up for public release


Sep 16 1999
case 53 added


Apr 04 2000
updated copyrights to 2000

fixed -string:DISALLOW checking

fixed disabling new'd objects via NO_TYPO


Apr 20 2000
added case 54

don't report if we find cast in case 47

For case 22, modified check for any hex number >= 6 digits and != 8 digits long which may indicate a mistyped 32-bit number.


Jul 08 2000
tweaked case 22 to warn for numbers whose length are near multiples of 8

tweak case 53 to not warn if the bitwise-OR'd expression was in a function call

for case 35, added EnterCriticalSection, InitializeCriticalSectionAndSpinCount, LeaveCriticalSection, etc. as functions that can raise an exception

added warning if no option files are specified

added -log_functions option to output current function that is scanned [external contribution]

clarified case 11 for modulus operator, %, and +/-.

Dec 04 2000
case 55 added

case 56 added

added -fn:BUFFERCHARS: option to support case 56

skip asm statements and blocks

Dec 10 2000
updated location of Perl binaries


Feb 02 2001
added -ignore_result option


Feb 24 2001
added -use_build_dat option to only scan files that are specified in build.dat


Jun 21 2001
tweaked case 53 to not report (X & (Y | Z)) == 0) as a possible error

added case 57 (supported by the -fn:REG: option) which flags Registry functions that don't check the function result with ERROR_SUCCESS.

added case 58 which checks that a NULL DACL is not passed to SetSecurityDescriptorDacl

added case 59 which checks that code doesn't check for non-NULL ptr before calling a memory-deallocator function that handles NULL ptrs. Size matters!

added case 60 which emits a warning if the expression becomes too long [default=2048 chars].

Jun 24 2001
added case 61 to check for using HWND_BROADCAST with SendMessage

reworked code to output typos to console from a centralized point and most of the typo descriptions are stored in one place.

display number of typos found in output for -showtime option

remove bugs found by running typo.pl with -w switch

added -output_xml option to output results in XML format

added links in docs for each typo


Aug 27 2001
fixed problem when neither -output_xml or -showtime options were specified and no typos were found

fixed problem with case 27 when variables were assigned inside an try-catch

added case 62 to catch using NonPagedPoolMustSucceed or NonPagedPoolCacheAlignedMustS flags with with the ExAllocatePool class of allocation APIs.


RELATED INFORMATION

Perl
Win32 version of Perl
AST Toolkit
Gimpel's PC-lint
LCLint
Panorama
STATIC
ProLint

MSWin32

Development\Debugging

Typo.pl is a Perl script which scans C/C++ source code for possible errors.

The script was originally written to locate various typing errors, i.e. X == Y; instead of X = Y;

The list of possible errors reported by the script has grown considerably.