Skip to content

Instantly share code, notes, and snippets.

@christophercrouzet
Last active January 14, 2022 07:50
Show Gist options
  • Save christophercrouzet/5d851d781f336ad8fc3b3cc624a3f6b9 to your computer and use it in GitHub Desktop.
Save christophercrouzet/5d851d781f336ad8fc3b3cc624a3f6b9 to your computer and use it in GitHub Desktop.
Matching types with the Clang AST

Using the tmp.cpp snippet below and clang-query with the following query...

// single line:

match typeLoc(isExpansionInMainFile(), loc(qualType(hasDeclaration(decl(hasAncestor(namespaceDecl(hasName("std"))))))))


// prettified:

match typeLoc(
    isExpansionInMainFile(),
    loc(
        qualType(
            hasDeclaration(
                decl(
                    hasAncestor(
                        namespaceDecl(
                            hasName("std")
                        )
                    )
                )
            )
        )
    )
)

... I'm expecting to have the string type locations at lines 8 (string foo) and 13 (const string &s) to be returned, and that's it.

However, when I use set traversal AsIs, I get 3 matches...

Match #1:

tmp.cpp:8:5: note: "root" binds here
    string foo;
    ^~~~~~

Match #2:

tmp.cpp:11:17: note: "root" binds here
    auto fn = [&foo, &bar]()
                ^~~

Match #3:

tmp.cpp:13:20: note: "root" binds here
        for (const string &s : {"hello", "world"})
                   ^~~~~~

... with the second one pointing to something that I would assume to be an expression (declRefExpr?) rather than a type.

And when ignoring the implicit nodes using set traversal IgnoreUnlessSpelledInSource, this time I get a single match...

Match #1:

tmp.cpp:8:5: note: "root" binds here
    string foo;
    ^~~~~~

... which is omitting the type in the for loop statement at line 13.

I have also attached a dump of the AST if it can be of any help.

The question is: what's going on?

