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 -w
to clang-query
to 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 false
so 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
causes clang-query
to 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.