Reputation: 2038
Assume the following example code
struct Bar {
int x, y, z;
};
int foo(const Bar& bar) {
return bar.x + bar.z;
}
Is there a way to automatically tell, that foo
accesses x
and z
from bar, but not y
? Maybe the compiler (any of gcc, clang, or msvc) can output something like that?
Upvotes: 1
Views: 90
Reputation: 12749
One tool that can do this sort of thing is clang-query
, which is
included in the
Clang+LLVM distribution. clang-query
searches the program's Abstract Syntax Tree (AST) for code that matches the given pattern(s).
For example, here is a shell script that invokes clang-query
to find member
accesses:
#!/bin/sh
# Run clang-query to find referenced members.
# Where I have Clang+LLVM installed.
CLANG_LLVM_DIR=$HOME/opt/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04
# This query reports member reference expressions (like "obj.field").
# It binds the name "field" to the matched field, and "func" to the
# function definition containing the reference.
query='m
memberExpr(
member(fieldDecl().bind("field")),
hasAncestor(functionDecl().bind("func")))'
# Run clang-query.
"$CLANG_LLVM_DIR"/bin/clang-query -c="$query" "$@"
# EOF
The core of query
is memberExpr
, a matcher that reports any
expression that is an access of a member. The rest is binding names to
relevant elements so they will be printed, as well as the member
expression itself.
When run on the input in the question, it prints:
$ ./cmd.sh test.cpp --
Match #1:
path_to_source/test.cpp:2:5: note: "field" binds here
int x, y, z;
^~~~~
path_to_source/test.cpp:5:1: note: "func" binds here
int foo(const Bar& bar) {
^~~~~~~~~~~~~~~~~~~~~~~~~
path_to_source/test.cpp:6:12: note: "root" binds here
return bar.x + bar.z;
^~~~~
Match #2:
path_to_source/test.cpp:2:5: note: "field" binds here
int x, y, z;
^~~~~~~~~~~
path_to_source/test.cpp:5:1: note: "func" binds here
int foo(const Bar& bar) {
^~~~~~~~~~~~~~~~~~~~~~~~~
path_to_source/test.cpp:6:20: note: "root" binds here
return bar.x + bar.z;
^~~~~
2 matches.
This is reporting both of the accesses.
If you want to do more than just print the accesses, you will probably
need to write something like a clang-tidy
check. Such a check uses AST
matchers like clang-query
, but you can also write arbitrary
C++ code to process the matches.
References:
Clang AST: MemberExpr, which is one of many Clang AST nodes. Although it is not necessary to look at the AST documentation to write matchers, I find it helpful since the matcher names are based on names in the main AST docs.
Stephen Kelly blog: Exploring Clang Tooling Part 2: Examining the Clang AST with clang-query: https://devblogs.microsoft.com/cppblog/exploring-clang-tooling-part-2-examining-the-clang-ast-with-clang-query
Firefox blog: Using clang-query
Upvotes: 2