C/C++ KAST examples

From Insight-10.0

Contents

Overparenthesized expression

// ParensExpr / Expr:: { ParensExpr/Expr:: } ParensExpr

int f(int i) {
return ((i + 1)) / 2;
}

int g(int n) {
return (((i - 1))) * 2;
}

Note: Use the "sequence of 0 or more children" modifier ({}) to match a chain of child AST nodes.

Find all static functions

// FuncDeclarator [ isStatic() ]

// finds this function
static void f() {
// ...
}

// and this one
static void h();

class C {
public:
// finds this function as well
static void m() {
// ...
}
};

Find all definitions of static functions

// FuncDeclarator [ isStatic() ] [ isDefinition() ]

// finds this function
static void f() {
}

// but not this one - not definition
static void g();

Find all definitions of functions whose return type is a pointer

// PtrDeclarator [ isFunction() ] [ isDefinition() ] [ @Spec = KTC_POINTEROPERATOR_POINTER ]

// finds this
void *foo() {
// ...
}

// and this
FILE *xfo(const char *name) {
// ...
}

// no match - returns reference, not pointer
int &getir(unsigned key) {
// ...
}

// no match
int boo() {
// ...
}

Find all functions whose return type is not a pointer

// FuncDeclarator [ not parent::PtrDeclarator | parent::PtrDeclarator [ @Spec != KTC_POINTEROPERATOR_POINTER ] ]

// no match - returns pointer
void *foo() {
// ...
}

// no match - returns pointer
FILE *xfo(const char *name) {
// ...
}

// finds this - returns reference, not a pointer
int &getir(unsigned key) {
// ...
}

// and this
int boo() {
// ...
}

Note: Use the not operator to negate the result of a predicate.

Find definitions of all non-member functions that use void in return type

This refers to functions such as void functions and functions returning pointer to void.

// FuncDef [ DeclSpecs[*]::BuiltinType [ @Spec = KTC_BUILTINTYPE_VOID ] ]

// finds this function
void printMe() {
}

// and this one
inline void *getAddress() {
}

// no match - not void
int getCount() {
}

// no match - not void
static FILE *openFile() {
}

Note: Use the "any element" modifier ([*]) to check if a sequence of AST nodes contains a node of specified type.

Note 2: This pattern is applicable to definitions of non-member functions, but not for definitions of class methods. To catch method definitions that use void in their return types, modify this pattern replacing "FuncDef" with "MemberFunc".

Find all void functions

// FuncDeclarator [ parent::DeclOrStmt / DeclSpecs[*]::BuiltinType [ @Spec = KTC_BUILTINTYPE_VOID ] ]

// find this function
void f() {
}

// and this one
static void g() {
}

// no match - return type is not void
inline int h() {
return 0;
}

// no match - return type is a pointer to void
void *j() {
return NULL;
}

Find all static member functions

// FuncDeclarator [ isClassMember() ] [ isStatic() ]

class C {
public:
// finds this function
static void m() {
// ...
}
};

// but not this one - not a member
static void f() {
}

Find all anonymous enums

// EnumType [ Name::Null ]

// finds this enum
typedef enum {
VALUE_0,
VALUE_1
} anonymousEnum;

// but not this one
enum aEnum{
INT_VALUE_0 = 0,
INT_VALUE_1 = 1
};

Note: Use Null to indicate a missing child of an AST node.

Find all anonymous unions

// ClassType [ @Tag = KTC_CLASSTAG_UNION ] [ Name::Null ]

struct S {
// finds this union
union {
short s;
int i;
long l;
} n;
};

// no match - named union
union U {
int i;
void *p;
};

Find non-template functions

// FuncDeclarator [ not isTemplate() ] [ not getParent().isTemplate() ] [ not getParent().getParent().isSpecialization() ]

// no match - template function
template<>
void f() {
}

template<>
class C {
public:
// no match - member of template class
void m() {
// ...
}
};

class D {
public:
// finds this member function
void n1() {
}
// no match - member template
template<>
void n2() {
// ...
}
};

// finds this function
void g() {
// ...
}

Find all assignment expressions

// BinaryExpr [ getOperationCode() = KTC_OPCODE_ASSIGN ]

void f() {
char *p;
// ...
p = "boo!";
}

Cast expression as lvalue

// BinaryExpr [ getOperationCode() = KTC_OPCODE_ASSIGN ] / Left::CastExpr

