I am trying to add a clang-tidy checker to detect patterns like if (ptr == NULL)
or something more complicated, which should be easily achieved by using AST matcher like below:
auto PtrVar = hasType(isAnyPointer());
auto PtrTracer = expr(anyOf(declRefExpr(PtrVar), memberExpr(member(PtrVar))));
auto EqNull = binaryOperator(hasOperatorName("=="), hasOperands(PtrTracer, ignoringImpCasts(nullPointerConstant())));
It works well on my test program:
#include <cstdio>
#include <cstdlib>
#include <cstring>
struct O {
int x;
char *s;
};
struct M {
int num;
char *name;
O *o;
};
int main() {
const char *s = "hello world";
M *m = (M *)malloc(sizeof(M));
if (m == NULL || m->num == 0 || m->name + 1 == NULL || m->o == NULL) {
return -1;
}
size_t len = strlen(s);
if (s == NULL) {
puts("NULL!!");
return -1;
}
if (strlen(s) > 4) return -1;
else if (sizeof(s) < puts(s)) return 1;
for (int i = 0; i < len; i++) {
if (s[i] == ' ') {
break;
}
}
return 0;
}
However, when I run the checker on real-world programs, it always misses lots of simple cases. For example, I fail to match any pattern like ptr == NULL
in bfd/compress.c
of binutils
project, though I can definitely match the two operands. Even when I copied my own test code into that file, the checker cannot show any results.
I dumped the AST of target code and tried to ignore imp/paren/cstyle casts, but all my efforts failed.
BinaryOperator 0x55cffa5344e0 <bfd/compress.c:630:7, /home/analyzer/.local/lib/clang/15.0.0/include/stddef.h:89:25> 'int' '=='
| | | |-ImplicitCastExpr 0x55cffa5344b0 <bfd/compress.c:630:7> 'bfd_byte *' <LValueToRValue>
| | | | `-DeclRefExpr 0x55cffa534410 <col:7> 'bfd_byte *' lvalue Var 0x55cffa533430 'uncompressed_buffer' 'bfd_byte *'
| | | `-ImplicitCastExpr 0x55cffa5344c8 </home/analyzer/.local/lib/clang/15.0.0/include/stddef.h:89:16, col:25> 'bfd_byte *' <BitCast>
| | | `-ParenExpr 0x55cffa534490 <col:16, col:25> 'void *'
| | | `-CStyleCastExpr 0x55cffa534468 <col:17, col:24> 'void *' <NullToPointer>
| | | `-IntegerLiteral 0x55cffa534430 <col:24> 'int' 0
It seems to be a problem with .c files but not .cpp files. If I save my test code as C file and generate compile_commands.json
, the checker also fails to match the equal null expersion.
I also tried to run clang-query on the source code but it also failed, so it may not be a problem specific to clang-tidy.
Update: this could be fixed by wrapping nullPointerConstant
with cStyleCastExpr
or applying ignoringCasts
to the node.