Be The Cppcheck Ruler

Internet streaming data abstraction

As a static analysis tool, Cppcheck has a set of default rules. However, when you are further into your development and realize that you need to create your own rules to catch specific bugs, then this is the right blog for you. There are two ways to set your own Cppcheck rules: create an XML-based rule file, or create a C++-based rule file. There is also a GUI tool that can be used to generate configuration and help you get started. This blog will focus however on the more advanced topic of creating your own rules.

Creating an XML-based Rule File

Creating an XML-based rule file is more simple and straightforward but the bad news is the handling of this “simple” rule is deprecated and will be removed in Cppcheck version 2.5. In order to create a Cppcheck rule, one needs to have knowledge of regular expressions. A regular expression is used to find the line of code we want to check with. By having the regular expression in hand, we already can apply a rule with the following command option:

# Command usage
cppcheck --rule=<REGULAR_EXPRESSION> <FILE>
# An example of catching code redundancy
cppcheck --rule="if \\( p \\) { free \\( p \\) ; }" dealloc.cpp

The following is the output of the command above:

Checking dealloc.cpp...
[dealloc.cpp:2]: (style) found 'if ( p ) { free ( p ) ; }'

We can customize the responded message by creating an XML-based rule file with the regular expression. The following is a simple XML example that catches redundant code. Click here for more examples.

<?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>

The pattern tag is where we insert the regular expression and the elements within the message tag are the messages that will be printed out on the terminal when the regular expression matches a line of code in a file. Once we have created the XML based rule file, we can use the following command option to locate the file, and apply the rule:

# Command usage
cppcheck --rule-file=<RULE_FILE> <FILE>
# An example of catching code redundancy
cppcheck --rule-file=dealloc.rule dealloc.cpp

The following is the output of the command above:

Checking dealloc.cpp...
[dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.

Creating a C++-Based Rule File

A C++-based rule file will give us the flexibility to create a more sophisticated rule. In order to apply a C++-based rule file, we will have to recompile the Cppcheck tool with the rule file.

Build Cppcheck From Source

To build Cppcheck from the source, we first have to download the Cppcheck source code with the following command:

git clone <https://github.com/danmar/cppcheck/>

Rules are organized into Check classes which can be found in the “lib” directory. For example, there is a class with the name CheckStl that contains various stl rules. The CheckOther class can always be used if no other class suits you. When you have added your rule, you must recompile Cppcheck before you can test it. One of the ways to compile Cppcheck is by using g++.

To get started, search through the Cppcheck repo to find the source file and header file that have the same name as the class you want to add the custom rule to. For example, the CheckOther class and its methods are located in checkother.cpp and checkother.h (under the “lib” directory).Let’s say you want to add a new rule to the CheckOther class. First, you have to create two new methods, one for detecting the pattern and one for error messaging. The following c++ code is an example of a rule to detect division by zero:

// Detect division by zero
void CheckOther::divisionByZero()
{
    // Loop through all tokens
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next())
    {
        // check if there is a division by zero
        if (Token::Match(tok, "/ 0"))
        {
            // report error
            divisionByZeroError(tok);
        }
    }
}
// Report error
void CheckOther::divisionByZeroError(const Token *tok)
{
    reportError(tok, // location
                Severity::error, // severity
                "divisionByZero", // id
                "Division by zero"); // message
}

The Token::Match function matches tokens against expressions. There are a few rules about the Token::Match expressions:

  1. The tokens are either completely matched or not matched at all. For example, the token “abc” is not matched by “ab”.
  2. Spaces are used as separators.
  3. With normal regular expressions, there are special meanings for + * ? ( ) but these are just normal characters in Token::Match patterns.

Once the rule methods are added to checkother.cpp, the checkother.h file then needs to get updated as well. The following are the things you need to add to the header file:

Declare the pattern detecting method as a public method in the CheckOther class and call it in the runChecks method: 
class CPPCHECKLIB CheckOther : public Check { 
public: 
void runChecks(...) override { 
// Checks ... 
// Call the pattern detecting method
checkOther.divisionByZero(); 
}


// Declare the pattern detecting method  
void divisionByZero(); 
}

Declare the error messaging method as a private method in the CheckOther class and call it in the getErrorMessages method:

class CPPCHECKLIB CheckOther : public Check { 
private: // Error messages..
void divisionByZeroError(const Token *tok); 
// Declare the error messaging method ... 
void getErrorMessages(...) const override { 
// error ... 
// Call the error messaging method
c.divisionByZeroError(nullptr); 
} 
}

Once the code is in place, we are ready to compile Cppcheck with the following command:

g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Iexternals/picojson -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp

Creating custom Cppcheck rules is a powerful way to catch critical bugs and enforce coding standards unique to your firmware projects. But managing custom tooling, build environments, and ensuring consistent processes across your team can quickly become a challenge. That’s where EmbedOps comes in. EmbedOps streamlines your firmware development workflow by containerizing your toolchains, automating builds and tests, and giving you a centralized dashboard to track everything — from static analysis runs to CI/CD results.

If you’re ready to simplify your firmware DevOps process and build a more reliable, scalable development environment, sign up for EmbedOps today. Have a project or development challenge you’d like to discuss? Book a call with our team and let’s explore how we can help you modernize your firmware workflow and deliver better products, faster.

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

CI Secrets for On-Time Embedded Systems

Beat the Clock: CI Secrets for On-Time Embedded Systems

Getting Continuous Integration (CI) right in embedded systems development is challenging. It’s a game-changer for meeting deadlines, but only if you nail the tech and the teamwork. Building Your CI Foundation Start by picking the right tools. Don’t just go for the shiniest features—think about how they’ll mesh with your current setup and whether they can handle embedded quirks. Your CI system needs to play nicely with your development environment and target hardware. Key elements to consider: Separate build and

Read More »
Create a Mature Test CLI Tool with Python Click

Create a Mature Test CLI Tool with Python Click

Introduction In firmware development, effective testing is crucial. Developers often need reliable ways to interact with their hardware. This tutorial will guide you through creating a Python-based command-line interface (CLI) tool that leverage the CANable USB-to-CAN adapter for testing devices on a CAN bus. By building this tool, you’ll streamline your testing process and provide a valuable resource for your entire team. (Check out this link for more information about the CANable.) What is Python Click? We’ll be using Python

Read More »
Addressing the CRA SBOM Mandate: How to Achieve Compliance

Addressing the CRA SBOM Mandate: How to Achieve Compliance

Written by: Joe Schneider The EU Cyber Resilience Act (CRA) has fundamentally changed embedded software development by mandating proof of a product’s secure operation and ability to remain safe throughout its lifecycle. Despite being an EU regulation, the CRA is applicable to any globally manufactured product targeting EU markets and has led many other jurisdictions to introduce similar cybersecurity initiatives. These include the USA’s Executive Order 14028, Japan’s METI’s Cybersecurity Management Guidelines and the UK’s Product Security and Telecommunications Infrastructure

Read More »