void f() {
int *p;
// ...
(char *) p = "boo!";
}

Find functions that are syntactically declared within a namespace

// FuncDeclarator [ ancestor::NamespaceDecl ]

namespace my {
// finds this
void f();
}

namespace their {
// and finds these two functions
int f1(int), f2(int, int);
}

Find functions that are syntactically declared not within a namespace

// FuncDeclarator [ not ancestor::NamespaceDecl ]

// finds this
void f();

namespace xn {
// no match - within a namespace
void f();
}

Find functions that are syntactically declared in an anonymous namespace

// FuncDeclarator [ ancestor::NamespaceDecl [ @Id = ] ]

namespace {
// finds this
void f();
}

namespace my {
// no match - namespace has name
void f();
}

// no match - on the global level
void f();

Find declarations of all non-static variables that are not part of any namespace ("global" variables)

// NameDeclarator [ isVariable() ] [ isGlobal() ] [ not isStatic() ]

namespace n {
// no match - variable in namespace;
int a;
}

// no match - static variable
static int b;

// no match - not a variable
void f();

// finds this variable
extern int c;

Find all classes whose name does not start with uppercase letter

//ClassType [ isDefinition() ][ not getName().starts-from-capital() ]

class A {
};

// finds this class
class b {
};

struct C {
// and this union
union x {
};
};

Find all non-public member variables whose name does not start from "m_"

// NameDeclarator [ isClassMember() ] [ isVariable() ] [ not isPublic() ] [ not getName().starts-with('m_') ]

class C {
public:
// no match - public member
static int count;
protected:
// no match - starts with "m_"
char *m_data;
// finds this member variable
int length;
};

Find all destructor declarations

// MemberDecl / Declarators[*]::AnyDeclarator [ isDestructor() ]

class C {
~C();
};

Note: Use the isDestructor() predicate to check if an AST node declares a destructor.

Find all inline destructor definitions (destructors defined inside a class)

// MemberFunc [ isDestructor() ]

class C {
~C() {}
};

Note: Use the isDestructor() predicate to check if an AST node declares a destructor.

Find classes with non-virtual destructor

// ClassType [ MemberDecls[*]::MemberDecl / Declarators[*]::AnyDeclarator [ isDestructor() ] [ not isVirtual() ] ]

// ClassType [ MemberDecls[*]::MemberFunc [ isDestructor() ] [ not isVirtual() ] ]

// this class matches
class A {
public:
~A();
};

// this one matches too
class B {
public:
~B() {}
};

// this class doesn't match - its destructor is virtual
class C {
public:
virtual ~C() {}
};

Find cases where a boolean value is incremented or decremented

// UnaryExpr [ getOperationCode() = KTC_OPCODE_PREINC | getOperationCode() = KTC_OPCODE_POSTINC | getOperationCode() = KTC_OPCODE_PREDEC | getOperationCode() = KTC_OPCODE_POSTDEC ] [ Expr.isBoolean() ]

int main() {
bool bFlag = true;
// matches all of the following expressions
bFlag++;
bFlag--;
--bFlag;
++bFlag;
return 0;
}

Find labels followed by a "}" (empty labeled statements)

// LabeledStmt [ Stmt::Null ]

int main(int argc, char** argv) {
if (argc < 3) {
return 1;
myLabel: /* empty labeled statement will be matched here */
}
return 0;
}

Find 'typedef's that do not define anything

// Decl [ DeclSpecs[*]::StorageClass [ @Spec = KTC_STORAGECLASS_TYPEDEF ] ] [ not Declarators[0]:: { AnyDeclarator / Declarator:: } NameDeclarator ]

/* These two 'typedef' definitions do not define anything */
typedef unsigned int;
signed char typedef;

Usually these type definitions are either typographical or programming errors. Please note that 'typedef' is not a special keyword, but rather another declaration specifier and may be inserted at any point among other declaration specifiers. That is why it is important to look for ’typedef’ at in any position within declaration specifiers, not at the beginning only (although that is where it is usually put).

To match any node in the sequence of nodes of the same generic type ('DeclSpec'’ link in this case), use the '[*]' modifier as shown.

Find signed bit fields occupying only one bit

// BitFieldDeclarator [ Bits::LiteralExpr.getIntValue() = 1 ] [ isSigned() ]

