c++clangabstract-syntax-treelibtoolingclang-query

How do I match builtin -> usages?


Take this silly C++ source file:

#include <optional>

struct Foo {
    int hello() const;
};

struct P1 {
    P1* another() const;
    int give() const;
};

auto fun() {
    return std::make_optional(std::make_optional(Foo{}));
}

int main()
{
    int * x = new int{3};
    P1 * p{};
    (*p->another()).another()->another();
    p->another()->give();
    p->give();
    return *x + (*fun())->hello() + p->give();
}

If I execute the following command on it,

clang-query -c "match expr(
    anyOf(
        unaryOperator(
            hasOperatorName(\"*\")
        ),
        unaryOperator(
            hasOperatorName(\"->\")
        ),
        cxxOperatorCallExpr(
            hasOverloadedOperatorName(\"*\")
        ),
        cxxOperatorCallExpr(
            hasOverloadedOperatorName(\"->\")
        )
    ), isExpansionInMainFile()
)" debugging.cpp | less

I get an output that ends like this (I've filtered out all matches happening in included headers):

/home/enrico/debugging.cpp:20:6: note: "root" binds here
   20 |     (*p->another()).another()->another();
      |      ^~~~~~~~~~~~~

Match #55:

/home/enrico/debugging.cpp:23:12: note: "root" binds here
   23 |     return *x + (*fun())->hello() + p->give();
      |            ^~

Match #56:

/home/enrico/debugging.cpp:23:17: note: "root" binds here
   23 |     return *x + (*fun())->hello() + p->give();
      |                 ^~~~~~~~~~

Match #57:

/home/enrico/debugging.cpp:23:18: note: "root" binds here
   23 |     return *x + (*fun())->hello() + p->give();
      |                  ^~~~~~
57 matches.

Which shows that the built-in -> has not been matched, whereas both the built-in and overloaded *s were matched.

What am I doing wrong?


Solution

  • Clang's AST labels unary * as a unary operator, but doesn't label -> as an operator at all.

    I don't see a clean way of finding every use of ->, without cases of implicit this, but if that is acceptable then you'd need

    anyOf(
        unaryOperator(
            hasOperatorName(\"*\")
        ),
        cxxOperatorCallExpr(
            hasOverloadedOperatorName(\"*\")
        ),
        memberExpr(
            isArrow()
        ), 
        cxxDependentScopeMemberExpr(
            isArrow()
        ),
        unresolvedMemberExpr(
            isArrow()
        )
    )
    

    which, for the file in the question would produce

    Match #172:
    
    /path/to/file.cpp:20:5: note: "root" binds here
       20 |     (*p->another()).another()->another();
          |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Match #173:
    
    /path/to/file.cpp:20:6: note: "root" binds here
       20 |     (*p->another()).another()->another();
          |      ^~~~~~~~~~~~~
    
    Match #174:
    
    /path/to/file.cpp:20:7: note: "root" binds here
       20 |     (*p->another()).another()->another();
          |       ^~~~~~~~~~
    
    Match #175:
    
    /path/to/file.cpp:21:5: note: "root" binds here
       21 |     p->another()->give();
          |     ^~~~~~~~~~~~~~~~~~
    
    Match #176:
    
    /path/to/file.cpp:21:5: note: "root" binds here
       21 |     p->another()->give();
          |     ^~~~~~~~~~
    
    Match #177:
    
    /path/to/file.cpp:22:5: note: "root" binds here
       22 |     p->give();
          |     ^~~~~~~
    
    Match #178:
    
    /path/to/file.cpp:23:12: note: "root" binds here
       23 |     return *x + (*fun())->hello() + p->give();
          |            ^~
    
    Match #179:
    
    /path/to/file.cpp:23:17: note: "root" binds here
       23 |     return *x + (*fun())->hello() + p->give();
          |                 ^~~~~~~~~~~~~~~
    
    Match #180:
    
    /path/to/file.cpp:23:18: note: "root" binds here
       23 |     return *x + (*fun())->hello() + p->give();
          |                  ^~~~~~
    
    Match #181:
    
    /path/to/file.cpp:23:37: note: "root" binds here
       23 |     return *x + (*fun())->hello() + p->give();
          |                                     ^~~~~~~
    181 matches.
    

    (having excluded all the matches happening in library code rather than in your file).