Yamahari
Yamahari

Reputation: 2038

Query information about which data members function accesses

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

Answers (1)

Scott McPeak
Scott McPeak

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:

Upvotes: 2

Related Questions