struct MT {
int ii: 1;
signed int si: 1;
};

Storing a sign requires one bit, so there are no bits left for the value itself. The pattern looks for member declarations. For uniformity reasons in C and C++, structure and union fields are called members (because in C++ structures and unions are special types of classes). The number of bits is attached to the member declarator. We check numeric attribute for equality to one and apply a built-in predicate 'isSignedInt()' to a declarator to find out if a declared member is a signed integer.

Again, the '[*]' modifier is used to match any of the structure or union members in the sequence.

Find switch selector that is constant

//CaseLable[Expr::LiteralExpr]

void foo(int c) {
switch (c) {
case 1: bar(); /* constant is used for a selector */
break;
default: baz();
}
}

This checker finds occurrences where a constant is used, but it would be better to use the value of enumerated type.

Find all IF statements

//IfStmt

void foo(boolean b) {
if (b) {
/* code */
}
}

Find IF statements with THEN branch containing at least one executable statement

// IfStmt [ Then::CompoundStmt / Stmts[*]::ExprStmt | Then::ExprStmt ]

void foo(boolean b) {
if (b) { // match this one
printf("IfStatement");
}
if (b) { // match this one
printf("IfStatement: Then");

} else {
printf("IfStatement: Else");
}
if (b) { // do not match this one
/* no code */
}
}

Find IF statements with no ELSE branch

// IfStmt [ Else::Null ]

void foo(boolean b) {
if (b) { // find this one
/* code */
}
if (b) { // do not match this one
/* code */
} else {
/* code */
}
}

Find binary "+" expressions

// BinaryExpr [ getOperationCode() = KTC_OPCODE_ADD ]

void foo(booleanint a, int b) {
int c = a + b; // match this one
c = ++b; // do not match
c = a / b; // do not match
}

Find calls to "gets"

// CallExpr / Func::IdExpr / Name::Name [ @Id = 'gets' ]

int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}

Find functions that return "void"

// FuncDeclarator [ parent::* [ DeclSpecs[*]::BuiltinType [ @Spec = KTC_BUILTINTYPE_VOID ] ] ]

static void foo() {
/* code */
}
void foo2() {
}

Find functions that return "void" or "char"

// FuncDeclarator [ $ret_type := getReturnType().getTypeName() ] [ $ret_type = 'void' | $ret_type = 'char' ]

static char foo1() {
}
void foo2() {
}
int foo3() {
}

Find inline void functions

// FuncDeclarator [ parent::* [ DeclSpecs[*]::BuiltinType [ @Spec = KTC_BUILTINTYPE_VOID ] ] ] [ isInline() ]

inline void foo() { // will match
}
static void foo1() { // will not match
}
inline char foo2() { // will not match
}
void foo3() { // will not match
}

Find functions whose declarations use formal parameter with names and without

//FuncDeclarator [ Params[*]::Decl [ Declarators[*]:: { AnyDeclarator / Declarator:: } NameDeclarator ] ] [ Params[*]::Decl [ not Declarators[*]:: { AnyDeclarator / Declarator:: } NameDeclarator ] ]

// will not match
void f();

// will not match
void fu(int, const char *);

// will not match
void fn(int id, const char *name);

// will match
void fx1(int id, const char *);

// will match
void fx2(int, const char *name);

Every non-empty clause in a switch statement shall be terminated with a break statement

// LabeledStmt [ Label::CaseLabel | Label::CaseRangeLabel | Label::DefaultLabel ] [ not parent::LabeledStmt [ Label::CaseLabel | Label::CaseRangeLabel | Label::DefaultLabel ] ] [ Stmt::CompoundStmt [ not descendant::BreakStmt ] [ not descendant::ThrowExpr ] [ not descendant::ReturnStmt ] | Stmt::LabeledStmt [ not Stmt::Null ] [ not descendant::BreakStmt ] [ not descendant::ThrowExpr ] [ not descendant::ReturnStmt ] | Stmt::* [ name() != 'CompoundStmt' ] [ name() != 'BreakStmt' ] [ name() != 'ReturnStmt' ] [ name() != 'LabeledStmt' ] [ not descendant::ThrowExpr ] | Stmt::Null ] [ next-sibling:: { * [ name() != 'BreakStmt' ] [ name() != 'ReturnStmt' ] [ not descendant::ThrowExpr ] / next-sibling:: } LabeledStmt [ Label::CaseLabel | Label::CaseRangeLabel | Label::DefaultLabel ] | next-sibling:: { * [ name() != 'BreakStmt' ] [ name() != 'ReturnStmt' ] [ not descendant::ThrowExpr ] / next-sibling:: } Null ]

