I wanted to know if there is any compiler option or clang-tidy checks for this situation:
#include<iostream>
#include <ostream>
int glob = 12;
int& test(int d){
std::cout << &glob << std::endl;
return glob;
}
int main() {
auto a = test(10);
std::cout << &a << std::endl;
return 0;
}
If I suddenly forget to put the &
after the auto
, I will see different addresses in the output, and it is expected, but is there any compiler flag or clang-tidy flag that warns me about it?
I'm not aware of an existing warning or check that does this, but one
can use
clang-query
to check for the pattern of declaring a variable using type auto
(which is distinct from auto&
) and initializing it with a call to a
function that returns a reference.
The following shell script invokes clang-query
with such a
match expression:
#!/bin/sh
PATH=$HOME/opt/clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH
# In the following, the comments are seen and ignored by clang-query
# (not the shell). But beware that clang-query has odd rules for where
# comments can go, and poor error reporting if the rules are violated.
query='m
varDecl( # Report any variable declaration
hasType( # whose declared type
autoType() # is `auto`,
),
hasInitializer( # and that has an initializer
implicitCastExpr( # that is an implicit conversion
hasSourceExpression( # from
callExpr( # a function call expression
callee( # where the callee
functionDecl( # is a directly named function
returns( # whose return type
referenceType() # is a reference.
)
)
)
)
)
)
)
).bind("decl")
'
if [ "x$1" = "x" ]; then
echo "usage: $0 filename.cc -- <compile options like -I, etc.>"
exit 2
fi
# Run the query. Setting 'bind-root' to false means clang-query will
# not also print a redundant "root" binding.
clang-query \
-c="set bind-root false" \
-c="$query" \
"$@"
# EOF
On the following input:
// test.cc
// Cases for `auto` declaring non-ref initialized from ref.
int &getref();
int getnonref();
void g(int &refparam)
{
// ref -> ref
auto &r2r = getref(); // not reported: no conversion
// ref -> nonref
auto r2nr = getref(); // reported
// nonref -> nonref
auto nr2nr = getnonref(); // not reported: no conversion
// nonref -> ref: Syntax error.
//auto &nr2r = getnonref();
auto rp2nr = refparam; // not reported: not a call
}
// EOF
it produces the output:
$ ./cmd.sh test.cc --
Match #1:
$PWD/test.cc:14:3: note: "decl" binds here
auto r2nr = getref(); // reported
^~~~~~~~~~~~~~~~~~~~
1 match.
In the match expression, I restrict reporting to initializers that are
function calls. However, depending on what you're after, you might want
to delete the entire hasSourceExpression
matcher (so
implicitCastExpr
is just followed by ()
), thus reporting any auto
declaration initialized using an implicit conversion, since arguably any
such case is "surprising" and perhaps unintended.
I haven't done any large-scale testing or tuning of this matcher, so it might need further adjustment for use in production.
The procedure for integrating clang-query
into a build or test
workflow is basically the same as doing so for clang-tidy
, assuming
the latter is done by direct invocation.