Enlico
Enlico

Reputation: 28470

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?

Upvotes: 0

Views: 79

Answers (1)

Caleth
Caleth

Reputation: 63039

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).

Upvotes: 2

Related Questions