void test193()
{
int x = 0;

switch (x)
{
case 0:
break;

case 1:
case 2:
break;
case 3:
4+5; // MATCHES

default:
// MATCHES
}

switch (x)
{
case 0:
break;

case 1:
case 2:
break;
case 4:
x = 0;
break;

default:
break;
}

int i=0;

switch(i)
{
case 0 :
i++; // MATCHES
case 1 :
i++;
break;
case 3 :
case 4 :
break;
case 5:
i++; // MATCHES
case 8:{
i++; // MATCHES
}
case 9:{
} // MATCHES
default:{
} // MATCHES
}

}

Floating point variables shall not be used as loop counters

// ForStmt [ Init::ExprStmt [ descendant::IdExpr [ isFloatPoint() ] ] ]

void test197()
{
int y,i,x = 0;
float j,x1 = 0;
for (x = 0; x < y; x = y++);
for (x1 = 0; x < 15; x++); // MATCHES
for (x1 = 0; x < j; x++); // MATCHES
for (x1 = 0; x < j; x = j++); // MATCHES
for (x = 0; i < 15; i++);
}

All letters contained in function and variable names will be composed entirely of lowercase letters

// NameDeclarator [ isVariable() | isFunction() ] [ getName().matches('[A-Z]') ]

class C
{
int f;
int g;
int H; // MATCHES
int MLK; // MATCHES
};

struct sD{};

struct SDKJSDF{};

extern void xY; // MATCHES

void test051();

void teSt051(); // MATCHES

void teSt051() // MATCHES
{
int abcd;
int ABXY; // MATCHES
int Abyy; // MATCHES
int aCCCC; // MATCHES
double z_Y_k; // MATCHES
}

Finding unnecessary negative value testing for unsigned integers

// BinaryExpr [ getOperationCode() = KTC_OPCODE_GE | getOperationCode() = KTC_OPCODE_LT ] [ Left.isUnsigned() ] [ Right.getIntValue() = 0 ]

void testUnsignedNegative()
{
unsigned int abc;

if (abc < 0); // MATCHES

while (abc >= 0); // MATCHES
}

Any label referenced by a goto statement is declared in the same block, or in a block enclosing the goto statement

// Label [ $lname := @Id ] [ parent:: { * [ name() != 'CompoundStmt' ] / parent:: } CompoundStmt [ $container := this() ] / ancestor::FuncBody / descendant::GotoStmt [ @Label = $lname ] [ not ancestor::CompoundStmt [ this() = $container ] ] ]

void test6_6_1()
{
int j = 0;
goto L1;

for (j = 0; j < 10; ++j)
{
L1: // MATCHES
j;
}
}

void test6_6_1x()
{
int j = 0;
goto L2;

for (j = 0; j < 10; ++j)
{
}
L2:
j;
}

A wider integer type may not be assigned to a narrower integer type

// BinaryExpr [ getOperationCode() = KTC_OPCODE_ASSIGN ] [ Left.getTypeSize() < Right.getTypeSize() ]

void testGetTypeSize()
{
long int a;
short int x;

x = a; // MATCHES
}

An assignment operator shall be declared for classes that contain pointers to data items

// NameDeclarator [ isPointer() ] [ $ptr_dcl := getParent() ] [ ancestor::ClassType [ getSemanticInfo() = $ptr_dcl ] [ not descendant::NameDeclarator [ isAssignmentOperator() ] ] ]

class Matrix
{
Matrix()
{
}

Matrix & operator = (const Matrix & other)
{
}

Matrix (Matrix &other)
{
}

private:
int *p;
int x[];
};

class X
{
X()
{
}

private:
int *p; // MATCHES
int x[];
};

class P
{
P()
{
}

P (P &other)
{
}

private:
int *p; // MATCHES
int x[];
};

A copy constructor shall be declared for classes that contain pointers to data items

// NameDeclarator [ isPointer() ] [ $ptr_dcl := getParent() ] [ ancestor::ClassType [ getSemanticInfo() = $ptr_dcl ] [ not descendant::NameDeclarator [ isCopyConstructor() ] ] ]

class Matrix
{
Matrix()
{
}

Matrix & operator = (const Matrix & other)
{
}

Matrix (Matrix &other)
{
}

private:
int *p;
int x[];
};

class X
{
X()
{
}

private:
int *p; // MATCHES
int x[];
};

class Y
{
Y()
{
}

Y & operator = (const Y & other)
{
}

private:
int *p; // MATCHES (2)
int x[];
};

Builtin type "wchar_t" will not be used

// BuiltinType [ @Spec = KTC_BUILTINTYPE_WCHAR_T ]

void test013()
{
wchar_t x; // MATCHES
char y;
int j;
wchar_t *f; // MATCHES
}

A class, or structure will not be declared in the definition of its type

// AnyDeclarator [ isClass() ] [ parent::Decl / DeclSpecs[*]::ClassType | parent::MemberDecl / DeclSpecs[*]::ClassType]

struct S
{
} s; // MATCHES

struct D
{
};

D d;

class C
{
} s; // MATCHES

class F
{
};

F f;

class X
{
};

A class's virtual functions shall not be invoked from its destructor or any of its constructors

// CallExpr [ isClassMember() ] [ isVirtual() ] [ ancestor::FuncBody / parent::* [ isConstructor() | isDestructor() ] ]

class base
{
public:
base(int, int);
~base();
void xyz();
virtual void display()
{
int x = 5;
}
};
base::base (int a, int b)
{
display(); // MATCHES
xyz();
}

base::~base()
{
display(); // MATCHES
xyz();
}

class derived : public base
{
public:
void display()
{
int y = 0;
}
};

void main()
{
base *ptr = new derived();
ptr->display();
}

An enum will not be declared in the definition of its type

// AnyDeclarator [ isEnum() ] [ parent::Decl / DeclSpecs[*]::EnumType ]

enum
{
up,
down
} direction; // MATCHES

enum i { in, out } i; // MATCHES

enum XYZ_direction
{
up,
down
};
XYZ_direction direction;

class X
{
enum
{
max_length = 100,
max_time = 73
};
};

All declarations at file scope should be static where possible

// NameDeclarator [ not isStatic() ] [ isGlobal() ]

int x; // MATCHES
int y; // MATCHES
int z; // MATCHES
static int h;
const int n = 5; // const static by default when global

struct S{} static s;
class C{} c; // MATCHES

void test() // MATCHES
{
int a;
int b;
}

Trivial forwarding functions should be inlined

// FuncDef [ not isInline() ] [ FuncBody::FuncBody / Stmt::CompoundStmt [ Stmts[0]::ReturnStmt ] [ Stmts[1]::Null ] ]

int safe() // MATCHES
{
return 0;
}

int getVal() // MATCHES
{
return safe();
}

inline int getValue()
{
return safe();
}

inline int abc()
{
int x = 5;
x = 2;
x = 1;
x++;
return 0;
}

int cde()
{
int x = 5;
x = 2;
x = 1;
x++;
return 0;
}

A class must not overload the greater than operator function

// FuncDeclarator [ isOpFunc() ] [ descendant::OpFunc [ getOperationCode() = KTC_OPCODE_GT ] ]

class Matrix
{
Matrix()
{
}

Matrix & operator> (const Matrix & other)
{
}
};

The volatile keyword shall not be used

// CVQualifier [ @Spec = KTC_CVQUALIFIER_VOLATILE ]

void test205()
{
volatile int *x;
int y;
int j;
volatile int a,b,c; // MATCHES three times
}

User-specified identifiers (internal and external) will not rely on significance of more than 10 characters

// NameDeclarator [ getName().length() > 10 ]

extern int alskfdiwoueroiweuroiweurweoriuweoriuweroiwuero; // MATCHES
extern int kjkjkje01;

void test046()
{
int xcljkfsdlkjfsdlkfjsdl43534534534534534534534 = 0; // MATCHES
xcljkfsdlkjfsdlkfjsdl43534534534sfdsflkjsdf = 5;

int abcddde = 5;
abcddde = 20;

int x;
x = 0;

int lkajsdflksjflksjfwiuwoe31, lkasdjkffls45515j; // MATCHES twice
}

Documentation for custom C/C++ KAST checkers

Overview

How-to

Tutorials

Examples

  • C/C++ KAST examples

Deployment

Reference