<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/83559>83559</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Pre-order on AST not preserved in `MatchFinder`
</td>
</tr>
<tr>
<th>Labels</th>
<td>
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
jirislaby
</td>
</tr>
</table>
<pre>
The docs of `MatchFinder` states:
> The order of matches is guaranteed to be equivalent to doing a pre-order traversal on the AST, and applying the matchers in the order in which they were added to the MatchFinder.
But if I run this checker:
```c++
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::ento;
namespace {
class MyChecker final : public Checker<check::EndOfTranslationUnit> {
public:
void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
AnalysisManager &A, BugReporter &BR) const;
};
class MatchCallback : public MatchFinder::MatchCallback {
public:
MatchCallback() {}
void run(const MatchFinder::MatchResult &res);
};
}
void MatchCallback::run(const MatchFinder::MatchResult &res)
{
if (auto RD = res.Nodes.getNodeAs<RecordDecl>("RD")) {
llvm::errs() << "RD:\n";
RD->dumpColor();
}
if (auto ME = res.Nodes.getNodeAs<MemberExpr>("ME")) {
llvm::errs() << "ME:\n";
ME->dumpColor();
}
if (auto ME = res.Nodes.getNodeAs<MemberExpr>("MESTORE")) {
llvm::errs() << "MESTORE:\n";
ME->dumpColor();
}
}
void MyChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
AnalysisManager &A,
BugReporter &BR) const
{
TU->dumpColor();
MatchFinder F;
MatchCallback CB;
F.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, recordDecl(isStruct()).bind("RD")),
&CB);
F.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, memberExpr().bind("ME")),
&CB);
F.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource,
binaryOperator(isAssignmentOperator(),
hasLHS(memberExpr().bind("MESTORE")))),
&CB);
F.matchAST(A.getASTContext());
}
extern "C" void clang_registerCheckers(CheckerRegistry ®istry) {
registry.addChecker<MyChecker>("jirislaby.PoBug", "XXX", "");
}
extern "C" const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING;
```
by:
```
clang -cc1 -analyze -load ./po-bug.so -analyzer-checker jirislaby.PoBug x.c
```
on this C file:
```
void put(unsigned short val, void *p)
{
struct {
unsigned short x;
} *pp = p;
pp->x = val;
}
```
I see:
```
TranslationUnitDecl 0x5619382fb8b8 <<invalid sloc>> <invalid sloc>
|...
`-FunctionDecl 0x56193835e5b8 <x.c:1:1, line:7:1> line:1:6 put 'void (unsigned short, void *)'
|-ParmVarDecl 0x56193835e460 <col:10, col:25> col:25 used val 'unsigned short'
|-ParmVarDecl 0x56193835e4e0 <col:30, col:36> col:36 used p 'void *'
`-CompoundStmt 0x56193835ea38 <line:2:1, line:7:1>
|-DeclStmt 0x56193835e960 <line:3:2, line:5:11>
| |-RecordDecl 0x56193835e6b8 <line:3:2, line:5:2> line:3:2 struct definition
| | `-FieldDecl 0x56193835e778 <line:4:3, col:18> col:18 referenced x 'unsigned short'
| `-VarDecl 0x56193835e890 <line:3:2, line:5:10> col:5 used pp 'struct (unnamed struct at x.c:3:2) *' cinit
| `-ImplicitCastExpr 0x56193835e930 <col:10> 'struct (unnamed struct at x.c:3:2) *' <BitCast>
| `-ImplicitCastExpr 0x56193835e918 <col:10> 'void *' <LValueToRValue>
| `-DeclRefExpr 0x56193835e8f8 <col:10> 'void *' lvalue ParmVar 0x56193835e4e0 'p' 'void *'
`-BinaryOperator 0x56193835ea18 <line:6:2, col:10> 'unsigned short' '='
|-MemberExpr 0x56193835e9b0 <col:2, col:6> 'unsigned short' lvalue ->x 0x56193835e778
| `-ImplicitCastExpr 0x56193835e998 <col:2> 'struct (unnamed struct at x.c:3:2) *' <LValueToRValue>
| `-DeclRefExpr 0x56193835e978 <col:2> 'struct (unnamed struct at x.c:3:2) *' lvalue Var 0x56193835e890 'pp' 'struct (unnamed struct at x.c:3:2) *'
`-ImplicitCastExpr 0x56193835ea00 <col:10> 'unsigned short' <LValueToRValue>
`-DeclRefExpr 0x56193835e9e0 <col:10> 'unsigned short' lvalue ParmVar 0x56193835e460 'val' 'unsigned short'
MESTORE:
MemberExpr 0x56193835e9b0 'unsigned short' lvalue ->x 0x56193835e778
`-ImplicitCastExpr 0x56193835e998 'struct (unnamed struct at x.c:3:2) *' <LValueToRValue>
`-DeclRefExpr 0x56193835e978 'struct (unnamed struct at x.c:3:2) *' lvalue Var 0x56193835e890 'pp' 'struct (unnamed struct at x.c:3:2) *'
RD:
RecordDecl 0x56193835e6b8 <x.c:3:2, line:5:2> line:3:2 struct definition
`-FieldDecl 0x56193835e778 <line:4:3, col:18> col:18 referenced x 'unsigned short'
ME:
MemberExpr 0x56193835e9b0 'unsigned short' lvalue ->x 0x56193835e778
`-ImplicitCastExpr 0x56193835e998 'struct (unnamed struct at x.c:3:2) *' <LValueToRValue>
`-DeclRefExpr 0x56193835e978 'struct (unnamed struct at x.c:3:2) *' lvalue Var 0x56193835e890 'pp' 'struct (unnamed struct at x.c:3:2) *'
```
That is `MemberExpr` (bound to `MESTORE`) is reported **before** `RecordDecl`.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsWVtz4jgW_jXKiwrKyODLAw_GwGxqO-kU0F2z-5KSbQGaEZJXkjPJ_votSQZfuCSb7p3dh-1Kg63LuXznO0fHGCtFd5yQKZjMwGR-hyu9F3L6G5VUMZy93WWieJtu9gQWIldQbCEIvAes8_2S8oJIEHhQaayJAn4CvDnwjp_-ApptQhZEmn0Hs4koSBXcVVhirgkpoBYwI5D8o6IvmBGuzUAhKN9BDEtJBm67lviFSIUZFBzqPYHJegNQCjEvIC5L9mY2mHGnRCpI3Tq3nXL4x57mezP0Bv8gkkBcFE67WdXyZ9j2YVZpSLfwHsrKiKMK5nuS_05k42vgub8coJn5c6PIpzxnVUEgQChnmO8AWibrzUNtXuuuVrsHCN3avNZY0zzhmL39k0iAlqmQxHw5gz69_wnr_ZpwRTV9Mfd2haLqAXO8-4zcpRRcE140tq3Ijiot39qy7GelTNg4PhBV4pxAJ9Of3ZxNgJ9gpZ-Pkf7IesK1OK1zn806ENYTOcNKwYe32my4pRwzCPwEllXGaA7TY_BTSwMne8GLr9uNxFwxrKng3zjVhvsnsW7ziTEQvghaOCJd3IuiXHClYW98TnIGAUo23wBKj57Epz_YCxwEKEhMisyq3YqUQmo3NlsBFEOroUEknPfQqaEwEKeYsQznv7eBaOe_BaG38orrwIs7CwGKjDFmeTjv1A4vtiDJip_guKxzRVTFtHFMEgVQfNmnrnQrumuIFfgJbbX4E3Yx3UKAIlxpAVdzCPw5lEQNH0VB1HBHtLlIFPDTFcmFLExMgb-wOKDV3KQHio-QtELM2MuhZrI0xcPB5qfAT6Hb6SdgknIjwO_sXM0HwF8U1aFMBRPSbe2sabBpG_-wuGH8AzlkRC5eS3ky_mHxWeMfFleNf1j8WcavN19XP-CB2_4T3LhE1Le0OXP85GfXDVM6rtWOK6tvVJSzfNh8u-17uyy4dIPLNjTdwpLO2nPLIS6K-kQFKKp7BAJQtPnr8_2OC0m-cUaUWpeEMVLc87WoZE5MVZRN_qGIqrWWVa5r61A8zKg5wHpZ2UPE-J7OeqH8EZsODTOtIS0rWun1H7aiF3OYUY7l29eSSKxt_KhKbMt4IFy3hs8ta2TssfrylzVA0U0Puzn4YWdPLtuGwPaFUWIyPllvUtOKvDZxvXYikFdNJDe5nAKE6gPatA_P0vYuRNY5aJK_19W408Bdd2oHPI6aYDStQyuf6_Jz6rWHT2JW7SwEtrD8-uuvzU2NzMc8cJUg32NZ-4HrBi15uv9OpKKCr7WkfOe6flsu0y_J4y_PyWPy5W9_X6yek6f75--L1fr-6-PzerO6f_yl0X1set1t9nbeDp_aCL6DgzwfwUFtARwwgQs4BGhZikFW7YZKnCbloO6vYQ8T-DrML2oQdV-ewi1l5JodNqBlZYhQcfu8U0C1F1LDF8wMvnYBQEl5fqhDZWtDK669fz2JryeYjgtAOLeySwtz2cyXpSmOr3bYGHIe264b91CRqz5eKvve6yQYxX6EtlmURfWRRfkLZrSAionccND0quejtSHpcDg8qRssK54bBR3h_oRMnHATJD8Z2f8ohYxyY23oxhbHezMfmGhAgMIa-F5Y2iGxmRseEQVhOnjC8vAdy74R48AzRuSCGSWekeGu0cRoP17DSpHCwG3U99V-SA9p6fFbevyg0eMHTk_ZcjJpyw-8QSoOpah4sdYH3VaAfYtmDRe6AmctyRhqLOwLiR0a9SbfCmqETIyQlhTL0tQKazrTtrggi94Rh1ohtvPHxCnIlnJqaHOmzMKwpISd6QvDtr6xlXkCehQ1QI8iKMmWSMJzUsDXm0GFJ50XwhrF7wLmNWprGpU2vscKYWhsniuLo-tYQ5cTtbi4ZgHMDSJdsxwn7g8loznVKVbanJOdkPpdgpvE_Yxy4Kczp6FDI1er3rNhFF2woUVwM_vlO2YV2YiV_T5jGTxpMjFYkW1fSbR9Twl7MZJhnaFn2YnC0ppyMfWc6lmnr-mk36hNveBIhJ41ZyQzg8CfdxS5OtI8cXSQzFrRbGkIriqonXZnRjdXWlF8N4Jx1Nb7AyS6EeY2pa8EOQ5_ih01KD0W2GRGYXmkwb8tt-PKO4hi71JeXiDIdchu4UQ-Jv1GSgQWDNvqhDcKZOs51t1f5-2n6PkhZv5sLr7HwP85zrkfc9zlraO4I-JzJ_Gfefo-_J9X_11e9R4X6oeGPdaQKvtOp3k2t_UiykxvCrWwk3VpCDwjlioo3Q9A9dmaZGRrXyeYa7O-9fNm4A3viqlfxH6M78h0FHoxiscBGt_tpyOvyPNJnHt-HI49bzIZkzAOx36wjbce9sM7OkUeGnu-Nxohzx-Ph6NtHMT-NotRkIXFOAdjjxwwZUPGXg5DIXd3VKmKTCN_MonvGM4IU8dXW3JqFplnTgXGHqNKq2abppqR6dPpjZPgMFlvIBcalpIoIl9IASk_f_t1V0k23Wtd2hdgaAnQckf1vsqGuTgAtLQ_HrqvQSnFbyTXAC2tlQqgpTX0XwEAAP___2bgwQ">