|-UsingDirectiveDecl 0x2716b50 <tmp.cpp:3:1, col:17> col:17 Namespace 0x1f43bf0 'std'
`-FunctionDecl 0x2716bd0 <line:5:1, line:18:1> line:6:1 main 'int ()'
`-CompoundStmt 0x271bd80 <line:7:1, line:18:1>
|-DeclStmt 0x2718fb8 <line:8:5, col:15>
| `-VarDecl 0x2716c80 <col:5, col:12> col:12 used foo 'std::string':'std::basic_string<char>' callinit destroyed
| `-CXXConstructExpr 0x2718f90 <col:12> 'std::string':'std::basic_string<char>' 'void () noexcept(is_nothrow_default_constructible<allocator<char>>::value)'
|-DeclStmt 0x2719050 <line:9:5, col:12>
| `-VarDecl 0x2718fe8 <col:5, col:9> col:9 used bar 'int'
|-DeclStmt 0x271bd38 <line:11:5, line:15:6>
| `-VarDecl 0x27190a8 <line:11:5, line:15:5> line:11:10 fn '(lambda at tmp.cpp:11:15)':'(lambda at tmp.cpp:11:15)' cinit
| `-ExprWithCleanups 0x271bd20 <col:15, line:15:5> '(lambda at tmp.cpp:11:15)':'(lambda at tmp.cpp:11:15)'
| `-CXXConstructExpr 0x271bcf0 <line:11:15, line:15:5> '(lambda at tmp.cpp:11:15)':'(lambda at tmp.cpp:11:15)' 'void ((lambda at tmp.cpp:11:15) &&) noexcept' elidable
| `-MaterializeTemporaryExpr 0x271b9c8 <line:11:15, line:15:5> '(lambda at tmp.cpp:11:15)' xvalue
| `-LambdaExpr 0x271b438 <line:11:15, line:15:5> '(lambda at tmp.cpp:11:15)'
| |-CXXRecordDecl 0x2719188 <line:11:15> col:15 implicit class definition
| | |-DefinitionData lambda pass_in_registers trivially_copyable can_const_default_init
| | | |-DefaultConstructor
| | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
| | | |-MoveConstructor exists simple trivial
| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment
| | | `-Destructor simple irrelevant trivial
| | |-CXXMethodDecl 0x27192c0 <col:28, line:15:5> line:11:15 operator() 'void () const' inline
| | | `-CompoundStmt 0x271b200 <line:12:5, line:15:5>
| | | `-CXXForRangeStmt 0x271b190 <line:13:9, line:14:10>
| | | |-<<<NULL>>>
| | | |-DeclStmt 0x271a680 <line:13:32>
| | | | `-VarDecl 0x2719530 <col:32, col:49> col:32 implicit used __range1 'std::initializer_list<const char *> &&' cinit
| | | | `-ExprWithCleanups 0x271a610 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' xvalue
| | | | `-MaterializeTemporaryExpr 0x271a568 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' xvalue extended by Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | | | `-CXXStdInitializerListExpr 0x271a550 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>'
| | | | `-MaterializeTemporaryExpr 0x271a538 <col:32, col:49> 'const char *const[2]' xvalue extended by Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | | | `-InitListExpr 0x271a4b8 <col:32, col:49> 'const char *const[2]'
| | | | |-ImplicitCastExpr 0x271a508 <col:33> 'const char *' <ArrayToPointerDecay>
| | | | | `-StringLiteral 0x2719398 <col:33> 'const char[6]' lvalue "hello"
| | | | `-ImplicitCastExpr 0x271a520 <col:42> 'const char *' <ArrayToPointerDecay>
| | | | `-StringLiteral 0x27193b8 <col:42> 'const char[6]' lvalue "world"
| | | |-DeclStmt 0x271ae30 <col:30>
| | | | `-VarDecl 0x271a718 <col:30> col:30 implicit used __begin1 'std::initializer_list<const char *>::const_iterator':'const char *const *' cinit
| | | | `-CXXMemberCallExpr 0x271a938 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | | `-MemberExpr 0x271a828 <col:30> '<bound member function type>' .begin 0x271a2a0
| | | | `-ImplicitCastExpr 0x271a958 <col:30> 'const std::initializer_list<const char *>' lvalue <NoOp>
| | | | `-DeclRefExpr 0x271a698 <col:30> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' lvalue Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | | |-DeclStmt 0x271ae48 <col:30>
| | | | `-VarDecl 0x271a7c0 <col:30> col:30 implicit used __end1 'std::initializer_list<const char *>::const_iterator':'const char *const *' cinit
| | | | `-CXXMemberCallExpr 0x271ad38 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | | `-MemberExpr 0x271ab30 <col:30> '<bound member function type>' .end 0x271a378
| | | | `-ImplicitCastExpr 0x271ad58 <col:30> 'const std::initializer_list<const char *>' lvalue <NoOp>
| | | | `-DeclRefExpr 0x271a6b8 <col:30> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' lvalue Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | | |-BinaryOperator 0x271aed0 <col:30> 'bool' '!='
| | | | |-ImplicitCastExpr 0x271aea0 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | | | | `-DeclRefExpr 0x271ae60 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | | `-ImplicitCastExpr 0x271aeb8 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | | | `-DeclRefExpr 0x271ae80 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a7c0 '__end1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | |-UnaryOperator 0x271af10 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue prefix '++'
| | | | `-DeclRefExpr 0x271aef0 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | |-DeclStmt 0x27194a0 <col:14, col:50>
| | | | `-VarDecl 0x2719438 <col:14, col:30> col:28 s 'const std::string &' cinit
| | | | `-ExprWithCleanups 0x271b120 <col:30> 'const std::string':'const std::basic_string<char>' lvalue
| | | | `-MaterializeTemporaryExpr 0x271b0c0 <col:30> 'const std::string':'const std::basic_string<char>' lvalue extended by Var 0x2719438 's' 'const std::string &'
| | | | `-CXXBindTemporaryExpr 0x271b0a0 <col:30> 'const std::string':'const std::basic_string<char>' (CXXTemporary 0x271b0a0)
| | | | `-CXXConstructExpr 0x271b060 <col:30> 'const std::string':'const std::basic_string<char>' 'void (const char *, const std::allocator<char> &)'
| | | | |-ImplicitCastExpr 0x271af78 <col:30> 'const char *':'const char *' <LValueToRValue>
| | | | | `-UnaryOperator 0x271af60 <col:30> 'const char *const':'const char *const' lvalue prefix '*' cannot overflow
| | | | | `-ImplicitCastExpr 0x271af48 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | | | | `-DeclRefExpr 0x271af28 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | | | `-CXXDefaultArgExpr 0x271b040 <<invalid sloc>> 'const std::allocator<char>':'const std::allocator<char>' lvalue
| | | `-CompoundStmt 0x271b1f0 <line:14:9, col:10>
| | |-FieldDecl 0x271b328 <line:11:17> col:17 implicit referenced 'std::string &'
| | |-FieldDecl 0x271b3b0 <col:23> col:23 implicit referenced 'int &'
| | |-CXXDestructorDecl 0x271b470 <col:15> col:15 implicit referenced ~ 'void () noexcept' inline default trivial
| | |-CXXConstructorDecl 0x271b680 <col:15> col:15 implicit constexpr 'void (const (lambda at tmp.cpp:11:15) &)' inline default trivial noexcept-unevaluated 0x271b680
| | | `-ParmVarDecl 0x271b798 <col:15> col:15 'const (lambda at tmp.cpp:11:15) &'
| | `-CXXConstructorDecl 0x271b838 <col:15> col:15 implicit used constexpr 'void ((lambda at tmp.cpp:11:15) &&) noexcept' inline default trivial
| | |-ParmVarDecl 0x271b948 <col:15> col:15 used '(lambda at tmp.cpp:11:15) &&'
| | |-CXXCtorInitializer Field 0x271b328 '' 'std::string &'
| | | `-MemberExpr 0x271bba0 <col:15> 'std::string':'std::basic_string<char>' lvalue . 0x271b328
| | | `-CXXStaticCastExpr 0x271bb70 <col:15> '(lambda at tmp.cpp:11:15)' xvalue static_cast<class (lambda at tmp.cpp:11:15) &&> <NoOp>
| | | `-DeclRefExpr 0x271bb40 <col:15> '(lambda at tmp.cpp:11:15)' lvalue ParmVar 0x271b948 '' '(lambda at tmp.cpp:11:15) &&'
| | |-CXXCtorInitializer Field 0x271b3b0 '' 'int &'
| | | `-MemberExpr 0x271bc68 <col:15> 'int' lvalue . 0x271b3b0
| | | `-CXXStaticCastExpr 0x271bc38 <col:15> '(lambda at tmp.cpp:11:15)' xvalue static_cast<class (lambda at tmp.cpp:11:15) &&> <NoOp>
| | | `-DeclRefExpr 0x271bc08 <col:15> '(lambda at tmp.cpp:11:15)' lvalue ParmVar 0x271b948 '' '(lambda at tmp.cpp:11:15) &&'
| | `-CompoundStmt 0x271bce0 <col:15>
| |-DeclRefExpr 0x271b2f8 <col:17> 'std::string':'std::basic_string<char>' lvalue Var 0x2716c80 'foo' 'std::string':'std::basic_string<char>'
| |-DeclRefExpr 0x271b378 <col:23> 'int' lvalue Var 0x2718fe8 'bar' 'int'
| `-CompoundStmt 0x271b200 <line:12:5, line:15:5>
| `-CXXForRangeStmt 0x271b190 <line:13:9, line:14:10>
| |-<<<NULL>>>
| |-DeclStmt 0x271a680 <line:13:32>
| | `-VarDecl 0x2719530 <col:32, col:49> col:32 implicit used __range1 'std::initializer_list<const char *> &&' cinit
| | `-ExprWithCleanups 0x271a610 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' xvalue
| | `-MaterializeTemporaryExpr 0x271a568 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' xvalue extended by Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | `-CXXStdInitializerListExpr 0x271a550 <col:32, col:49> 'std::initializer_list<const char *>':'std::initializer_list<const char *>'
| | `-MaterializeTemporaryExpr 0x271a538 <col:32, col:49> 'const char *const[2]' xvalue extended by Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| | `-InitListExpr 0x271a4b8 <col:32, col:49> 'const char *const[2]'
| | |-ImplicitCastExpr 0x271a508 <col:33> 'const char *' <ArrayToPointerDecay>
| | | `-StringLiteral 0x2719398 <col:33> 'const char[6]' lvalue "hello"
| | `-ImplicitCastExpr 0x271a520 <col:42> 'const char *' <ArrayToPointerDecay>
| | `-StringLiteral 0x27193b8 <col:42> 'const char[6]' lvalue "world"
| |-DeclStmt 0x271ae30 <col:30>
| | `-VarDecl 0x271a718 <col:30> col:30 implicit used __begin1 'std::initializer_list<const char *>::const_iterator':'const char *const *' cinit
| | `-CXXMemberCallExpr 0x271a938 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | `-MemberExpr 0x271a828 <col:30> '<bound member function type>' .begin 0x271a2a0
| | `-ImplicitCastExpr 0x271a958 <col:30> 'const std::initializer_list<const char *>' lvalue <NoOp>
| | `-DeclRefExpr 0x271a698 <col:30> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' lvalue Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| |-DeclStmt 0x271ae48 <col:30>
| | `-VarDecl 0x271a7c0 <col:30> col:30 implicit used __end1 'std::initializer_list<const char *>::const_iterator':'const char *const *' cinit
| | `-CXXMemberCallExpr 0x271ad38 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | `-MemberExpr 0x271ab30 <col:30> '<bound member function type>' .end 0x271a378
| | `-ImplicitCastExpr 0x271ad58 <col:30> 'const std::initializer_list<const char *>' lvalue <NoOp>
| | `-DeclRefExpr 0x271a6b8 <col:30> 'std::initializer_list<const char *>':'std::initializer_list<const char *>' lvalue Var 0x2719530 '__range1' 'std::initializer_list<const char *> &&'
| |-BinaryOperator 0x271aed0 <col:30> 'bool' '!='
| | |-ImplicitCastExpr 0x271aea0 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | | `-DeclRefExpr 0x271ae60 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | `-ImplicitCastExpr 0x271aeb8 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | `-DeclRefExpr 0x271ae80 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a7c0 '__end1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| |-UnaryOperator 0x271af10 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue prefix '++'
| | `-DeclRefExpr 0x271aef0 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| |-DeclStmt 0x27194a0 <col:14, col:50>
| | `-VarDecl 0x2719438 <col:14, col:30> col:28 s 'const std::string &' cinit
| | `-ExprWithCleanups 0x271b120 <col:30> 'const std::string':'const std::basic_string<char>' lvalue
| | `-MaterializeTemporaryExpr 0x271b0c0 <col:30> 'const std::string':'const std::basic_string<char>' lvalue extended by Var 0x2719438 's' 'const std::string &'
| | `-CXXBindTemporaryExpr 0x271b0a0 <col:30> 'const std::string':'const std::basic_string<char>' (CXXTemporary 0x271b0a0)
| | `-CXXConstructExpr 0x271b060 <col:30> 'const std::string':'const std::basic_string<char>' 'void (const char *, const std::allocator<char> &)'
| | |-ImplicitCastExpr 0x271af78 <col:30> 'const char *':'const char *' <LValueToRValue>
| | | `-UnaryOperator 0x271af60 <col:30> 'const char *const':'const char *const' lvalue prefix '*' cannot overflow
| | | `-ImplicitCastExpr 0x271af48 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' <LValueToRValue>
| | | `-DeclRefExpr 0x271af28 <col:30> 'std::initializer_list<const char *>::const_iterator':'const char *const *' lvalue Var 0x271a718 '__begin1' 'std::initializer_list<const char *>::const_iterator':'const char *const *'
| | `-CXXDefaultArgExpr 0x271b040 <<invalid sloc>> 'const std::allocator<char>':'const std::allocator<char>' lvalue
| `-CompoundStmt 0x271b1f0 <line:14:9, col:10>
`-ReturnStmt 0x271bd70 <line:17:5, col:12>
`-IntegerLiteral 0x271bd50 <col:12> 'int' 0
#include <string>
using namespace std;
int
main()
{
string foo;
int bar;
auto fn = [&foo, &bar]()
{
for (const string &s : {"hello", "world"})
{}
};
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment