cppcheck/man/writing-rules.docbook

211 lines
6.3 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<article>
<articleinfo>
<title>Writing Cppcheck rules</title>
<author>
<firstname>Daniel</firstname>
<surname>Marjamäki</surname>
<affiliation>
<orgname>Cppcheck</orgname>
</affiliation>
</author>
<pubdate>2010</pubdate>
</articleinfo>
<section>
<title>Introduction</title>
<para>This is a short guide for developers who want to write Cppcheck
rules.</para>
<para>There are two ways to write rules.</para>
<variablelist>
<varlistentry>
<term>Regular expressions</term>
<listitem>
<para>Simple rules can be created by using regular expressions. No
compilation is required.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>C++</term>
<listitem>
<para>Advanced rules must be created with C++. These rules must be
compiled and linked statically with Cppcheck.</para>
</listitem>
</varlistentry>
</variablelist>
<para>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.</para>
</section>
<section>
<title>Data representation of the source code</title>
<para>The data used by the rules are not the raw source code.
<literal>Cppcheck</literal> will read the source code and process it
before the rules are used.</para>
<para>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.</para>
<para>Between each token in the code there is always a space. For instance
the raw code "1+f()" is processed into "1 + f ( )".</para>
<para>The code is simplified in many ways. For example:</para>
<itemizedlist>
<listitem>
<para>The templates are instantiated</para>
</listitem>
<listitem>
<para>The typedefs are handled</para>
</listitem>
<listitem>
<para>There is no "else if". These are converted into "else {
if.."</para>
</listitem>
<listitem>
<para>The bodies of "if", "else", "while", "do" and "for" are always
enclosed in "{" and "}"</para>
</listitem>
<listitem>
<para>A declaration of multiple variables is split up into multiple
variable declarations. "int a,b;" =&gt; "int a; int b;"</para>
</listitem>
<listitem>
<para>There is no sizeof</para>
</listitem>
<listitem>
<para>NULL is replaced with 0</para>
</listitem>
<listitem>
<para>Static value flow analysis is made. Known values are inserted
into the code.</para>
</listitem>
<listitem>
<para>.. and many more</para>
</listitem>
</itemizedlist>
<para>The simplifications are made in the <literal>Cppcheck</literal>
<literal>Tokenizer</literal>. For more information see:
<uri>http://cppcheck.sourceforge.net/doxyoutput/classTokenizer.html</uri></para>
</section>
<section>
<title>Regular expressions</title>
<para>Simple rules can be defined through regular expressions. Cppcheck
uses PCRE to handle regular expressions.</para>
<section>
<title>Creating regular expression</title>
<para>Let's create a regular expression that checks for:</para>
<programlisting>if (p)
free(p);</programlisting>
<para>The condition is often redundant, on most implementations it is
valid to free a NULL pointer.</para>
<para>The regular expression must match the simplified code. Create a
source file that has the bad code:</para>
<programlisting>void f() {
if (p)
free(p);
}</programlisting>
<para>To see the simplified code use <literal>cppcheck --debug
dealloc.cpp</literal>.</para>
<programlisting>##file dealloc.cpp
1: void f ( ) {
2: if ( p ) {
3: free ( p ) ; }
4: }</programlisting>
<para>In the <literal>--debug</literal> output there are line feeds and
line numbers. But the newlines and line numbers are only there to make
the output easier to read. The real simplified code is written on a
single line:</para>
<programlisting>void f ( ) { if ( p ) { free ( p ) ; } }</programlisting>
<para>Now we can use <literal>cppcheck --rule</literal> to develop a
regular expression.</para>
<programlisting>$ cppcheck --rule="if \( p \) { free \( p \) ; }" dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:2]: (style) found 'if ( p ) { free ( p ) ; }'</programlisting>
<para>Feel free to improve the pattern. Above, the pointer name must be
"p" to get a match.</para>
</section>
<section>
<title>Create rule file</title>
<para>A rule consist of:</para>
<itemizedlist>
<listitem>
<para>a pattern to search for.</para>
</listitem>
<listitem>
<para>an error message that is reported when pattern is found - this
is optional, if none is given a default message is written.</para>
</listitem>
</itemizedlist>
<para>Here is a simple example:</para>
<programlisting>&lt;?xml version="1.0"?&gt;
&lt;rule version="1"&gt;
&lt;pattern&gt;if \( p \) { free \( p \) ; }&lt;/pattern&gt;
&lt;message&gt;
&lt;id&gt;redundantCondition&lt;/id&gt;
&lt;severity&gt;style&lt;/severity&gt;
&lt;summary&gt;Redundant condition. It is valid to free a NULL pointer.&lt;/summary&gt;
&lt;/message&gt;
&lt;/rule&gt;</programlisting>
<para>The <literal>message</literal>, <literal>id</literal>,
<literal>severity</literal> and <literal>summary</literal> elements are
optional. But highly recommended.</para>
<para>If you save that xml data in <literal>dealloc.rule</literal> you
can test this rule:</para>
<programlisting>$ cppcheck --rule-file=dealloc.rule dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.</programlisting>
</section>
</section>
</article>