Library: document not-null and not-uninit argument configuration
This commit is contained in:
parent
24dfc052e2
commit
2a884446be
|
@ -91,14 +91,18 @@ bool Library::load(const char path[])
|
|||
_noreturn[name] = (strcmp(functionnode->GetText(), "true") == 0);
|
||||
else if (strcmp(functionnode->Name(),"arg")==0 && functionnode->Attribute("nr") != NULL) {
|
||||
const int nr = atoi(functionnode->Attribute("nr"));
|
||||
|
||||
const char *nullpointer = functionnode->Attribute("nullpointer");
|
||||
const char *uninitdata = functionnode->Attribute("uninitdata");
|
||||
const char *uninitderefdata = functionnode->Attribute("uninitderefdata");
|
||||
|
||||
functionArgument[name][nr].nullpointer = (nullpointer != NULL);
|
||||
functionArgument[name][nr].uninitdata = (uninitdata != NULL);
|
||||
functionArgument[name][nr].uninitderefdata = (uninitderefdata != NULL);
|
||||
bool notnull = false;
|
||||
bool notuninit = false;
|
||||
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
|
||||
if (strcmp(argnode->Name(), "not-null") == 0)
|
||||
notnull = true;
|
||||
else if (strcmp(argnode->Name(), "not-uninit") == 0)
|
||||
notuninit = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
functionArgument[name][nr].notnull = notnull;
|
||||
functionArgument[name][nr].notuninit = notuninit;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -72,23 +72,22 @@ public:
|
|||
}
|
||||
|
||||
struct Argument {
|
||||
bool nullpointer;
|
||||
bool uninitdata;
|
||||
bool uninitderefdata;
|
||||
bool notnull;
|
||||
bool notuninit;
|
||||
};
|
||||
|
||||
// function name, argument nr => argument data
|
||||
std::map<std::string, std::map<int, Argument> > functionArgument;
|
||||
|
||||
bool isnullargbad(const std::string &functionName, int argnr) const {
|
||||
const Argument *arg = getarg(functionName,argnr);
|
||||
return arg && arg->nullpointer;
|
||||
}
|
||||
bool isnullargbad(const std::string &functionName, int argnr) const {
|
||||
const Argument *arg = getarg(functionName,argnr);
|
||||
return arg && arg->notnull;
|
||||
}
|
||||
|
||||
bool isuninitargbad(const std::string &functionName, int argnr) const {
|
||||
const Argument *arg = getarg(functionName,argnr);
|
||||
return arg && arg->uninitdata;
|
||||
}
|
||||
bool isuninitargbad(const std::string &functionName, int argnr) const {
|
||||
const Argument *arg = getarg(functionName,argnr);
|
||||
return arg && arg->notuninit;
|
||||
}
|
||||
|
||||
std::set<std::string> returnuninitdata;
|
||||
|
||||
|
@ -98,16 +97,16 @@ private:
|
|||
std::map<std::string, int> _dealloc; // deallocation functions
|
||||
std::map<std::string, bool> _noreturn; // is function noreturn?
|
||||
|
||||
const Argument * getarg(const std::string &functionName, int argnr) const {
|
||||
std::map<std::string, std::map<int, Argument> >::const_iterator it1;
|
||||
it1 = functionArgument.find(functionName);
|
||||
if (it1 != functionArgument.end()) {
|
||||
const std::map<int,Argument>::const_iterator it2 = it1->second.find(argnr);
|
||||
if (it2 != it1->second.end())
|
||||
return &it2->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
const Argument * getarg(const std::string &functionName, int argnr) const {
|
||||
std::map<std::string, std::map<int, Argument> >::const_iterator it1;
|
||||
it1 = functionArgument.find(functionName);
|
||||
if (it1 != functionArgument.end()) {
|
||||
const std::map<int,Argument>::const_iterator it2 = it1->second.find(argnr);
|
||||
if (it2 != it1->second.end())
|
||||
return &it2->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int getid(const std::map<std::string,int> &data, const std::string &name) const {
|
||||
const std::map<std::string,int>::const_iterator it = data.find(name);
|
||||
|
|
|
@ -706,12 +706,13 @@ Checking test.c...
|
|||
|
||||
<para><literal>Cppcheck</literal> has internal knowledge about how
|
||||
standard C/C++ functions work. There is no internal knowledge about how
|
||||
various libraries and environments work. <literal>Cppcheck</literal> can
|
||||
however be told how libraries and environments work by using configuration
|
||||
files.</para>
|
||||
all libraries and environments work, and there can't be.
|
||||
<literal>Cppcheck</literal> can be told how libraries and environments
|
||||
work by using configuration files.</para>
|
||||
|
||||
<para>The idea is that users will be able to download configuration files
|
||||
for all popular libraries and environments here:</para>
|
||||
<para>The idea is that users will be able to download library
|
||||
configuration files for all popular libraries and environments
|
||||
here:</para>
|
||||
|
||||
<para><uri>http://cppcheck.sourceforge.net/archive</uri></para>
|
||||
|
||||
|
@ -733,9 +734,10 @@ Checking test.c...
|
|||
<section>
|
||||
<title><alloc> and <dealloc></title>
|
||||
|
||||
<para>Allocation and deallocation is defined within
|
||||
<literal><memory></literal> or <literal><resource> using
|
||||
<alloc> and <dealloc>.</literal></para>
|
||||
<para>Allocation and deallocation is defined <literal>using
|
||||
<alloc> and <dealloc>. These are used inside inside
|
||||
<literal><memory></literal> or
|
||||
<literal><resource>.</literal></literal></para>
|
||||
|
||||
<para>Here is example code:</para>
|
||||
|
||||
|
@ -765,33 +767,23 @@ Checking test.c...</programlisting>
|
|||
</memory>
|
||||
</def></programlisting>
|
||||
|
||||
<para>That tells Cppcheck that <literal>alloc_something</literal>
|
||||
allocates memory and that the matching deallocation function is
|
||||
<literal>free_something</literal>.</para>
|
||||
|
||||
<para>Output from <literal>Cppcheck</literal>:</para>
|
||||
|
||||
<programlisting># cppcheck --library=something.cfg test.c
|
||||
Checking test.c...
|
||||
[test.c:10]: (error) Memory leak: p</programlisting>
|
||||
|
||||
<para>Some allocation functions initialize the allocated data. Others
|
||||
don't. Here is a short code example:</para>
|
||||
|
||||
<programlisting>void f()
|
||||
{
|
||||
char *p = alloc_something();
|
||||
char c = *p;
|
||||
free_something();
|
||||
}</programlisting>
|
||||
|
||||
<para>If alloc_something() initializes the memory that is allocated then
|
||||
that code is OK. Otherwise that code is bad. To configure that </para>
|
||||
|
||||
<para/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title><ignore> and <use></title>
|
||||
|
||||
<para>The <ignore> and <use> tells Cppcheck how functions
|
||||
uses allocated memory. Example code:</para>
|
||||
<para>The <literal><ignore></literal> and
|
||||
<literal><use></literal> tells Cppcheck how functions uses
|
||||
allocated memory. Example code:</para>
|
||||
|
||||
<programlisting>void f()
|
||||
{
|
||||
|
@ -840,7 +832,7 @@ Checking test.c...</programlisting>
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<title><alloc init="false"></title>
|
||||
<title>allocate but not initialize</title>
|
||||
|
||||
<para>Some allocation function initialize the data, others don't. Here
|
||||
is a example code:</para>
|
||||
|
@ -852,6 +844,11 @@ Checking test.c...</programlisting>
|
|||
free_something();
|
||||
}</programlisting>
|
||||
|
||||
<para>No error is reported:</para>
|
||||
|
||||
<programlisting># cppcheck --library=something.cfg test.c
|
||||
Checking test.c...</programlisting>
|
||||
|
||||
<para>Here is a configuration that tells cppcheck that alloc_something
|
||||
doesn't initialize the data:</para>
|
||||
|
||||
|
@ -865,11 +862,79 @@ Checking test.c...</programlisting>
|
|||
|
||||
<para>Now you will get this error message:</para>
|
||||
|
||||
<programlisting>daniel@dator:~/cppcheck$ ./cppcheck --library=something.cfg test.c
|
||||
<programlisting># cppcheck --library=something.cfg test.c
|
||||
Checking test.c...
|
||||
[test.c:4]: (error) Memory is allocated but not initialized: p</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>function arguments: null pointers</title>
|
||||
|
||||
<para>You can define if a function parameter can be NULL or if it must
|
||||
be non-NULL.</para>
|
||||
|
||||
<para>Example code:</para>
|
||||
|
||||
<programlisting>void do_something(char *p);
|
||||
|
||||
void f()
|
||||
{
|
||||
do_something(NULL);
|
||||
}</programlisting>
|
||||
|
||||
<para>Normally no error is reported for that code.</para>
|
||||
|
||||
<para>But if the do_something() parameter should be non-NULL you can use
|
||||
this configuration:</para>
|
||||
|
||||
<programlisting><?xml version="1.0"?>
|
||||
<def>
|
||||
<function name="do_something">
|
||||
<arg nr="1">
|
||||
<not-null/>
|
||||
</arg>
|
||||
</function>
|
||||
</def></programlisting>
|
||||
|
||||
<para>Now the output from cppcheck is:</para>
|
||||
|
||||
<programlisting># cppcheck --library=something.cfg test1.c
|
||||
Checking test1.c...
|
||||
[test1.c:5]: (error) Null pointer dereference</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Function arguments: uninitialized data</title>
|
||||
|
||||
<para>Here is example code:</para>
|
||||
|
||||
<programlisting>void do_something(char *p);
|
||||
|
||||
void f()
|
||||
{
|
||||
char str[10];
|
||||
do_something(str);
|
||||
}</programlisting>
|
||||
|
||||
<para>Normally <literal>Cppcheck</literal> doesn't report any error
|
||||
message for that. However if the parameter must be initialized there is
|
||||
a problem. Here is a configuration that says that the parameter must be
|
||||
initialized:</para>
|
||||
|
||||
<para><programlisting><?xml version="1.0"?>
|
||||
<def>
|
||||
<function name="do_something">
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
</def></programlisting>Now the cppcheck output is:</para>
|
||||
|
||||
<programlisting># cppcheck --library=something.cfg test1.c
|
||||
Checking test1.c...
|
||||
[test1.c:6]: (error) Uninitialized variable: str</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>no return</title>
|
||||
|
||||
|
|
Loading…
Reference in New Issue