I know that it will never be fully accurate without the headers because C++ isn't context free.
Using the classic example of 'A B(C);', it means that it can be recognized as a function declaration or an object definition. Either is fine for me. I just need the file totally parsed.
I am not interested in the semantic analysis of the code, just in the syntactic one and AFAIK the grammar of clang is one of the best.
The problem is that in some scenarios clang is avoiding some declarations when it doesn't know the types although I guess it can correctly parse it.
See the following case. Content of class.cpp:
A::A() { }
A::~A() { }
void A::B() { }
A::C() { }
Executing the clang command line application:
$ clang -Xclang -ast-dump -fsyntax-only class.cpp
it's just recognizing as AST nodes the constructor and the last method.
typedef char *__builtin_va_list;
int A() (CompoundStmt 0x9a6a570 <class.cpp:3:8, col:10>)
int C() (CompoundStmt 0x9a6a600 <class.cpp:9:8, col:10>)
Is there any way to get the complete AST tree?
Thanks!
I'm not sure if this is what you need:
test.cpp:
class A {
A();
~A();
void B();
void C();
};
A::A() {}
A::~A() {}
void A::B() {}
void A::C() {}
To dump the AST use: clang -Xclang -ast-dump -fsyntax-only test.cpp
TranslationUnitDecl 0x204f150 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x204f690 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x204f6f0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x204fab0 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-CXXRecordDecl 0x204fb00 <test.cpp:1:1, line:6:1> line:1:7 class A definition
| |-CXXRecordDecl 0x204fc10 <col:1, col:7> col:7 implicit class A
| |-CXXConstructorDecl 0x204fd10 <line:2:5, col:7> col:5 A 'void (void)'
| |-CXXDestructorDecl 0x2088cc0 <line:3:5, col:8> col:5 ~A 'void (void)'
| |-CXXMethodDecl 0x2088d90 <line:4:5, col:12> col:10 B 'void (void)'
| |-CXXMethodDecl 0x2088e50 <line:5:5, col:12> col:10 C 'void (void)'
| `-CXXConstructorDecl 0x2088f80 <line:1:7> col:7 implicit A 'void (const class A &)' inline noexcept-unevaluated 0x2088f80
| `-ParmVarDecl 0x20890c0 <col:7> col:7 'const class A &'
|-CXXConstructorDecl 0x2089120 parent 0x204fb00 prev 0x204fd10 <line:8:1, col:9> col:4 A 'void (void)'
| `-CompoundStmt 0x2089218 <col:8, col:9>
|-CXXDestructorDecl 0x2089280 parent 0x204fb00 prev 0x2088cc0 <line:9:1, col:10> col:4 ~A 'void (void)'
| `-CompoundStmt 0x2089368 <col:9, col:10>
|-CXXMethodDecl 0x20893c0 parent 0x204fb00 prev 0x2088d90 <line:10:1, col:14> col:9 B 'void (void)'
| `-CompoundStmt 0x2089498 <col:13, col:14>
`-CXXMethodDecl 0x20894f0 parent 0x204fb00 prev 0x2088e50 <line:11:1, col:14> col:9 C 'void (void)'
`-CompoundStmt 0x20895c8 <col:13, col:14>
Related
Observation: Clang AST don't contain any template specialisation if a header file is missing. It is true even when the template got nothing to do with the header file.
For example,
#include "unknown_header.h"
template<class T>
T add(T x, T y) {return x + y;}
void sum(void)
{
add<double>(5.0, 6.0);
}
generate the FunctionTemplateDecl instance, which doesn't contain specialisation for type double.
But if we get rid of "unknown_header.h", resulting in successful compilation, we can see the specialisation using type double. The specialisation can be seen below after removing the header.
|-FunctionTemplateDecl 0x55ec327d83e8 <tmp.cpp:1:1, line:2:31> col:3 add
| |-TemplateTypeParmDecl 0x55ec327d80f0 <line:1:10, col:16> col:16 referenced class depth 0 index 0 T
| |-FunctionDecl 0x55ec327d8348 <line:2:1, col:31> col:3 add 'T (T, T)'
| | |-ParmVarDecl 0x55ec327d81c0 <col:7, col:9> col:9 referenced x 'T'
| | |-ParmVarDecl 0x55ec327d8238 <col:12, col:14> col:14 referenced y 'T'
| | `-CompoundStmt 0x55ec327d8580 <col:17, col:31>
| | `-ReturnStmt 0x55ec327d8570 <col:18, col:29>
| | `-BinaryOperator 0x55ec327d8550 <col:25, col:29> '<dependent type>' '+'
| | |-DeclRefExpr 0x55ec327d8510 <col:25> 'T' lvalue ParmVar 0x55ec327d81c0 'x' 'T'
| | `-DeclRefExpr 0x55ec327d8530 <col:29> 'T' lvalue ParmVar 0x55ec327d8238 'y' 'T'
| `-FunctionDecl 0x55ec327d8ae8 <col:1, col:31> col:3 used add 'double (double, double)'
| |-TemplateArgument type 'double'
| | `-BuiltinType 0x55ec327923c0 'double'
| |-ParmVarDecl 0x55ec327d8960 <col:7, col:9> col:9 used x 'double':'double'
| |-ParmVarDecl 0x55ec327d89d8 <col:12, col:14> col:14 used y 'double':'double'
| `-CompoundStmt 0x55ec327d8da8 <col:17, col:31>
| `-ReturnStmt 0x55ec327d8d98 <col:18, col:29>
| `-BinaryOperator 0x55ec327d8d78 <col:25, col:29> 'double' '+'
| |-ImplicitCastExpr 0x55ec327d8d48 <col:25> 'double':'double' <LValueToRValue>
| | `-DeclRefExpr 0x55ec327d8d08 <col:25> 'double':'double' lvalue ParmVar 0x55ec327d8960 'x' 'double':'double'
| `-ImplicitCastExpr 0x55ec327d8d60 <col:29> 'double':'double' <LValueToRValue>
| `-DeclRefExpr 0x55ec327d8d28 <col:29> 'double':'double' lvalue ParmVar 0x55ec327d89d8 'y' 'double':'double'
Question: What stops clang from generating the template specialisation if the header is missing?
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 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.
I would like to know if there is a tool / GCC compilation switch one could use to probably see the trace / output of the GCC compiler before runtime. Generally , I would appreciate a tool annotating how the GCC compiler parsed through my code showing all precomputed static values/types .
Example : Take for instance the case on the metaFactoril I wrote here, http://pastebin.com/N6eN9jNx .
Can some tool show the expansion of the recursive calls and substituted values when MetaFactorial<5>::value is called ?
gcc does allow dumping of intermediate files and various other diagnostic tools, but it's not really meant for "regular people" to read. There are more than a dozen -fdump-tree-* options, and I'd expect one of those does what you want. -fdump-tree-original looked good, but it just shows the 120 as a final result - I didn't try any other variant.
With clang++ -Xclang -ast-dump you get this (with colours), which is quite clear to read in my view.
TranslationUnitDecl 0x31853b0 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x31858f0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x3185950 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x3185d50 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-ClassTemplateDecl 0x3185eb0 <metafact.cpp:1:1, line:5:1> line:2:7 MetaFactorial
| |-NonTypeTemplateParmDecl 0x3185db0 <line:1:10, col:14> col:14 referenced 'int' n
| |-CXXRecordDecl 0x3185e20 <line:2:1, line:5:1> line:2:7 class MetaFactorial definition
| | |-CXXRecordDecl 0x31c77d0 <col:1, col:7> col:7 implicit class MetaFactorial
| | |-AccessSpecDecl 0x31c7860 <line:3:3, col:9> col:3 public
| | `-EnumDecl 0x31c7890 <line:4:5, col:47> col:5
| | `-EnumConstantDecl 0x31c7ba0 <col:10, col:42> col:10 value 'enum MetaFactorial::(anonymous at metafact.cpp:4:5)'
| | `-BinaryOperator 0x31c7b78 <col:18, col:42> '<dependent type>' '*'
| | |-DeclRefExpr 0x31c7940 <col:18> 'int' NonTypeTemplateParm 0x3185db0 'n' 'int'
| | `-DependentScopeDeclRefExpr 0x31c7b38 <col:22, col:42> '<dependent type>' lvalue
| |-ClassTemplateSpecialization 0x31c7c20 'MetaFactorial'
| |-ClassTemplateSpecializationDecl 0x31c83a0 <line:1:1, line:5:1> line:2:7 class MetaFactorial definition
| | |-TemplateArgument integral 5
| | |-CXXRecordDecl 0x31c8590 prev 0x31c83a0 <col:1, col:7> col:7 implicit class MetaFactorial
| | |-AccessSpecDecl 0x31c8620 <line:3:3, col:9> col:3 public
| | `-EnumDecl 0x31c8650 <line:4:5> col:5
| | `-EnumConstantDecl 0x31d1390 <col:10, col:42> col:10 referenced value 'enum MetaFactorial<5>::(anonymous at metafact.cpp:4:5)'
| | `-ImplicitCastExpr 0x31d13e0 <col:18, col:42> 'unsigned int' <IntegralCast>
| | `-BinaryOperator 0x31d1368 <col:18, col:42> 'int' '*'
| | |-SubstNonTypeTemplateParmExpr 0x31c8730 <col:18> 'int'
| | | `-IntegerLiteral 0x31c8710 <col:18> 'int' 5
| | `-ImplicitCastExpr 0x31d1350 <col:22, col:42> 'int' <IntegralCast>
| | `-DeclRefExpr 0x31d1318 <col:22, col:42> 'enum MetaFactorial<4>::(anonymous at metafact.cpp:4:5)' EnumConstant 0x31d12b0 'value' 'enum MetaFactorial<4>::(anonymous at metafact.cpp:4:5)'
| |-ClassTemplateSpecializationDecl 0x31ce740 <line:1:1, line:5:1> line:2:7 class MetaFactorial definition
| | |-TemplateArgument integral 4
| | |-CXXRecordDecl 0x31ce940 prev 0x31ce740 <col:1, col:7> col:7 implicit class MetaFactorial
| | |-AccessSpecDecl 0x31ce9d0 <line:3:3, col:9> col:3 public
| | `-EnumDecl 0x31cea00 <line:4:5> col:5
| | `-EnumConstantDecl 0x31d12b0 <col:10, col:42> col:10 referenced value 'enum MetaFactorial<4>::(anonymous at metafact.cpp:4:5)'
| | `-ImplicitCastExpr 0x31d1300 <col:18, col:42> 'unsigned int' <IntegralCast>
| | `-BinaryOperator 0x31d1288 <col:18, col:42> 'int' '*'
| | |-SubstNonTypeTemplateParmExpr 0x31ceae0 <col:18> 'int'
| | | `-IntegerLiteral 0x31ceac0 <col:18> 'int' 4
| | `-ImplicitCastExpr 0x31d1270 <col:22, col:42> 'int' <IntegralCast>
| | `-DeclRefExpr 0x31d1238 <col:22, col:42> 'enum MetaFactorial<3>::(anonymous at metafact.cpp:4:5)' EnumConstant 0x31d11d0 'value' 'enum MetaFactorial<3>::(anonymous at metafact.cpp:4:5)'
| |-ClassTemplateSpecializationDecl 0x31ceb80 <line:1:1, line:5:1> line:2:7 class MetaFactorial definition
| | |-TemplateArgument integral 3
| | |-CXXRecordDecl 0x31ced80 prev 0x31ceb80 <col:1, col:7> col:7 implicit class MetaFactorial
| | |-AccessSpecDecl 0x31cee10 <line:3:3, col:9> col:3 public
| | `-EnumDecl 0x31cee40 <line:4:5> col:5
| | `-EnumConstantDecl 0x31d11d0 <col:10, col:42> col:10 referenced value 'enum MetaFactorial<3>::(anonymous at metafact.cpp:4:5)'
| | `-ImplicitCastExpr 0x31d1220 <col:18, col:42> 'unsigned int' <IntegralCast>
| | `-BinaryOperator 0x31d11a8 <col:18, col:42> 'int' '*'
| | |-SubstNonTypeTemplateParmExpr 0x31cef20 <col:18> 'int'
| | | `-IntegerLiteral 0x31cef00 <col:18> 'int' 3
| | `-ImplicitCastExpr 0x31d1190 <col:22, col:42> 'int' <IntegralCast>
| | `-DeclRefExpr 0x31d1158 <col:22, col:42> 'enum MetaFactorial<2>::(anonymous at metafact.cpp:4:5)' EnumConstant 0x31d10f0 'value' 'enum MetaFactorial<2>::(anonymous at metafact.cpp:4:5)'
| |-ClassTemplateSpecializationDecl 0x31cefc0 <line:1:1, line:5:1> line:2:7 class MetaFactorial definition
| | |-TemplateArgument integral 2
| | |-CXXRecordDecl 0x31cf1c0 prev 0x31cefc0 <col:1, col:7> col:7 implicit class MetaFactorial
| | |-AccessSpecDecl 0x31cf250 <line:3:3, col:9> col:3 public
| | `-EnumDecl 0x31cf280 <line:4:5> col:5
| | `-EnumConstantDecl 0x31d10f0 <col:10, col:42> col:10 referenced value 'enum MetaFactorial<2>::(anonymous at metafact.cpp:4:5)'
| | `-ImplicitCastExpr 0x31d1140 <col:18, col:42> 'unsigned int' <IntegralCast>
| | `-BinaryOperator 0x31d10c8 <col:18, col:42> 'int' '*'
| | |-SubstNonTypeTemplateParmExpr 0x31cf360 <col:18> 'int'
| | | `-IntegerLiteral 0x31cf340 <col:18> 'int' 2
| | `-ImplicitCastExpr 0x31d10b0 <col:22, col:42> 'int' <IntegralCast>
| | `-DeclRefExpr 0x31d1078 <col:22, col:42> 'enum MetaFactorial<1>::(anonymous at metafact.cpp:4:5)' EnumConstant 0x31d1010 'value' 'enum MetaFactorial<1>::(anonymous at metafact.cpp:4:5)'
| `-ClassTemplateSpecializationDecl 0x31cf400 <line:1:1, line:5:1> line:2:7 class MetaFactorial definition
| |-TemplateArgument integral 1
| |-CXXRecordDecl 0x31cf600 prev 0x31cf400 <col:1, col:7> col:7 implicit class MetaFactorial
| |-AccessSpecDecl 0x31cf690 <line:3:3, col:9> col:3 public
| `-EnumDecl 0x31d0d70 <line:4:5> col:5
| `-EnumConstantDecl 0x31d1010 <col:10, col:42> col:10 referenced value 'enum MetaFactorial<1>::(anonymous at metafact.cpp:4:5)'
| `-ImplicitCastExpr 0x31d1060 <col:18, col:42> 'unsigned int' <IntegralCast>
| `-BinaryOperator 0x31d0fe0 <col:18, col:42> 'int' '*'
| |-SubstNonTypeTemplateParmExpr 0x31d0e50 <col:18> 'int'
| | `-IntegerLiteral 0x31d0e30 <col:18> 'int' 1
| `-ImplicitCastExpr 0x31d0fc8 <col:22, col:42> 'int' <IntegralCast>
| `-DeclRefExpr 0x31d0f90 <col:22, col:42> 'enum MetaFactorial<0>::(anonymous at metafact.cpp:10:5)' EnumConstant 0x31c7fd0 'value' 'enum MetaFactorial<0>::(anonymous at metafact.cpp:10:5)'
|-ClassTemplateSpecializationDecl 0x31c7c20 <line:7:1, line:11:1> line:8:7 class MetaFactorial definition
| |-TemplateArgument integral 0
| |-CXXRecordDecl 0x31c7e40 <col:1, col:7> col:7 implicit class MetaFactorial
| |-AccessSpecDecl 0x31c7ed0 <line:9:3, col:9> col:3 public
| `-EnumDecl 0x31c7f00 <line:10:5, col:21> col:5
| `-EnumConstantDecl 0x31c7fd0 <col:11, col:19> col:11 referenced value 'enum MetaFactorial<0>::(anonymous at metafact.cpp:10:5)'
| `-ImplicitCastExpr 0x31c8020 <col:19> 'unsigned int' <IntegralCast>
| `-IntegerLiteral 0x31c7fb0 <col:19> 'int' 1
|-FunctionDecl 0x31c8110 <line:13:1, col:24> col:13 used my_func 'void (int)' extern
| `-ParmVarDecl 0x31c8050 <col:21> col:24 'int'
`-FunctionDecl 0x31c8210 <line:15:1, line:22:1> line:15:5 main 'int (void)'
`-CompoundStmt 0x31d1608 <col:12, line:22:1>
|-DeclStmt 0x31d1478 <line:17:3, col:34>
| `-VarDecl 0x31c8320 <col:3, col:29> col:7 used n 'int' cinit
| `-ImplicitCastExpr 0x31d1460 <col:11, col:29> 'int' <IntegralCast>
| `-DeclRefExpr 0x31d1428 <col:11, col:29> 'enum MetaFactorial<5>::(anonymous at metafact.cpp:4:5)' EnumConstant 0x31d1390 'value' 'enum MetaFactorial<5>::(anonymous at metafact.cpp:4:5)'
|-CallExpr 0x31d1580 <line:19:3, col:12> 'void'
| |-ImplicitCastExpr 0x31d1568 <col:3> 'void (*)(int)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x31d1510 <col:3> 'void (int)' lvalue Function 0x31c8110 'my_func' 'void (int)'
| `-ImplicitCastExpr 0x31d15b0 <col:11> 'int' <LValueToRValue>
| `-DeclRefExpr 0x31d14e8 <col:11> 'int' lvalue Var 0x31c8320 'n' 'int'
`-ReturnStmt 0x31d15e8 <line:21:3, col:10>
`-IntegerLiteral 0x31d15c8 <col:10> 'int' 0
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