Writing Cppcheck rules
Daniel
Marjamäki
Cppcheck
2010
Introduction
This is a short guide for developers who want to write Cppcheck
rules.
There are two ways to write rules.
Regular expressions
Simple rules can be created by using regular expressions. No
compilation is required.
C++
Advanced rules must be created with C++. These rules must be
compiled and linked statically with Cppcheck.
It is a good first step to use regular expressions. It is easier.
You'll get results quicker. Therefore this guide will focus on regular
expressions.
Data representation of the source code
The data used by the rules are not the raw source code.
Cppcheck will read the source code and process it
before the rules are used.
Cppcheck is designed to find bugs and dangerous code. Stylistic
information such as indentation, comments, etc are filtered out at an
early state. You don't need to worry about such stylistic information when
you write rules.
Between each token in the code there is always a space. For instance
the raw code "1+f()" is processed into "1 + f ( )".
The code is simplified in many ways. For example:
The templates are instantiated
The typedefs are handled
There is no "else if". These are converted into "else {
if.."
The bodies of "if", "else", "while", "do" and "for" are always
enclosed in "{" and "}"
A declaration of multiple variables is split up into multiple
variable declarations. "int a,b;" => "int a; int b;"
There is no sizeof
NULL is replaced with 0
Static value flow analysis is made. Known values are inserted
into the code.
.. and many more
The simplifications are made in the Cppcheck
Tokenizer. For more information see:
http://cppcheck.sourceforge.net/doxyoutput/classTokenizer.html
Regular expressions
Simple rules can be defined through regular expressions.
Cppcheck uses the PCRE library to handle regular
expressions. PCRE stands for "Perl Compatible Regular
Expressions". The homepage for PCRE is
http://www.pcre.org.
Creating the regular expression
Let's create a regular expression that checks for:
if (p)
free(p);
For such code the condition is often redundant, on most
implementations it is valid to free a NULL pointer.
The regular expression must be written for the simplified code. To
see what the simplified code looks like you can create a source file
with some code:
void f() {
if (p)
free(p);
}
Save that code as dealloc.cpp and use
cppcheck --rule=".+" dealloc.cpp:
$ ./cppcheck --rule=".+" dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:1]: (style) found ' void f ( ) { if ( p ) { free ( p ) ; } }'
From that output we can see that the simplified code is:
void f ( ) { if ( p ) { free ( p ) ; } }
Now that we know how the simplified code looks for a simple test
case, we can create a regular expression:
$ cppcheck --rule="if \( p \) { free \( p \) ; }" dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:2]: (style) found 'if ( p ) { free ( p ) ; }'
Create rule file
A rule consist of:
a pattern to search for
an error message that is reported when pattern is found
Here is a simple example:
<?xml version="1.0"?>
<rule version="1">
<pattern>if \( p \) { free \( p \) ; }</pattern>
<message>
<id>redundantCondition</id>
<severity>style</severity>
<summary>Redundant condition. It is valid to free a NULL pointer.</summary>
</message>
</rule>
If you save that xml data in dealloc.rule you
can test this rule:
$ cppcheck --rule-file=dealloc.rule dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.