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