1666 lines
40 KiB
HTML
1666 lines
40 KiB
HTML
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||
|
"http://www.w3.org/TR/html4/strict.dtd">
|
||
|
<html>
|
||
|
<head>
|
||
|
<title>List of potential checkers</title>
|
||
|
<link type="text/css" rel="stylesheet" href="content.css">
|
||
|
<link type="text/css" rel="stylesheet" href="menu.css">
|
||
|
<script type="text/javascript" src="scripts/menu.js"></script>
|
||
|
<script type="text/javascript" src="scripts/dbtree.js"></script>
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<div id="page">
|
||
|
|
||
|
<!-- menu -->
|
||
|
<!--#include virtual="menu.html.incl"-->
|
||
|
<!-- page content -->
|
||
|
<div id="content">
|
||
|
<h1>List of potential checkers</h1>
|
||
|
|
||
|
<p>This page contains a list of potential checkers to implement in the static analyzer. If you are interested in contributing to the analyzer's development, this is a good resource to help you get started. The specific names of the checkers are subject to review, and are provided here as suggestions.</p>
|
||
|
|
||
|
<!-- ========================= allocation/deallocation ======================= -->
|
||
|
<h3>allocation/deallocation</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">memory.LeakNeverReleased<br>
|
||
|
(C, C++)</span><br><br>
|
||
|
Memory may be never released, potential leak of memory
|
||
|
</td><td>
|
||
|
<pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
int f() {};
|
||
|
|
||
|
void test() {
|
||
|
int *p1 = (int*)malloc(sizeof(int)); // warn
|
||
|
int *p2 = new int; // warn
|
||
|
int x = f();
|
||
|
if (x==1)
|
||
|
return;
|
||
|
delete p2;
|
||
|
}
|
||
|
</pre></td><td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=15237">PR15237</a>
|
||
|
</td></tr>
|
||
|
|
||
|
<tr><td><span class="name">memory.MismatchedFree
|
||
|
<br>enhancement to unix.Malloc<br>(C, C++)</span><br><br>
|
||
|
Mismatched deallocation function is used
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void test() {
|
||
|
int *p1 = new int;
|
||
|
int *p2 = new int[1];
|
||
|
|
||
|
free(p1); // warn
|
||
|
free(p2); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=15238">PR15238</a>
|
||
|
</td></tr>
|
||
|
|
||
|
<tr><td><span class="name">memory.LeakPtrValChanged
|
||
|
<br>enhancement to unix.Malloc<br>(C, C++)</span><br><br>
|
||
|
Potential memory leak: a pointer to newly allocated data loses its original
|
||
|
value
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void f(const int *);
|
||
|
void g(int *);
|
||
|
|
||
|
void test() {
|
||
|
int *p1 = new int;
|
||
|
p1++; // warn
|
||
|
int *p2 = (int *)malloc(sizeof(int));
|
||
|
p2 = p1; // warn
|
||
|
int *p3 = new int;
|
||
|
f(p3);
|
||
|
p3++; // warn
|
||
|
int *p4 = new int;
|
||
|
f(p4);
|
||
|
p4++; // ok
|
||
|
}
|
||
|
</pre></td><td class="aligned">done at r174678 (C case)
|
||
|
</td></tr>
|
||
|
|
||
|
<tr><td><span class="name">memory.LeakEvalOrder<br>
|
||
|
(C, C++)</span><br><br>
|
||
|
Potential memory leak: argument evaluation order is undefined, g() may never be called
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void f1(int, int);
|
||
|
void f2(int*, int*);
|
||
|
int g(int *) { throw 1; };
|
||
|
int h();
|
||
|
|
||
|
void test() {
|
||
|
f1(g(new int), h()); // warn
|
||
|
f1(g((int *)malloc(sizeof(int))), h()); // warn
|
||
|
f2(new int, new int);
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">memory.DstBufferTooSmall
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Destination buffer too small
|
||
|
</td><td><pre>
|
||
|
#include <string.h>
|
||
|
|
||
|
void test() {
|
||
|
const char* s1 = "abc";
|
||
|
char *s2 = new char;
|
||
|
strcpy(s2, s1); // warn
|
||
|
|
||
|
int* p1 = new int[3];
|
||
|
int* p2 = new int;
|
||
|
memcpy(p2, p1, 3); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">memory.NegativeArraySize
|
||
|
<br>enhancement to experimental.security.MallocOverflow<br>(C, C++)
|
||
|
</span><br><br>
|
||
|
'n' is used to specify the buffer size may be negative
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void test() {
|
||
|
int *p;
|
||
|
int n1 = -1;
|
||
|
p = new int[n1]; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ======================= constructors/destructors ====================== -->
|
||
|
<h3>constructors/destructors</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">ctordtor.ExptInsideDtorExplicit<br>
|
||
|
(C++)</span><br><br>
|
||
|
It is dangerous to let an exception leave a destructor. Using try..catch will
|
||
|
solve the problem.
|
||
|
</td><td><pre>
|
||
|
void f();
|
||
|
|
||
|
class A {
|
||
|
A() {}
|
||
|
~A() { throw 1; } // warn
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">ctordtor.ExptInsideDtorImplicit<br>
|
||
|
(C++)</span><br><br>
|
||
|
Calls to functions inside a destructor that are known to throw exceptions is
|
||
|
dangerous. Using try..catch will solve the problem.
|
||
|
</td><td><pre>
|
||
|
void f() { throw 1; };
|
||
|
|
||
|
class A {
|
||
|
A() {}
|
||
|
~A() { f(); } // warn
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">ctordtor.PlacementSelfCopy<br>
|
||
|
(C++11)</span><br><br>
|
||
|
For a placement copy or move, it is almost certainly an error if the constructed object is also the object being copied from.
|
||
|
</td><td><pre>
|
||
|
class A {};
|
||
|
|
||
|
void test(A *dst, A *src) {
|
||
|
::new (dst) A(*dst); // warn (should be 'src')
|
||
|
}
|
||
|
</pre></td><td class="aligned"><!--rdar://problem/13688366--></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- =============================== va_list =============================== -->
|
||
|
<h3>va_list</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">valist.Uninitialized</span><br><br>
|
||
|
Calls to the <code>va_arg</code>, <code>va_copy</code>, or
|
||
|
<code>va_end</code> macro must happen after calling <code>va_start</code> and
|
||
|
before calling <code>va_end</code>.
|
||
|
</td><td><pre>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
void test(int x, ...) {
|
||
|
va_list args;
|
||
|
int y = va_arg(args, int); // warn
|
||
|
va_start(args, x);
|
||
|
va_end(args, x);
|
||
|
int z = va_arg(args, int); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=16812">PR16811</a></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">valist.Unterminated</span><br><br>
|
||
|
Every <code>va_start</code> must be matched by a <code>va_end</code>. A va_list
|
||
|
can only be ended once.
|
||
|
|
||
|
<i>This should be folded into the generalized "ownership checker" described on the <a href="open_projects.html">Open Projects</a> page.</i>
|
||
|
</td><td><pre>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
void test(int x, ...) {
|
||
|
va_list args;
|
||
|
va_start(args, x);
|
||
|
int y = x + va_arg(args, int);
|
||
|
// missing va_end
|
||
|
}
|
||
|
</pre></td><td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=16812">PR16812</a></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ============================== exceptions ============================= -->
|
||
|
<h3>exceptions</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">exceptions.ThrowSpecButNotThrow
|
||
|
<br>(C++)</span><br><br>
|
||
|
Function prototype has throw(T) specifier but the function do not throw
|
||
|
</td><td><pre>
|
||
|
void f() throw(int) { // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">exceptions.NoThrowSpecButThrows
|
||
|
<br>(C++)</span><br><br>
|
||
|
An exception is throw from a function having the throw() specifier
|
||
|
</td><td><pre>
|
||
|
void f() throw() {
|
||
|
throw(1); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">exceptions.ThrownTypeDiffersSpec
|
||
|
<br>(C++)</span><br><br>
|
||
|
The type of a thrown exception differs from those specified in the throw(T)
|
||
|
specifier
|
||
|
</td><td><pre>
|
||
|
struct S{};
|
||
|
void f() throw(int) {
|
||
|
S s;
|
||
|
throw (s); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ========================= smart pointers ============================== -->
|
||
|
<h3>smart pointers</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">smartptr.SmartPtrInit<br>
|
||
|
(C++)</span><br><br>
|
||
|
C++03: auto_ptr should store a pointer to an object obtained via new as allocated
|
||
|
memory will be cleaned using delete<br>
|
||
|
C++11: one should use unique_ptr<T[]> to keep a pointer to memory
|
||
|
allocated by new[]<br>
|
||
|
C++11: to keep a pointer to memory allocated by new[] in a shared_ptr one
|
||
|
should use a custom deleter that calls delete[]
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
#include <memory>
|
||
|
|
||
|
void test() {
|
||
|
std::auto_ptr<int> p1(new int); // Ok
|
||
|
std::auto_ptr<int> p2(new int[3]); // warn
|
||
|
std::auto_ptr<int>
|
||
|
p3((int *)malloc(sizeof(int))); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ============================== dead code ============================== -->
|
||
|
<h3>dead code</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">deadcode.UnmodifiedVariable
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
A variable is never modified but was not declared const and is not a reference.
|
||
|
<br><br>
|
||
|
<i>(opt-in checker)</i>
|
||
|
</td><td><pre>
|
||
|
extern int computeDelta();
|
||
|
|
||
|
int foo(bool cond) {
|
||
|
int i = 0;
|
||
|
if (cond) {
|
||
|
const int delta = computeDelta();
|
||
|
// Forgot to modify 'i'.
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
</pre></td><td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=16890">PR16890</a></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ========================= undefined behavior ========================== -->
|
||
|
<h3>undefined behavior</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ExitInDtor
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: std::exit is called to end the program during the
|
||
|
destruction of an object with static storage duration
|
||
|
</td><td><pre>
|
||
|
#include <cstdlib>
|
||
|
|
||
|
class A {
|
||
|
public:
|
||
|
~A() {
|
||
|
std::exit(1); // warn
|
||
|
}
|
||
|
};
|
||
|
|
||
|
A a;
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.LocalStaticDestroyed
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: function containing a definition of static local object is
|
||
|
called during the destruction of an object with static storage duration so that
|
||
|
flow of control passes through the definition of the previously destroyed
|
||
|
static local object
|
||
|
</td><td><pre>
|
||
|
void f();
|
||
|
|
||
|
class A {
|
||
|
public:
|
||
|
~A() {
|
||
|
f(); // warn
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class B {};
|
||
|
|
||
|
A a;
|
||
|
|
||
|
void f() {
|
||
|
static B b; // <-
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.UseAfterRelease
|
||
|
<br>enhancement to unix.Malloc<br>(C, C++)</span><br><br>
|
||
|
Pointer to deleted object is referenced (The effect of using an invalid pointer
|
||
|
value is undefined)
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void test() {
|
||
|
int *p = new int;
|
||
|
delete p;
|
||
|
int i = *p; // warn
|
||
|
}
|
||
|
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ZeroAllocDereference
|
||
|
<br>enhancement to unix.Malloc<br>(C, C++)</span><br><br>
|
||
|
The effect of dereferencing a pointer returned as a request for zero size is
|
||
|
undefined
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
int *p = new int[0];
|
||
|
int i = p[0]; // warn
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.DeadReferenced
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the following usage of the pointer to the object whose
|
||
|
lifetime has ended can result in undefined behavior
|
||
|
</td><td><pre>
|
||
|
// C++03
|
||
|
#include <new>
|
||
|
|
||
|
class A {
|
||
|
public:
|
||
|
int i;
|
||
|
void f() {};
|
||
|
};
|
||
|
|
||
|
class B : public A {
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
B *b = new B;
|
||
|
new(b) A;
|
||
|
b->i; // warn
|
||
|
b->f(); // warn
|
||
|
static_cast<A*>(b); // warn
|
||
|
dynamic_cast<A*>(b); // warn
|
||
|
delete b; // warn
|
||
|
}
|
||
|
|
||
|
// C++11
|
||
|
#include <new>
|
||
|
|
||
|
class A {
|
||
|
public:
|
||
|
int i;
|
||
|
void f() {};
|
||
|
};
|
||
|
|
||
|
class B : public A {
|
||
|
public:
|
||
|
~B() {};
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
A *a = new A;
|
||
|
new(a) B;
|
||
|
a->i; // warn
|
||
|
a->f(); // warn
|
||
|
B *b = new B;
|
||
|
new(b) A;
|
||
|
b->i; // warn
|
||
|
b->f(); // warn
|
||
|
static_cast<A*>(b); // warn
|
||
|
dynamic_cast<A*>(b); // warn
|
||
|
delete b; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ObjLocChanges
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the program must ensure that an object occupies the same
|
||
|
storage location when the implicit or explicit destructor call takes place
|
||
|
</td><td><pre>
|
||
|
#include <new>
|
||
|
|
||
|
class T { };
|
||
|
struct B {
|
||
|
~B();
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
B *b1 = new B;
|
||
|
B b2;
|
||
|
new (b1) T;
|
||
|
new (&b2) T;
|
||
|
delete b1; // warn
|
||
|
} // warn
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ExprEvalOrderUndef
|
||
|
<br>(C, C++03)</span><br><br>
|
||
|
Undefined behavior: a scalar object shall have its stored value modified at
|
||
|
most once by the evaluation of an expression
|
||
|
</td><td><pre>
|
||
|
void test () {
|
||
|
int i = 0;
|
||
|
int v[1] = {0};
|
||
|
i = v[i++]; // warn
|
||
|
i = ++i + 1; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.StaticInitReentered
|
||
|
<br>(C)</span><br><br>
|
||
|
Undefined behavior: static declaration is re-entered while the object is being
|
||
|
initialized
|
||
|
</td><td><pre>
|
||
|
int test(int i) {
|
||
|
static int s = test(2*i); // warn
|
||
|
return i+1;
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ConstModified
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Undefined behavior: const object is being modified
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
class X {
|
||
|
public :
|
||
|
mutable int i;
|
||
|
int j;
|
||
|
};
|
||
|
class Y {
|
||
|
public :
|
||
|
X x;
|
||
|
Y();
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
const int *ciq =
|
||
|
(int *)malloc(sizeof(int));
|
||
|
int *iq = const_cast<int *>(ciq);
|
||
|
*iq = 1; // warn
|
||
|
|
||
|
const Y y;
|
||
|
Y* p = const_cast<Y*>(&y);
|
||
|
p->x.i = 1; // ok
|
||
|
p->x.j = 1; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.DeadDestructed
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the destructor is invoked for an object whose lifetime
|
||
|
has ended
|
||
|
</td><td><pre>
|
||
|
class A {
|
||
|
public:
|
||
|
void f() {};
|
||
|
A() {};
|
||
|
~A() {};
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
A a;
|
||
|
a.~A();
|
||
|
} // warn
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.MethodCallBeforeBaseInit
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: calls member function but base not yet initialized
|
||
|
</td><td><pre>
|
||
|
class A {
|
||
|
public :
|
||
|
A(int );
|
||
|
};
|
||
|
class B : public A {
|
||
|
public :
|
||
|
int f();
|
||
|
B() : A(f()) {} // warn
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.MemberOrBaseRefBeforeCtor
|
||
|
<br>(C++)</span><br><br>
|
||
|
C++ Undefined behavior: non-static member or base class of non-POD class type
|
||
|
is referred before constructor begins execution<br>
|
||
|
C++11 Undefined behavior: non-static member or base class of a class with a
|
||
|
non-trivial constructor is referred before constructor begins execution
|
||
|
</td><td><pre>
|
||
|
// C++03
|
||
|
struct POD {
|
||
|
int i;
|
||
|
};
|
||
|
|
||
|
struct non_POD : public POD {
|
||
|
int j;
|
||
|
POD pod;
|
||
|
};
|
||
|
|
||
|
extern POD pod;
|
||
|
extern non_POD non_pod;
|
||
|
|
||
|
int *p1 = &non_pod.j; // warn
|
||
|
int *p2 = &non_pod.pod.i; // warn
|
||
|
int *p3 = &pod.i; // ok
|
||
|
POD *p4 = &non_pod; // warn
|
||
|
|
||
|
POD a;
|
||
|
non_POD b;
|
||
|
|
||
|
struct S {
|
||
|
int *k;
|
||
|
non_POD non_pod;
|
||
|
S() : k(&non_pod.j) {} // warn
|
||
|
};
|
||
|
|
||
|
// C++11
|
||
|
struct trivial {
|
||
|
int i;
|
||
|
};
|
||
|
|
||
|
struct non_trivial: public trivial {
|
||
|
non_trivial() {};
|
||
|
int j;
|
||
|
trivial pod;
|
||
|
};
|
||
|
|
||
|
extern trivial t;
|
||
|
extern non_trivial nt;
|
||
|
|
||
|
int *p1 = &nt.j; // warn
|
||
|
int *p2 = &nt.i; // warn
|
||
|
int *p3 = &t.i; // ok
|
||
|
trivial *p4 = &nt;
|
||
|
|
||
|
trivial t;
|
||
|
non_trivial nt;
|
||
|
|
||
|
struct S {
|
||
|
int *k;
|
||
|
non_trivial nt;
|
||
|
S() : k(&nt.j) {} // warn
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.MemberRefAfterDtor
|
||
|
<br>(C++)</span><br><br>
|
||
|
C++03: Undefined behavior: non-static member of non-POD class type is referred
|
||
|
after destructor ends execution<br>
|
||
|
C++11: Undefined behavior: non-static member of a class with a non-trivial
|
||
|
destructor is referred after destructor ends execution
|
||
|
</td><td><pre>
|
||
|
// C++03
|
||
|
struct non_POD {
|
||
|
virtual void f() {};
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
non_POD *non_pod = new non_POD();
|
||
|
non_pod->~non_POD();
|
||
|
non_pod->f(); // warn
|
||
|
}
|
||
|
|
||
|
// C++11
|
||
|
struct S {
|
||
|
~S() {};
|
||
|
void f() {};
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
S *s = new S();
|
||
|
s->~S();
|
||
|
s->f(); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.CtorForeignCall
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: call to virtual function of an object under construction
|
||
|
whose type is neither the constructors own class or one of its bases
|
||
|
</td><td><pre>
|
||
|
class A {
|
||
|
public:
|
||
|
virtual void f() {};
|
||
|
};
|
||
|
|
||
|
class B {
|
||
|
public:
|
||
|
B(A* a) { a->f(); } // warn
|
||
|
};
|
||
|
|
||
|
class C : public A, B {
|
||
|
public:
|
||
|
C() : B((A*)this) {}
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.CtorForeignCast
|
||
|
undefbehavior.CtorForeignTypeid
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the operand of typeid/dynamic_cast is an object under
|
||
|
construction whose type is neither the constructors own class or one of its
|
||
|
bases
|
||
|
</td><td><pre>
|
||
|
#include <typeinfo>
|
||
|
|
||
|
class A {
|
||
|
public:
|
||
|
virtual void f() {};
|
||
|
};
|
||
|
|
||
|
class B {
|
||
|
public:
|
||
|
B(A* a) {
|
||
|
typeid(*a); // warn
|
||
|
dynamic_cast<B*>(a); //warn
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class C : public A, B {
|
||
|
public:
|
||
|
C() : B((A*)this) {}
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.MemberRefInCatch
|
||
|
undefbehavior.BaseRefInCatch
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: referring to any non-static member or base class of an
|
||
|
object in the handler for a function-try-block of a constructor or destructor
|
||
|
for that object results in undefined behavior
|
||
|
</td><td><pre>
|
||
|
class C {
|
||
|
int i;
|
||
|
public :
|
||
|
C()
|
||
|
try
|
||
|
: i(1) {}
|
||
|
catch (...)
|
||
|
{
|
||
|
i=2; // warn
|
||
|
}
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ReturnAtCatchEnd
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: a function returns when control reaches the end of a
|
||
|
handler. This results in undefined behavior in a value-returning
|
||
|
function
|
||
|
</td><td><pre>
|
||
|
int test() try {
|
||
|
}
|
||
|
catch(int) {
|
||
|
} // warn
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.AutoptrsOwnSameObj
|
||
|
<br>(C++03)</span><br><br>
|
||
|
Undefined behavior: if more than one auto_ptr owns the same object at the same
|
||
|
time the behavior of the program is undefined.
|
||
|
</td><td><pre>
|
||
|
#include <memory>
|
||
|
|
||
|
void test() {
|
||
|
int *data = new int;
|
||
|
std::auto_ptr<int> p(data);
|
||
|
std::auto_ptr<int> q(data); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.BasicStringBoundAccess
|
||
|
<br>(C++03)</span><br><br>
|
||
|
Undefined behavior: out-of-bound basic_string access
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
std::basic_string<char> s;
|
||
|
char c = s[10]; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.BasicStringBoundModification
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: out-of-bound basic_string modification
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
std::basic_string<char> s;
|
||
|
s[10] = 0; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.EosDereference
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the result of operator*() on an end of stream is
|
||
|
undefined
|
||
|
</td><td><pre>
|
||
|
#include <vector>
|
||
|
|
||
|
void test() {
|
||
|
std::vector<int> v;
|
||
|
int i = *v.end(); // warn
|
||
|
*v.end() = 0; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.QsortNonPOD
|
||
|
undefbehavior.QsortNonTrivial
|
||
|
<br>C++</span><br><br>
|
||
|
C++03: Undefined behavior: the objects in the array passed to qsort are of
|
||
|
non-POD type<br>
|
||
|
C++11: Undefined behavior: the objects in the array passed to qsort are of
|
||
|
non-trivial type
|
||
|
</td><td><pre>
|
||
|
// C++03
|
||
|
#include <cstdlib>
|
||
|
|
||
|
struct non_POD {
|
||
|
int i;
|
||
|
non_POD(int ii) : i(ii) {}
|
||
|
};
|
||
|
|
||
|
non_POD values[] = { non_POD(2), non_POD(1) };
|
||
|
|
||
|
int compare(const void *a,
|
||
|
const void *b) {
|
||
|
return ( (*(non_POD*)a).i -
|
||
|
(*(non_POD*)b).i );
|
||
|
}
|
||
|
|
||
|
void test() {
|
||
|
qsort(values, 2, sizeof(non_POD),
|
||
|
compare); // warn
|
||
|
}
|
||
|
|
||
|
// C++11
|
||
|
#include <cstdlib>
|
||
|
|
||
|
struct S {};
|
||
|
|
||
|
struct trivial_non_POD : public S {
|
||
|
int i;
|
||
|
};
|
||
|
|
||
|
struct non_trivial {
|
||
|
int i;
|
||
|
non_trivial() {}
|
||
|
};
|
||
|
|
||
|
trivial_non_POD tnp[2];
|
||
|
non_trivial nt[2];
|
||
|
|
||
|
int compare1(const void *a,
|
||
|
const void *b) {
|
||
|
return ( (*(trivial_non_POD *)a).i -
|
||
|
(*(trivial_non_POD *)b).i );
|
||
|
}
|
||
|
|
||
|
int compare2(const void *a,
|
||
|
const void *b) {
|
||
|
return ( (*(non_trivial *)a).i -
|
||
|
(*(non_trivial *)b).i );
|
||
|
}
|
||
|
|
||
|
void test() {
|
||
|
qsort(tnp, 2, sizeof(trivial_non_POD),
|
||
|
compare1); // ok
|
||
|
qsort(nt, 2, sizeof(non_trivial),
|
||
|
compare2); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ThrowWhileCopy
|
||
|
<br>C++</span><br><br>
|
||
|
Undefined behavior: copy constructor/assignment operator can throw an exception.
|
||
|
The effects are undefined if an exception is thrown.
|
||
|
</td><td><pre>
|
||
|
struct S {
|
||
|
int i, j;
|
||
|
S (const S &s) {
|
||
|
i = s.i;
|
||
|
throw 1; // warn
|
||
|
j = s.j;
|
||
|
};
|
||
|
S &operator=(const S &s) {
|
||
|
i = s.i;
|
||
|
throw 1; // warn
|
||
|
j = s.j;
|
||
|
}
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ValarrayArgBound
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: the value of the second argument is greater than the number
|
||
|
of values pointed to by the first argument
|
||
|
</td><td><pre>
|
||
|
#include <valarray>
|
||
|
|
||
|
struct S {
|
||
|
int i;
|
||
|
S(int ii) : i(ii) {};
|
||
|
};
|
||
|
|
||
|
void test(void) {
|
||
|
S s[] = { S(1), S(2) };
|
||
|
std::valarray<S> v(s,3); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ValarrayLengthDiffer
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: valarray operands are of different length
|
||
|
</td><td><pre>
|
||
|
// C++03
|
||
|
#include <valarray>
|
||
|
|
||
|
void test(void) {
|
||
|
std::valarray<int> a(0, 1), b(0, 2);
|
||
|
std::valarray<bool> c(false, 1);
|
||
|
a = b; // warn
|
||
|
a *= b; // warn
|
||
|
a = a * b; // warn
|
||
|
c = a == b; // warn
|
||
|
b.resize(1);
|
||
|
a = b; // OK
|
||
|
}
|
||
|
|
||
|
// C++11
|
||
|
#include <valarray>
|
||
|
|
||
|
void test(void) {
|
||
|
std::valarray<int> a(0, 1), b(0, 2);
|
||
|
std::valarray<bool> c(false, 1);
|
||
|
a = b; // ok
|
||
|
a *= b; // ok
|
||
|
a = a * b; // warn
|
||
|
c = a == b; // warn
|
||
|
b.resize(1);
|
||
|
a = b; // OK
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ValarrayZeroLength
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: calling sum()/min()/max() method of an array having zero
|
||
|
length, the behavior is undefined
|
||
|
</td><td><pre>
|
||
|
#include <valarray>
|
||
|
|
||
|
void test(void) {
|
||
|
std::valarray<int> v(0, 0);
|
||
|
v.sum(); // warn
|
||
|
v.min(); // warn
|
||
|
v.max(); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.ValarrayBadIndirection
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: element N is specified more than once in the
|
||
|
indirection
|
||
|
</td><td><pre>
|
||
|
#include <valarray>
|
||
|
|
||
|
void test() {
|
||
|
size_t addr[] = {0, 1, 1}; // N is 1
|
||
|
std::valarray<size_t>indirect(addr, 3);
|
||
|
std::valarray<int> a(0, 5), b(1, 3);
|
||
|
a[indirect] = b; //warn
|
||
|
a[indirect] *= b; //warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.IosBaseDestroyedBeforeInit
|
||
|
<br>(C++)</span><br>
|
||
|
<br>Undefined behavior: ios_base object is destroyed before initialization have
|
||
|
taken place. basic_ios::init should be call to initialize ios_base
|
||
|
members
|
||
|
</td><td><pre>
|
||
|
#include <ios>
|
||
|
|
||
|
using namespace std;
|
||
|
template <class T, class Traits = std::char_traits<T>>
|
||
|
class my_stream1 : public std::basic_ios<T, Traits> {
|
||
|
};
|
||
|
|
||
|
template <class T, class Traits = std::char_traits<T>>
|
||
|
class my_stream2 : public std::basic_ios<T, Traits> {
|
||
|
class my_streambuf : public std::basic_streambuf<T, Traits> {
|
||
|
};
|
||
|
public:
|
||
|
my_stream2() {
|
||
|
this->init(new my_streambuf);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
my_stream1<char> *p1 = new my_stream1<char>
|
||
|
my_stream2<char> *p2 = new my_stream2<char>
|
||
|
delete p1; // warn
|
||
|
delete p2; // ok
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.IosBaseUsedBeforeInit
|
||
|
<br>(C++11)</span><br><br>
|
||
|
Undefined behavior: ios_base object is used before initialization have taken
|
||
|
place. basic_ios::init should be call to initialize ios_base members
|
||
|
</td><td><pre>
|
||
|
#include <ios>
|
||
|
|
||
|
using namespace std;
|
||
|
template <class T, class Traits = std::char_traits<T>>
|
||
|
class my_stream1 : public std::basic_ios<T, Traits> {
|
||
|
};
|
||
|
|
||
|
template <class T, class Traits = std::char_traits<T>>
|
||
|
class my_stream2 : public std::basic_ios<T, Traits> {
|
||
|
class my_streambuf : public std::basic_streambuf<T, Traits> {
|
||
|
};
|
||
|
public:
|
||
|
my_stream2() {
|
||
|
this->init(new my_streambuf);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
my_stream1<char> *p1 = new my_stream1<char>
|
||
|
my_stream2<char> *p2 = new my_stream2<char>
|
||
|
p1->narrow('a', 'b'); // warn
|
||
|
p2->narrow('a', 'b'); // ok
|
||
|
delete p1; // warn
|
||
|
delete p2; // ok
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">undefbehavior.MinusOnePosType
|
||
|
<br>(C++)</span><br><br>
|
||
|
Undefined behavior: passing -1 to any streambuf/istream/ostream member that
|
||
|
accepts a value of type traits::pos_type result in undefined behavior
|
||
|
</td><td><pre>
|
||
|
#include <fstream>
|
||
|
|
||
|
class my_streambuf : public std::streambuf {
|
||
|
void f() {
|
||
|
seekpos(-1); // warn
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
std::filebuf fb;
|
||
|
std::istream in(&fb);
|
||
|
std::ostream out(&fb);
|
||
|
std::filebuf::off_type pos(-1);
|
||
|
in.seekg(pos); // warn
|
||
|
out.seekp(-1); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
</table>
|
||
|
|
||
|
<!-- ============================ different ================================ -->
|
||
|
<h3>different</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr>
|
||
|
</thead>
|
||
|
|
||
|
<tr><td><span class="name">different.ArgEvalOrderUndef
|
||
|
<br>(C)</span><br><br>
|
||
|
Errors because of the order of evaluation of function arguments is undefined
|
||
|
</td><td><pre>
|
||
|
void f(int, int);
|
||
|
|
||
|
void test() {
|
||
|
int i = 0;
|
||
|
int v[1] = {0};
|
||
|
f(v[i], i++); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.IdenticalExprBinOp
|
||
|
<br>(C)</span><br><br>
|
||
|
There are identical sub-expressions to the left and to the right of the
|
||
|
operator
|
||
|
</td><td><pre>
|
||
|
#define A 1
|
||
|
#define B 1
|
||
|
|
||
|
bool isNan(double d) {
|
||
|
return d != d; // ok
|
||
|
}
|
||
|
|
||
|
int f();
|
||
|
|
||
|
void test() {
|
||
|
int i = 0;
|
||
|
if (i != 0 && i != 0) {} // warn
|
||
|
|
||
|
if(i == A || i == B) {} // ok
|
||
|
|
||
|
if (++i != 0 && ++i != 0) {} // ok
|
||
|
|
||
|
if (f() && f()) {} // ok
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.FuncPtrInsteadOfCall
|
||
|
<br>(C)</span><br><br>
|
||
|
Possibly a function call should be used instead of a pointer to function
|
||
|
</td><td><pre>
|
||
|
int f();
|
||
|
|
||
|
void test() {
|
||
|
if (f == 0) {} // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.IdenticalCondIfElseIf
|
||
|
<br>(C)</span><br><br>
|
||
|
The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a
|
||
|
probability of logical error presence
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i = 7;
|
||
|
if (i == 1) {}
|
||
|
else if (i == 1) {} // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">SuccessiveAssign
|
||
|
<br>(C)</span><br><br>
|
||
|
Successive assign to a variable
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i=0;
|
||
|
i=1;
|
||
|
i=2; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.NullDerefStmtOrder
|
||
|
<br>enhancement to core.NullDereference<br>(C)</span><br><br>
|
||
|
Dereferencing of the null pointer might take place. Checking the pointer for
|
||
|
null should be performed first
|
||
|
</td><td><pre>
|
||
|
struct S {
|
||
|
int x;
|
||
|
};
|
||
|
|
||
|
S* f();
|
||
|
|
||
|
void test() {
|
||
|
S *p1 = f();
|
||
|
int x1 = p1->x; // warn
|
||
|
if (p1) {};
|
||
|
|
||
|
S *p2 = f();
|
||
|
int x2 = p2->x; // ok
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.NullDerefCondOrder
|
||
|
<br>enhancement to core.NullDereference<br>(C)</span><br><br>
|
||
|
Dereferencing of the null pointer might take place. Checking the pointer for
|
||
|
null should be performed first
|
||
|
</td><td><pre>
|
||
|
struct S{bool b;};
|
||
|
|
||
|
S* f();
|
||
|
|
||
|
void test() {
|
||
|
S *p = f();
|
||
|
if (p->b && p) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.IdenticalStmtThenElse
|
||
|
<br>(C)</span><br><br>
|
||
|
The 'else' statement is equivalent to the 'then' statement
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i;
|
||
|
if (i==1) {
|
||
|
i++;
|
||
|
}
|
||
|
else { // warn
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.MultipleAccessors
|
||
|
<br>(C++)</span><br><br>
|
||
|
multiple accessors met for 'class::field'
|
||
|
</td><td><pre>
|
||
|
class A {
|
||
|
int i;
|
||
|
int j;
|
||
|
public:
|
||
|
int getI() { return i; }
|
||
|
int getJ() { return i; } // warn
|
||
|
void setI(int& ii) { i = ii; }
|
||
|
void setJ(int& jj) { i = jj; } // warn
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.AccessorsForPublic
|
||
|
<br>(C++)</span><br><br>
|
||
|
Accessors exist for 'class::field'. Should this field really be public?
|
||
|
</td><td><pre>
|
||
|
class A {
|
||
|
public:
|
||
|
int i; // warn
|
||
|
int getI() { return i; }
|
||
|
void setI(int& ii) { i = ii; }
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.LibFuncResultUnised
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Calling 'f' ignoring its return value is of no use (* create the list of known
|
||
|
system/library/API functions falling into this category)
|
||
|
</td><td><pre>
|
||
|
#include <vector>
|
||
|
|
||
|
void test() {
|
||
|
std::vector<int> v;
|
||
|
v.empty(); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.WrongVarForStmt
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Possibly wrong variable is used in the loop/cond-expression of the 'for'
|
||
|
statement. Did you mean 'proper_variable_name'?
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i;
|
||
|
int j;
|
||
|
for (j=0; j<3; ++i); // warn
|
||
|
for (int j=0; i<3; ++j); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.FloatingCompare
|
||
|
<br>(C)</span><br><br>
|
||
|
Comparing floating point numbers may be not precise
|
||
|
</td><td><pre>
|
||
|
#include <math.h>
|
||
|
|
||
|
void test() {
|
||
|
double b = sin(M_PI / 6.0);
|
||
|
if (b == 0.5) // warn
|
||
|
b = 0;
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.BoolCompare
|
||
|
<br>maybe merge with experimental.core.BoolAssignment<br>(C, C++)</span><br><br>
|
||
|
Comparing boolean to a value other then 0 or 1
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i;
|
||
|
if (0 < i < 3) {}; // warn
|
||
|
bool b;
|
||
|
if (b == 3) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.BitwiseOpBoolArg
|
||
|
<br>maybe join with experimental.core.BoolAssignment<br>(C, C++)</span><br><br>
|
||
|
bool value is used at the left/right part of the & (|) operator. Did you mean
|
||
|
&& (||) ?
|
||
|
</td><td><pre>
|
||
|
int f();
|
||
|
|
||
|
void test() {
|
||
|
bool b = true;
|
||
|
if (b & f()) {} // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.LabelInsideSwitch
|
||
|
<br>(C)</span><br><br>
|
||
|
Possible misprint: label found inside the switch() statement. (* did you mean
|
||
|
'default'?)
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int c = 7;
|
||
|
switch(c){
|
||
|
case 1:
|
||
|
c += 1; break;
|
||
|
defalt: // warn
|
||
|
c -= 1; break;
|
||
|
}
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.IdenticalCondIfIf
|
||
|
<br>(C)</span><br><br>
|
||
|
The conditions of two subsequent 'if' statements are identical
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int c = 7;
|
||
|
if (c > 5) // <-
|
||
|
c += 1;
|
||
|
if (c > 5) // warn
|
||
|
c -= 1;
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.CondOpIdenticalReturn
|
||
|
<br>(C)</span><br><br>
|
||
|
The return expressions of the '?:' operator are identical
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
unsigned a;
|
||
|
a = a > 5 ? a : a; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.LogicalOpUselessArg
|
||
|
<br>(C)</span><br><br>
|
||
|
The second operand of the && operator has no impact on expression result
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
unsigned a;
|
||
|
if (a<7 && a<10) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.SameResLogicalExpr
|
||
|
<br>(C)</span><br><br>
|
||
|
The expression always evaluates to true/false
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int i=0;
|
||
|
if (i!=0) {}; // warn
|
||
|
if (i==0 && i==1) {}; // warn
|
||
|
if (i<0 || i>=0) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.SameResUnsignedCmp
|
||
|
<br>(C)</span><br><br>
|
||
|
Comparison of unsigned expression 'op expr' is always true/false
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
unsigned u;
|
||
|
if (u < -1) {}; // warn
|
||
|
if (u >= 0) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.OpPrecedenceAssignCmp
|
||
|
<br>(C)</span><br><br>
|
||
|
Comparison operation has higher precedence then assignment. Bool value is
|
||
|
assigned to variable of type 'type'. Parenthesis may bee required around an
|
||
|
assignment
|
||
|
</td><td><pre>
|
||
|
int f();
|
||
|
|
||
|
void test() {
|
||
|
bool b;
|
||
|
int x, y;
|
||
|
if((b = x != y)) {} // ok
|
||
|
if((x = f() != y)) {} // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.OpPrecedenceIifShift
|
||
|
<br>(C)</span><br><br>
|
||
|
?: has lower precedence then <<
|
||
|
</td><td><pre>
|
||
|
#include <iostream>
|
||
|
|
||
|
void test() {
|
||
|
int a;
|
||
|
std::cout << a ? "a" : "b"; // warn
|
||
|
a << a>7 ? 1 : 2; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.ObjectUnused
|
||
|
<br>(C++)</span><br><br>
|
||
|
The object was created but is not being used<br><br>
|
||
|
The exception object was created but is not being used. Did you mean
|
||
|
'throw std::exception();'?
|
||
|
</td><td><pre>
|
||
|
#include <exception>
|
||
|
|
||
|
struct S {
|
||
|
int x, y;
|
||
|
S(int xx, int yy) : x(xx), y(yy) {
|
||
|
}
|
||
|
S(int xx) {
|
||
|
S(xx, 0); // warn
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void test() {
|
||
|
S(0, 0); // warn
|
||
|
std::exception(); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.StaticArrayPtrCompare
|
||
|
<br>(C)</span><br><br>
|
||
|
Pointer to static array is being compared to NULL. May the subscripting is
|
||
|
missing
|
||
|
</td><td><pre>
|
||
|
void test() {
|
||
|
int a1[1];
|
||
|
if (a1 == 0) {}; // warn
|
||
|
|
||
|
int a2[1][1];
|
||
|
if (a2[0]) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.ConversionToBool
|
||
|
<br>maybe join with experimental.core.BoolAssignment<br>(C, C++)</span><br><br>
|
||
|
Odd implicit conversion from 'type' to 'bool'
|
||
|
</td><td><pre>
|
||
|
bool test() {
|
||
|
return 1.; // warn
|
||
|
return ""; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.ArrayBound
|
||
|
<br>enhancement to experimental.security.ArrayBound[v2]<br>(C, C++)</span><br><br>
|
||
|
Out-of-bound dynamic array access
|
||
|
</td><td><pre>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
void test() {
|
||
|
int *p2 = new int[1];
|
||
|
if(p2[1]) {}; // warn
|
||
|
int i = 1;
|
||
|
if(p2[i]) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.StrcpyInputSize
|
||
|
<BR>enhancement to experimental.unix.cstring.OutOfBounds<br>(C)</span><br><br>
|
||
|
Buffer copy without checking size of input
|
||
|
</td><td><pre>
|
||
|
void test(char* string) {
|
||
|
char buf[24];
|
||
|
strcpy(buf, string); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.IntegerOverflow
|
||
|
<br>(C)</span><br><br>
|
||
|
Integer overflow
|
||
|
</td><td><pre>
|
||
|
#include <limits.h>
|
||
|
|
||
|
int f(int x) {
|
||
|
return INT_MAX+1; // warn
|
||
|
}
|
||
|
|
||
|
void test() {
|
||
|
int x = INT_MAX+1; // warn
|
||
|
f(INT_MAX+1); // warn
|
||
|
|
||
|
int y = INT_MAX/2+1; // warn
|
||
|
x = y*2; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.SignExtension
|
||
|
<br>(C)</span><br><br>
|
||
|
Unexpected sign extension might take place
|
||
|
</td><td><pre>
|
||
|
void f(unsigned int i);
|
||
|
int g();
|
||
|
|
||
|
unsigned int test() {
|
||
|
long long sll;
|
||
|
unsigned long long ull = sll; // warn
|
||
|
long sl;
|
||
|
unsigned long ul = sl; // warn
|
||
|
int si;
|
||
|
unsigned int ui = si; // warn
|
||
|
short ss;
|
||
|
unsigned short us = ss; // warn
|
||
|
signed char sc;
|
||
|
unsigned char uc = sc; // warn
|
||
|
f(si); // warn
|
||
|
ui = g(); // warn
|
||
|
return si; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.NumericTruncation
|
||
|
<br>(C)</span><br><br>
|
||
|
Numeric truncation might take place
|
||
|
</td><td><pre>
|
||
|
void f(int i);
|
||
|
int g();
|
||
|
|
||
|
int test() {
|
||
|
unsigned long long ull;
|
||
|
long long sll;
|
||
|
unsigned long ul = ull; // warn
|
||
|
long sl = sll; // warn
|
||
|
unsigned int ui = ul; // warn
|
||
|
int si = sl; // warn
|
||
|
unsigned short us = ui; // warn
|
||
|
short ss = si; // warn
|
||
|
unsigned char uc = us; // warn
|
||
|
signed char sc = uc; // warn
|
||
|
f(sll); // warn
|
||
|
ss = g(); // warn
|
||
|
return sll; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">different.MissingCopyCtorAssignOp
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
The class has dynamically allocated data members but do not define a copy
|
||
|
constructor/assignment operator
|
||
|
</td><td><pre>
|
||
|
class C { // warn
|
||
|
int *p; // <-
|
||
|
public:
|
||
|
C() { p = new int; }
|
||
|
~C() { delete p; }
|
||
|
};
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- ============================ WinAPI =================================== -->
|
||
|
<h3>WinAPI</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">WinAPI.CreateProcess
|
||
|
<br>(C)</span><br><br>
|
||
|
After calling CreateProcess(), ensure that process and thread handles get closed
|
||
|
(* for the given example: examine data flow from pi, pi.hProcess and pi.hThread)
|
||
|
</td><td><pre>
|
||
|
#include <windows.h>
|
||
|
|
||
|
void test() {
|
||
|
STARTUPINFO si;
|
||
|
PROCESS_INFORMATION pi;
|
||
|
BOOL fSuccess;
|
||
|
fSuccess = CreateProcess(
|
||
|
NULL, TEXT("MyProgram.exe"), NULL, NULL,
|
||
|
TRUE, 0, NULL, NULL, &si, &pi);
|
||
|
} // warn
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">WinAPI.LoadLibrary
|
||
|
<br>(C)</span><br><br>
|
||
|
Calling LoadLibrary without a fully qualified path may allow to load a DLL from
|
||
|
arbitrary location
|
||
|
</td><td><pre>
|
||
|
#include <windows.h>
|
||
|
|
||
|
void test() {
|
||
|
HINSTANCE h = LoadLibrary("X.dll"); // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">WinAPI.WideCharToMultiByte
|
||
|
<br>(C)</span><br><br>
|
||
|
Buffer overrun while calling WideCharToMultiByte
|
||
|
</td><td><pre>
|
||
|
#include <windows.h>
|
||
|
|
||
|
void test()
|
||
|
{
|
||
|
wchar_t ws[] = L"abc";
|
||
|
char s[3];
|
||
|
int res1 = WideCharToMultiByte(
|
||
|
CP_UTF8, 0, ws, -1, s,
|
||
|
3, NULL, NULL); // warn
|
||
|
int res2 = WideCharToMultiByte(
|
||
|
CP_UTF8, 0, ws, -1, s,
|
||
|
3, NULL, NULL); // ok
|
||
|
if (res2 == sizeof(s))
|
||
|
s[res2-1] = 0;
|
||
|
else
|
||
|
s[res2] = 0;
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<!-- =========================== optimization ============================== -->
|
||
|
<h3>optimization</h3>
|
||
|
<table class="checkers">
|
||
|
<col class="namedescr"><col class="example"><col class="progress">
|
||
|
<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
|
||
|
|
||
|
<tr><td><span class="name">optimization.PassConstObjByValue
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Optimization: It is more effective to pass const n-th parameter by reference to
|
||
|
avoid unnecessary object copying
|
||
|
</td><td><pre>
|
||
|
struct A {
|
||
|
int a[20];
|
||
|
int b;
|
||
|
};
|
||
|
|
||
|
bool FirstIsZero(const struct A a) { // warn
|
||
|
return a.a[0] == 0;
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">optimization.PostfixIncIter
|
||
|
<br>(C++)</span><br><br>
|
||
|
Optimization: It is more effective to use prefix ++ with iterator here
|
||
|
</td><td><pre>
|
||
|
#include <vector>
|
||
|
|
||
|
void test() {
|
||
|
std::vector<int> v;
|
||
|
std::vector<int>::const_iterator it;
|
||
|
for(it = v.begin();
|
||
|
it != v.end(); it++) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">optimization.MultipleCallsStrlen
|
||
|
<br>(C)</span><br><br>
|
||
|
Optimization: multiple calls to strlen for a given string in the given
|
||
|
expression. It is more effective to hold strlen result in a temporary
|
||
|
variable
|
||
|
</td><td><pre>
|
||
|
#include <string.h>
|
||
|
|
||
|
void test() {
|
||
|
const char* s = "abc";
|
||
|
if (strlen(s) > 0 &&
|
||
|
strlen(s) < 7) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">optimization.EmptyCstrDetect
|
||
|
<br>(C)</span><br><br>
|
||
|
Optimization: it is more efficient to use "str[0] != '\0'" to identify an empty
|
||
|
string
|
||
|
</td><td><pre>
|
||
|
#include <string.h>
|
||
|
|
||
|
void test() {
|
||
|
const char* s = "abc";
|
||
|
if (strlen(s) > 0) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">optimization.StrLengthCalculation
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Optimization: it is more efficient to use string::length() method to calculate
|
||
|
string length
|
||
|
</td><td><pre>
|
||
|
#include <string>
|
||
|
#include <string.h>
|
||
|
|
||
|
void test() {
|
||
|
std::string s;
|
||
|
if (strlen(s.c_str()) != 0) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
<tr><td><span class="name">optimization.EmptyContainerDetect
|
||
|
<br>(C, C++)</span><br><br>
|
||
|
Optimization: It is more efficient to use container.empty() to identify an
|
||
|
empty container
|
||
|
</td><td><pre>
|
||
|
#include <list>
|
||
|
|
||
|
void test() {
|
||
|
std::list<int> l;
|
||
|
if (l.size() != 0) {}; // warn
|
||
|
}
|
||
|
</pre></td><td class="aligned"></td></tr>
|
||
|
|
||
|
</table>
|
||
|
|
||
|
<br>
|
||
|
</div> <!-- page -->
|
||
|
</div> <!-- content -->
|
||
|
</body>
|
||
|
</html>
|