I'd like to create an AST matcher for the following code snippet
#include <unordered_map>
int main() {
int a = 3333, b = 4444, c = 5555;
std::unordered_map<int *, int> unorderedMapPtr = {{&a, b}, {&b, c}};
for (auto mapIter : unorderedMapPtr) {
a = mapIter.second;
}
}
Specifically to recognize the key type (int *) in the std::unordered_map and in mapIter.
varDecl() recognizes mapIter, but further specializations have failed me. I'd like to keep it agnostic to int, and look for underlying pointer types.
The AST dump produces the following:
Dumping main:
FunctionDecl 0x7fdb7b0256e8 <tests/unorderedMap.cpp:2:1, line:8:1> line:2:5 main 'int ()'
`-CompoundStmt 0x7fdb7b183ac0 <col:12, line:8:1>
|-DeclStmt 0x7fdb7b025a00 <line:3:3, col:35>
| |-VarDecl 0x7fdb7b0257c0 <col:3, col:11> col:7 used a 'int' cinit
| | `-IntegerLiteral 0x7fdb7b025828 <col:11> 'int' 3333
| |-VarDecl 0x7fdb7b025880 <col:3, col:21> col:17 used b 'int' cinit
| | `-IntegerLiteral 0x7fdb7b0258e8 <col:21> 'int' 4444
| `-VarDecl 0x7fdb7b025940 <col:3, col:31> col:27 used c 'int' cinit
| `-IntegerLiteral 0x7fdb7b0259a8 <col:31> 'int' 5555
|-DeclStmt 0x7fdb7b15d7d0 <line:4:3, col:70>
| `-VarDecl 0x7fdb7b0263c0 <col:3, col:69> col:34 used unorderedMapPtr 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>' cinit destroyed
| `-ExprWithCleanups 0x7fdb7b15cbe0 <col:52, col:69> 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>'
| `-CXXConstructExpr 0x7fdb7b15cbb0 <col:52, col:69> 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>' 'void (initializer_list<std::unordered_map<int *, int, std::hash<int *>, std::equal_to<int *>, std::allocator<std::pair<int *const, int>>>::value_type>)' list std::initializer_list
| `-CXXStdInitializerListExpr 0x7fdb7b15b438 <col:52, col:69> 'initializer_list<std::unordered_map<int *, int, std::hash<int *>, std::equal_to<int *>, std::allocator<std::pair<int *const, int>>>::value_type>':'std::initializer_list<std::pair<int *const, int>>'
| `-MaterializeTemporaryExpr 0x7fdb7b15b420 <col:52, col:69> 'const std::pair<int *const, int>[2]' xvalue
| `-InitListExpr 0x7fdb7b155860 <col:52, col:69> 'const std::pair<int *const, int>[2]'
| |-CXXConstructExpr 0x7fdb7b159c60 <col:53, col:59> 'const std::pair<int *const, int>' 'void (int *&&, int &) noexcept((is_nothrow_constructible<first_type, int *>::value && is_nothrow_constructible<second_type, int &>::value))' list
| | |-MaterializeTemporaryExpr 0x7fdb7b1570f0 <col:54, col:55> 'int *' xvalue
| | | `-UnaryOperator 0x7fdb7b026448 <col:54, col:55> 'int *' prefix '&' cannot overflow
| | | `-DeclRefExpr 0x7fdb7b026428 <col:55> 'int' lvalue Var 0x7fdb7b0257c0 'a' 'int'
| | `-DeclRefExpr 0x7fdb7b026460 <col:58> 'int' lvalue Var 0x7fdb7b025880 'b' 'int'
| `-CXXConstructExpr 0x7fdb7b15b3e8 <col:62, col:68> 'const std::pair<int *const, int>' 'void (int *&&, int &) noexcept((is_nothrow_constructible<first_type, int *>::value && is_nothrow_constructible<second_type, int &>::value))' list
| |-MaterializeTemporaryExpr 0x7fdb7b15b3d0 <col:63, col:64> 'int *' xvalue
| | `-UnaryOperator 0x7fdb7b0264f0 <col:63, col:64> 'int *' prefix '&' cannot overflow
| | `-DeclRefExpr 0x7fdb7b0264d0 <col:64> 'int' lvalue Var 0x7fdb7b025880 'b' 'int'
| `-DeclRefExpr 0x7fdb7b026508 <col:67> 'int' lvalue Var 0x7fdb7b025940 'c' 'int'
`-CXXForRangeStmt 0x7fdb7b1839a0 <line:5:3, line:7:3>
|-<<<NULL>>>
|-DeclStmt 0x7fdb7b15db68 <line:5:23>
| `-VarDecl 0x7fdb7b15d920 <col:23> col:23 implicit used __range1 'std::unordered_map<int *, int> &' cinit
| `-DeclRefExpr 0x7fdb7b15d7e8 <col:23> 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>' lvalue Var 0x7fdb7b0263c0 'unorderedMapPtr' 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>'
|-DeclStmt 0x7fdb7b163928 <col:21>
| `-VarDecl 0x7fdb7b15dc08 <col:21> col:21 implicit used __begin1 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' cinit
| `-CXXMemberCallExpr 0x7fdb7b15ddc0 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
| `-MemberExpr 0x7fdb7b15dd90 <col:21> '<bound member function type>' .begin 0x7fdb7b125448
| `-DeclRefExpr 0x7fdb7b15db80 <col:21> 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>' lvalue Var 0x7fdb7b15d920 '__range1' 'std::unordered_map<int *, int> &'
|-DeclStmt 0x7fdb7b163940 <col:21>
| `-VarDecl 0x7fdb7b15dcb8 <col:21> col:21 implicit used __end1 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' cinit
| `-CXXMemberCallExpr 0x7fdb7b163838 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
| `-MemberExpr 0x7fdb7b163808 <col:21> '<bound member function type>' .end 0x7fdb7b125598
| `-DeclRefExpr 0x7fdb7b15dba0 <col:21> 'std::unordered_map<int *, int>':'std::unordered_map<int *, int>' lvalue Var 0x7fdb7b15d920 '__range1' 'std::unordered_map<int *, int> &'
|-CXXOperatorCallExpr 0x7fdb7b180840 <col:21> 'bool' '!=' adl
| |-ImplicitCastExpr 0x7fdb7b180828 <col:21> 'bool (*)(const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &, const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fdb7b1807e0 <col:21> 'bool (const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &, const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &)' lvalue Function 0x7fdb7b162430 'operator!=' 'bool (const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &, const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &)'
| |-ImplicitCastExpr 0x7fdb7b1807b0 <col:21> 'const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue <NoOp>
| | `-DeclRefExpr 0x7fdb7b163958 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue Var 0x7fdb7b15dc08 '__begin1' 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
| `-ImplicitCastExpr 0x7fdb7b1807c8 <col:21> 'const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue <NoOp>
| `-DeclRefExpr 0x7fdb7b163978 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue Var 0x7fdb7b15dcb8 '__end1' 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
|-CXXOperatorCallExpr 0x7fdb7b180a90 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue '++'
| |-ImplicitCastExpr 0x7fdb7b180a78 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &(*)()' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fdb7b180a28 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &()' lvalue CXXMethod 0x7fdb7b161cb0 'operator++' 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>> &()'
| `-DeclRefExpr 0x7fdb7b180a08 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue Var 0x7fdb7b15dc08 '__begin1' 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
|-DeclStmt 0x7fdb7b15d8b8 <col:8, col:38>
| `-VarDecl 0x7fdb7b15d850 <col:8, col:21> col:13 used mapIter 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::value_type':'std::pair<int *const, int>' cinit
| `-CXXConstructExpr 0x7fdb7b183958 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::value_type':'std::pair<int *const, int>' 'void (const std::pair<int *const, int> &) noexcept'
| `-ImplicitCastExpr 0x7fdb7b183780 <col:21> 'const std::pair<int *const, int>' lvalue <NoOp>
| `-CXXOperatorCallExpr 0x7fdb7b180be0 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::value_type':'std::pair<int *const, int>' lvalue '*'
| |-ImplicitCastExpr 0x7fdb7b180bc8 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::reference (*)() const' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fdb7b180b50 <col:21> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::reference () const' lvalue CXXMethod 0x7fdb7b161970 'operator*' 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::reference () const'
| `-ImplicitCastExpr 0x7fdb7b180b38 <col:21> 'const std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue <NoOp>
| `-DeclRefExpr 0x7fdb7b180ae8 <col:21> 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>' lvalue Var 0x7fdb7b15dc08 '__begin1' 'std::unordered_map<int *, int>::iterator':'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>'
`-CompoundStmt 0x7fdb7b183aa8 <col:40, line:7:3>
`-BinaryOperator 0x7fdb7b183a88 <line:6:5, col:17> 'int' lvalue '='
|-DeclRefExpr 0x7fdb7b183a00 <col:5> 'int' lvalue Var 0x7fdb7b0257c0 'a' 'int'
`-ImplicitCastExpr 0x7fdb7b183a70 <col:9, col:17> 'int':'int' <LValueToRValue>
`-MemberExpr 0x7fdb7b183a40 <col:9, col:17> 'int':'int' lvalue .second 0x7fdb7b135f88
`-DeclRefExpr 0x7fdb7b183a20 <col:9> 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::value_type':'std::pair<int *const, int>' lvalue Var 0x7fdb7b15d850 'mapIter' 'std::__hash_map_iterator<std::__hash_iterator<std::__hash_node<std::__hash_value_type<int *, int>, void *> *>>::value_type':'std::pair<int *const, int>'
I found two hacky ways to do so.
auto PairTypeM = varDecl(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom(hasName("std::pair")))))));
auto PointerTypeM = declRefExpr(hasType(hasCanonicalType(pointerType())));
return decl(forEachDescendant(PointerTypeM), PairTypeM);
This will catch decompositions of the type:
for (auto [myKey, myValue] : unorderedMapPtr) { // WARNING
... // unorderedMapPtr = std::unordered_map<int *, int>
}
But not of other types of cxxForRangeStmts.
The second method catches much more, but uses RegEx via matchesName().
// Replacing "std::__hash_value_type" -> ".*" enables matching
// std::unordered_set<std::pair> where pair = <T1 *, T2>
auto PointerHashM = varDecl(hasDescendant(cxxOperatorCallExpr(
hasOperatorName("*"), hasDescendant(declRefExpr(to(namedDecl(matchesName(
"std::__hash_value_type<.*\\*,.*>"))))))));
auto ContainerM = expr(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(isSameOrDerivedFrom(hasName("std::unordered_map")))))));
auto PointerIterM = stmt(cxxForRangeStmt(hasLoopVariable(PointerHashM),
hasRangeInit(ContainerM)))
.bind(WarnAtNode);
return decl(forEachDescendant(PointerIterM));
For context, I was trying to extend the functionality of the existing alpha checker for non-determinism.
I am working on a small project where I am trying to implement a refactoring tool using Clang's LibTooling. As part of this project, I need to find calls to a specific function from a specific library.
My attempts at using clang-query to come up with the AST matchers have failed.
However, I have discovered the following:
I can successfully find calls to non-template functions using my AST matchers.
A similar AST matcher query fails to find calls to a template function.
I have written the following test program to be able to post it here as a self-sufficient example, without depending on the library that I am working with:
template <typename T>
int func1(T param) {
return 4;
}
int main() {
int value = 4;
int result = func1(value);
return 0;
}
In order to observe the generated AST by Clang, I compiled the above program by using:
clang -Xclang -ast-dump -fsyntax-only test.cpp
The following is the generated AST:
TranslationUnitDecl 0xad9088 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0xad9960 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0xad9620 '__int128'
|-TypedefDecl 0xad99d0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0xad9640 'unsigned __int128'
|-TypedefDecl 0xad9d48 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'
| `-RecordType 0xad9ac0 '__NSConstantString_tag'
| `-CXXRecord 0xad9a28 '__NSConstantString_tag'
|-TypedefDecl 0xad9de0 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0xad9da0 'char *'
| `-BuiltinType 0xad9120 'char'
|-TypedefDecl 0xb16e98 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
| `-ConstantArrayType 0xb16e40 '__va_list_tag [1]' 1
| `-RecordType 0xad9ed0 '__va_list_tag'
| `-CXXRecord 0xad9e38 '__va_list_tag'
|-FunctionTemplateDecl 0xb17160 <test.cpp:1:1, line:4:1> line:2:5 func1
| |-TemplateTypeParmDecl 0xb16ef0 <line:1:11, col:20> col:20 referenced typename depth 0 index 0 T
| |-FunctionDecl 0xb170c0 <line:2:1, line:4:1> line:2:5 func1 'int (T)'
| | |-ParmVarDecl 0xb16fc8 <col:11, col:13> col:13 param 'T'
| | `-CompoundStmt 0xb17238 <col:20, line:4:1>
| | `-ReturnStmt 0xb17228 <line:3:5, col:12>
| | `-IntegerLiteral 0xb17208 <col:12> 'int' 4
| `-FunctionDecl 0xb17700 <line:2:1, line:4:1> line:2:5 used func1 'int (int)'
| |-TemplateArgument type 'int'
| |-ParmVarDecl 0xb17608 <col:11, col:13> col:13 param 'int':'int'
| `-CompoundStmt 0xb17960 <col:20, line:4:1>
| `-ReturnStmt 0xb17950 <line:3:5, col:12>
| `-IntegerLiteral 0xb17208 <col:12> 'int' 4
`-FunctionDecl 0xb172a0 <line:6:1, line:10:1> line:6:5 main 'int ()'
`-CompoundStmt 0xb17928 <col:12, line:10:1>
|-DeclStmt 0xb17408 <line:7:5, col:18>
| `-VarDecl 0xb17380 <col:5, col:17> col:9 used value 'int' cinit
| `-IntegerLiteral 0xb173e8 <col:17> 'int' 4
|-DeclStmt 0xb178e0 <line:8:5, col:30>
| `-VarDecl 0xb17438 <col:5, col:29> col:9 result 'int' cinit
| `-CallExpr 0xb178a0 <col:18, col:29> 'int'
| |-ImplicitCastExpr 0xb17888 <col:18> 'int (*)(int)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0xb17800 <col:18> 'int (int)' lvalue Function 0xb17700 'func1' 'int (int)' (FunctionTemplate 0xb17160 'func1')
| `-ImplicitCastExpr 0xb178c8 <col:24> 'int' <LValueToRValue>
| `-DeclRefExpr 0xb174e8 <col:24> 'int' lvalue Var 0xb17380 'value' 'int'
`-ReturnStmt 0xb17918 <line:9:5, col:12>
`-IntegerLiteral 0xb178f8 <col:12> 'int' 0
Now, based on what I could find from Introduction to the Clang AST and AST Matcher Reference, I came up with the following two AST matchers and tested in clang-query:
clang-query> match callExpr(callee(functionTemplateDecl(hasName("func1"))))
0 matches.
clang-query> match callExpr(hasDeclaration(functionTemplateDecl(hasName("func1"))))
0 matches.
They both return 0 matches. The same queries work if I change func1 so that it is not a template function, and if I change functionTemplateDecl to functionDecl.
Any ideas what's going on?
if you use functionDecl in your matcher, it should work on your template code as well.
1 template <typename T>
2 int func1(T param) {
3 return 4;
4 }
5
6 int func1(int param1, int param2) {
7 return 5;
8 }
9
10 int main() {
11 int value = 4;
12 int result = func1(value);
13 int result2 = func1(value, value);
14 return 0;
15 }
clang-query> match callExpr(callee(functionDecl(hasName("func1"))))
Match #1:
/.../test.cpp:12:18: note: "root" binds here
int result = func1(value);
^~~~~~~~~~~~
Match #2:
/.../test.cpp:13:19: note: "root" binds here
int result2 = func1(value, value);
^~~~~~~~~~~~~~~~~~~
2 matches.
if you want to go for the template instantiations, add a Narrowing matcher:
clang-query> match callExpr(callee(functionDecl(hasName("func1"), isTemplateInstantiation())))
Match #1:
/.../test.cpp:12:18: note: "root" binds here
int result = func1(value);
^~~~~~~~~~~~
1 match.
I didn't understand why I have this error:
error: assigning to 'mapped_type' (aka 'int ()()') from incompatible type 'int (Rsh::)(*)'
All lib needed is already included in another .h
I have an Rsh.h with:
class Rsh
{
private:
map<string, int(*)(const string &, FILE*)> supportedCmds;
public:
Rsh ();
~Rsh ();
void setup ();
};
And a Rsh.cpp with :
void Rsh::setup()
{
// Fill up list of supported commands
supportedCmds["cd"] = &Rsh::ashCd;
supportedCmds["ls"] = &Rsh::ashLs;
supportedCmds["pwd"] = &Rsh::ashPwd;
supportedCmds["history"] = &Rsh::ashHistory;
supportedCmds["exit"] = NULL;
}
And the output g++ message is this :
src/Rsh.cpp:24:26: error: assigning to 'mapped_type' (aka 'int (*)(const
std::__1::basic_string<char> &, __sFILE *)') from incompatible type 'int (Rsh::*)(const
string &, FILE *)'
supportedCmds["cd"] = &Rsh::ashCd;
^~~~~~~~~~~
src/Rsh.cpp:25:26: error: assigning to 'mapped_type' (aka 'int (*)(const
std::__1::basic_string<char> &, __sFILE *)') from incompatible type 'int (Rsh::*)(const
string &, FILE *)'
supportedCmds["ls"] = &Rsh::ashLs;
^~~~~~~~~~~
src/Rsh.cpp:26:26: error: assigning to 'mapped_type' (aka 'int (*)(const
std::__1::basic_string<char> &, __sFILE *)') from incompatible type 'int (Rsh::*)(const
string &, FILE *)'
supportedCmds["pwd"] = &Rsh::ashPwd;
^~~~~~~~~~~~
src/Rsh.cpp:27:29: error: assigning to 'mapped_type' (aka 'int (*)(const
std::__1::basic_string<char> &, __sFILE *)') from incompatible type 'int (Rsh::*)(const
string &, FILE *)'
supportedCmds["history"] = &Rsh::ashHistory;
^~~~~~~~~~~~~~~~
4 errors generated.
Any suggestions?
Fixed with : map<string, int(Rsh::*)(const string &, FILE*)> supportedCmds;
Thank's to #WhiZTiM
I would like to extract the types and names of the entire caller object and parameter types of member call expressions made from the A::WriteData method definition.
class ostream {
public:
void write(char* c, unsigned int i) {
}
};
struct StringWrapper {
char c[30];
void Write(ostream& os) {
os.write((char*)&c, sizeof(c));
}
};
struct DoubleWrapper {
double d;
void Write(ostream& os) {
os.write((char*)&d, sizeof(d));
}
};
struct Data {
DoubleWrapper dw;
int i;
StringWrapper sw;
};
class A {
public:
void WriteData(ostream& os);
private:
Data* d;
};
void A::WriteData(ostream& os) {
os.write((char*)&d->i, sizeof(d->i));
d->dw.Write(os);
d->sw.Write(os);
}
The relevant part of clang's AST for the WriteData method is here-
`-CXXMethodDecl 0x1221980 parent 0x1221628 prev 0x12217f8 <line:29:1, line:33:1> line:29:9 WriteData 'void (class ostream &) __attribute__((thiscall))'
|-ParmVarDecl 0x1221908 <col:19, col:28> col:28 used os 'class ostream &'
`-CompoundStmt 0x1221d90 <col:32, line:33:1>
|-CXXMemberCallExpr 0x1221bc8 <line:30:5, col:40> 'void'
| |-MemberExpr 0x1221a30 <col:5, col:8> '<bound member function type>' .write 0x418a10
| | `-DeclRefExpr 0x1221a18 <col:5> 'class ostream' lvalue ParmVar 0x1221908 'os' 'class ostream &'
| |-CStyleCastExpr 0x1221b10 <col:14, col:25> 'char *' <BitCast>
| | `-UnaryOperator 0x1221ae8 <col:21, col:25> 'int *' prefix '&'
| | `-MemberExpr 0x1221aa0 <col:22, col:25> 'int' lvalue ->i 0x12215b0
| | `-ImplicitCastExpr 0x1221a90 <col:22> 'struct Data *' <LValueToRValue>
| | `-MemberExpr 0x1221a68 <col:22> 'struct Data *' lvalue ->d 0x12218a8
| | `-CXXThisExpr 0x1221a58 <col:22> 'class A *' this
| `-UnaryExprOrTypeTraitExpr 0x1221bb0 <col:28, col:39> 'unsigned int' sizeof
| `-ParenExpr 0x1221b98 <col:34, col:39> 'int' lvalue
| `-MemberExpr 0x1221b70 <col:35, col:38> 'int' lvalue ->i 0x12215b0
| `-ImplicitCastExpr 0x1221b60 <col:35> 'struct Data *' <LValueToRValue>
| `-MemberExpr 0x1221b38 <col:35> 'struct Data *' lvalue ->d 0x12218a8
| `-CXXThisExpr 0x1221b28 <col:35> 'class A *' this
|-CXXMemberCallExpr 0x1221ca0 <line:31:5, col:19> 'void'
| |-MemberExpr 0x1221c60 <col:5, col:11> '<bound member function type>' .Write 0x1221248
| | `-MemberExpr 0x1221c38 <col:5, col:8> 'struct DoubleWrapper' lvalue ->dw 0x1221570
| | `-ImplicitCastExpr 0x1221c28 <col:5> 'struct Data *' <LValueToRValue>
| | `-MemberExpr 0x1221c00 <col:5> 'struct Data *' lvalue ->d 0x12218a8
| | `-CXXThisExpr 0x1221bf0 <col:5> 'class A *' this
| `-DeclRefExpr 0x1221c88 <col:17> 'class ostream' lvalue ParmVar 0x1221908 'os' 'class ostream &'
`-CXXMemberCallExpr 0x1221d70 <line:32:5, col:19> 'void'
|-MemberExpr 0x1221d30 <col:5, col:11> '<bound member function type>' .Write 0x418d20
| `-MemberExpr 0x1221d08 <col:5, col:8> 'struct StringWrapper' lvalue ->sw 0x12215e8
| `-ImplicitCastExpr 0x1221cf8 <col:5> 'struct Data *' <LValueToRValue>
| `-MemberExpr 0x1221cd0 <col:5> 'struct Data *' lvalue ->d 0x12218a8
| `-CXXThisExpr 0x1221cc0 <col:5> 'class A *' this
`-DeclRefExpr 0x1221d58 <col:17> 'class ostream' lvalue ParmVar 0x1221908 'os' 'class ostream &'
In this case, the information I want from my matcher would be:
caller=os: ostream ; params= &d->i: int*, sizeof(d->i): size_t
caller=d->dw: DoubleWrapper ; params= os: ostream
caller=d->sw: StringWrapper ; params= os: ostream
I've tried the following matcher:
cxxMemberCallExpr(
allOf(
hasAncestor(
cxxMethodDecl(isDefinition(),
hasName("WriteData"))),
anyOf(callee(cxxMethodDecl(hasName("WriteData"))),
callee(cxxMethodDecl(hasName("write"))))
)).bind("write-call-expr")
This gives me the correct statements that I want to extract.
I've extracted the following (invalid c++ follows):
Parameter types by getting the QualType of the parameters as a
string:
cxxMemberCallExpr->getDirectCallee()->parameters()
The argument names:
cxxMemberCallExpr->getArg(i)->printPretty(...) // **is there a better way to do this?**
Type of the object caller:
cxxMemberCallExpr->getImplicitObjectArgument()->getType().getAsString()
Object caller method:
dyn_cast<MemberExpr>(cxxMemberCallExpr->getCallee())
->getMemberNameInfo().getName().getAsString()
How do I get the object caller itself?
Is there a better way to get the argument names that are passed to the Write methods?
Looking at the AST, I'm not sure where to start.
The naïve approach of overriding each special memb func, and plonking a printf or cout in the body of each (e.g. C++: Implicit Member Functions here on SO; Vandevoorde and Josuttis call this "tracers"), does not appeal:
invasive, in terms of codebase delta
O(n), where n = |classes|, in terms of effort
possible observer effect
Giving -E (or -save-temps) to g++ causes the latter to emit a .ii file for every source .cpp (or .cxx) file; in such a .ii, the source will be annotated with the exact steps taken by the cpp preprocessor. I want, ideally, something similar but with source being instead annotated as
Foo baz;
//// Line 55, choosing Foo::Foo(); implicit; empty body
Foo qux(Bar::mkFoo(42));
//// Line 56, choosing Foo::Foo(int)
//// Line 56, choosing Foo::Foo(const Foo&)
//// Line 56, choosing Foo::~Foo(); implicit
Foo qux2(std::move(Bar::mkFoo(4.2)));
//// Line 57, choosing Foo::Foo(double)
//// Line 57, choosing Foo::Foo(Foo&&)
//// Line 57, choosing Foo::~Foo(); implicit
The ideal approach will show choices as made for the particular optimization options given to the compiler.
Anyone know of a way to achieve this desideratum with g++? I'm ready to start writing a g++ plugin, but figured to ask before reinventing this particular wheel.
If there's a way to do this with clang, that'd be good to know as well.
Many thanks in advance.
I would guess you can build something like this on top of clang. clang provides several libraries providing access to the internal data structures used, including an AST. The AST should contain lots of information, though, and it may not be obvious what's of interest.
For example, using this input
struct foo {
foo();
explicit foo(int);
foo(foo&&);
foo(foo const&);
~foo();
};
struct bar {
static auto mkfoo(int x) -> foo;
};
template <typename T>
auto move(T&& t) -> T&&;
int main() {
foo f0;
foo f1{bar::mkfoo(17)};
foo f2{move(bar::mkfoo(17))};
}
using the built-in command to dump the AST, i.e., using the command-line
clang -cc1 -std=c++11 -ast-dump ast.cpp
yields the following output below. If you look closely you will see most of the operations you are after. You may need to ignore the detail you are not interested, though.
TranslationUnitDecl 0x7fea3b82ccc0 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x7fea3b82d200 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x7fea3b82d260 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x7fea3b82d620 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-CXXRecordDecl 0x7fea3b82d670 <ast.cpp:1:1, line:7:1> line:1:8 referenced struct foo definition
| |-CXXRecordDecl 0x7fea3b82d780 <col:1, col:8> col:8 implicit referenced struct foo
| |-CXXConstructorDecl 0x7fea3b82d8b0 <line:2:5, col:9> col:5 used foo 'void (void)'
| |-CXXConstructorDecl 0x7fea3b873260 <line:3:5, col:21> col:14 foo 'void (int)'
| | `-ParmVarDecl 0x7fea3b82d980 <col:18> col:21 'int'
| |-CXXConstructorDecl 0x7fea3b873420 <line:4:5, col:14> col:5 used foo 'void (struct foo &&)'
| | `-ParmVarDecl 0x7fea3b873360 <col:9, col:12> col:14 'struct foo &&'
| |-CXXConstructorDecl 0x7fea3b8735e0 <line:5:5, col:19> col:5 foo 'void (const struct foo &)'
| | `-ParmVarDecl 0x7fea3b873520 <col:9, col:18> col:19 'const struct foo &'
| `-CXXDestructorDecl 0x7fea3b8736f0 <line:6:5, col:10> col:5 used ~foo 'void (void) noexcept'
|-CXXRecordDecl 0x7fea3b8737e0 <line:9:1, line:11:1> line:9:8 struct bar definition
| |-CXXRecordDecl 0x7fea3b8738f0 <col:1, col:8> col:8 implicit struct bar
| `-CXXMethodDecl 0x7fea3b873af0 <line:10:5, col:33> col:17 used mkfoo 'auto (int) -> struct foo' static
| `-ParmVarDecl 0x7fea3b873990 <col:23, col:27> col:27 x 'int'
|-FunctionTemplateDecl 0x7fea3b873e70 <line:13:1, line:14:22> col:6 move
| |-TemplateTypeParmDecl 0x7fea3b873ba0 <line:13:11, col:20> col:20 referenced typename T
| |-FunctionDecl 0x7fea3b873dd0 <line:14:1, col:22> col:6 move 'auto (T &&) -> T &&'
| | `-ParmVarDecl 0x7fea3b873cc0 <col:11, col:15> col:15 t 'T &&'
| `-FunctionDecl 0x7fea3b8757b0 <col:1, col:22> col:6 used move 'auto (struct foo &&) -> struct foo &&'
| |-TemplateArgument type 'struct foo'
| `-ParmVarDecl 0x7fea3b8756b0 <col:11, col:15> col:15 t 'struct foo &&'
`-FunctionDecl 0x7fea3b873f10 <line:16:1, line:20:1> line:16:5 main 'int (void)'
`-CompoundStmt 0x7fea3b875a48 <col:12, line:20:1>
|-DeclStmt 0x7fea3b8740e0 <line:17:5, col:11>
| `-VarDecl 0x7fea3b874020 <col:5, col:9> col:9 f0 'struct foo' callinit
| `-CXXConstructExpr 0x7fea3b874078 <col:9> 'struct foo' 'void (void)'
|-DeclStmt 0x7fea3b875398 <line:18:5, col:27>
| `-VarDecl 0x7fea3b874110 <col:5, col:26> col:9 f1 'struct foo' listinit
| `-ExprWithCleanups 0x7fea3b875380 <col:9, col:26> 'struct foo'
| `-CXXConstructExpr 0x7fea3b875348 <col:9, col:26> 'struct foo' 'void (struct foo &&)' elidable
| `-MaterializeTemporaryExpr 0x7fea3b875330 <col:12, col:25> 'struct foo' xvalue
| `-CXXBindTemporaryExpr 0x7fea3b8752c8 <col:12, col:25> 'struct foo' (CXXTemporary 0x7fea3b8752c0)
| `-CallExpr 0x7fea3b875290 <col:12, col:25> 'struct foo'
| |-ImplicitCastExpr 0x7fea3b875278 <col:12, col:17> 'auto (*)(int) -> struct foo' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fea3b8741b8 <col:12, col:17> 'auto (int) -> struct foo' lvalue CXXMethod 0x7fea3b873af0 'mkfoo' 'auto (int) -> struct foo'
| `-IntegerLiteral 0x7fea3b875200 <col:23> 'int' 17
`-DeclStmt 0x7fea3b875a30 <line:19:5, col:33>
`-VarDecl 0x7fea3b8753c0 <col:5, col:32> col:9 f2 'struct foo' listinit
`-ExprWithCleanups 0x7fea3b875a18 <col:9, col:32> 'struct foo'
`-CXXConstructExpr 0x7fea3b8759e0 <col:9, col:32> 'struct foo' 'void (struct foo &&)'
`-CallExpr 0x7fea3b875950 <col:12, col:31> 'struct foo':'struct foo' xvalue
|-ImplicitCastExpr 0x7fea3b875938 <col:12> 'auto (*)(struct foo &&) -> struct foo &&' <FunctionToPointerDecay>
| `-DeclRefExpr 0x7fea3b8758b0 <col:12> 'auto (struct foo &&) -> struct foo &&' lvalue Function 0x7fea3b8757b0 'move' 'auto (struct foo &&) -> struct foo &&' (FunctionTemplate 0x7fea3b873e70 'move')
`-MaterializeTemporaryExpr 0x7fea3b875980 <col:17, col:30> 'struct foo':'struct foo' xvalue
`-CXXBindTemporaryExpr 0x7fea3b875558 <col:17, col:30> 'struct foo' (CXXTemporary 0x7fea3b875550)
`-CallExpr 0x7fea3b875518 <col:17, col:30> 'struct foo'
|-ImplicitCastExpr 0x7fea3b875500 <col:17, col:22> 'auto (*)(int) -> struct foo' <FunctionToPointerDecay>
| `-DeclRefExpr 0x7fea3b8754a8 <col:17, col:22> 'auto (int) -> struct foo' lvalue CXXMethod 0x7fea3b873af0 'mkfoo' 'auto (int) -> struct foo'
`-IntegerLiteral 0x7fea3b8754e0 <col:28> 'int' 17