There does not appear to be a warning option built in to gcc or
clang that does what is requested. However, we can use
clang-query
instead.
Below is a clang-query command that will report comparison of
32-bit and 64-bit integers, on the assumption that int is 32 bits and
long is 64 bits. (More about that below.)
#!/bin/sh
PATH=$HOME/opt/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH
# In this query, the comments are ignored because clang-query (not the
# shell) recognizes and discards them.
query='m
binaryOperator( # Find a binary operator expression
anyOf( # such that any of:
hasOperatorName("<"), # is operator <, or
hasOperatorName("<="), # is operator <=, or
hasOperatorName(">"), # is operator >, or
hasOperatorName(">="), # is operator >=, or
hasOperatorName("=="), # is operator ==, or
hasOperatorName("!=") # is operator !=;
),
hasEitherOperand( # and where either operand
implicitCastExpr( # is an implicit cast
has( # from
expr( # an expression
hasType( # whose type
hasCanonicalType( # after resolving typedefs
anyOf( # is either
asString("int"), # int or
asString("unsigned int") # unsigned int,
)
)
),
unless( # unless that expression
integerLiteral() # is an integer literal,
)
)
),
hasImplicitDestinationType( # and to a type
hasCanonicalType( # that after typedefs
anyOf( # is either
asString("long"), # long or
asString("unsigned long") # unsigned long.
)
)
)
).bind("operand")
)
)
'
# Run the query on test.c.
clang-query \
-c="set bind-root false" \
-c="$query" \
test.c -- -w
# EOF
When run on the following test.c it reports all of the indicated cases:
// test.c
// Demonstrate reporting comparisons of different-size operands.
#include <stddef.h> // size_t
#include <stdint.h> // int32_t, etc.
void test(int32_t i32, int64_t i64, uint32_t u32, uint64_t u64)
{
i32 < i32; // Not reported: same sizes.
i32 < i64; // reported
i64 < i64;
u32 < u32;
u32 < u64; // reported
u64 < u64;
i32 < u64; // reported
u32 < i64; // reported
i32 <= i64; // reported
i64 > i32; // reported
i64 >= i32; // reported
i32 == i64; // reported
u64 != u32; // reported
i32 + i64; // Not reported: not a comparison operator.
((int64_t)i32) < i64; // Not reported: explicit cast.
u64 < 3; // Not reported: comparison with integer literal.
// Example #1 in question.
size_t n = 0;
for (unsigned int i = 0; i < n; i++) {} // reported
}
// Example #2 in question.
void f(uint64_t n)
{
for (uint32_t i = 0; i < n; ++i) { // reported
}
}
// EOF
Some details about the clang-query command:
-
The command passes
-wtoclang-queryto suppress other warnings.
That’s just because I wrote the test in a way that provokes warnings
about unused values, and is not necessary with normal code. -
It passes
set bind-root falseso the only reported site is the
operand of interest rather than also reporting the entire expression. -
Unfortunately it is not possible to have the query also print the
names of the types involved. Attempting to do so with a binding
causesclang-queryto complain, “Matcher does not support binding.”
The unsatisfying aspect of the query is it explicitly lists the source
and destination types. Unfortunately, clang-query does not have a
matcher to, say, report any 32-bit type, so they have to be listed
individually. You might want to add [unsigned] long long on the
destination side. You might also need to remove [unsigned] long if running this
code with compiler options that target an IL32 platform like Windows.
Relatedly, note that clang-query accepts compiler options after
the --, or alternatively in a
compile_commands.json
file.
Unfortunately there isn’t dedicated documentation of the clang-query
command line, and even its --help does not mention the -- command
line option. The best I can link is the
documentation for libtooling,
as clang-query uses that library internally for command line
processing.
Finally, I’ll note that I haven’t done any “tuning” of this query on
real code. It is likely to produce a lot of noise, and will need
further tweaking. For a tutorial on how to work with clang-query,
I recommend the blog post
Exploring Clang Tooling Part 2: Examining the Clang AST with clang-query
by Stephen Kelly. There is also the
AST Matcher Reference,
but the documentation there is quite terse.