mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-04 03:44:59 +00:00
Reland "[ASTMatchers] Output currently matching node on crash"
Extend D120185 to also log the node being matched on in case of a crash. This can help if a matcher is causing a crash or there are not enough interesting nodes bound. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D122529 This reverts commit61d67c8eec
. This relands commit6e33e45b94
. Fixing the build issue on 32bit machines due to not enough free bits in the PointerUnion.
This commit is contained in:
parent
f2ea125ea0
commit
b4ad3c3891
@ -761,53 +761,189 @@ public:
|
||||
D);
|
||||
}
|
||||
|
||||
private:
|
||||
bool TraversingASTNodeNotSpelledInSource = false;
|
||||
bool TraversingASTNodeNotAsIs = false;
|
||||
bool TraversingASTChildrenNotSpelledInSource = false;
|
||||
|
||||
class CurMatchData {
|
||||
// We don't have enough free low bits in 32bit builds to discriminate 8 pointer
|
||||
// types in PointerUnion. so split the union in 2 using a free bit from the
|
||||
// callback pointer.
|
||||
#define CMD_TYPES_0 \
|
||||
const QualType *, const TypeLoc *, const NestedNameSpecifier *, \
|
||||
const NestedNameSpecifierLoc *
|
||||
#define CMD_TYPES_1 \
|
||||
const CXXCtorInitializer *, const TemplateArgumentLoc *, const Attr *, \
|
||||
const DynTypedNode *
|
||||
|
||||
#define IMPL(Index) \
|
||||
template <typename NodeType> \
|
||||
typename std::enable_if_t< \
|
||||
llvm::is_one_of<const NodeType *, CMD_TYPES_##Index>::value> \
|
||||
SetCallbackAndRawNode(const MatchCallback *CB, const NodeType &N) { \
|
||||
assertEmpty(); \
|
||||
Callback.setPointerAndInt(CB, Index); \
|
||||
Node##Index = &N; \
|
||||
} \
|
||||
\
|
||||
template <typename T> \
|
||||
typename std::enable_if_t< \
|
||||
llvm::is_one_of<const T *, CMD_TYPES_##Index>::value, const T *> \
|
||||
getNode() const { \
|
||||
assertHoldsState(); \
|
||||
return Callback.getInt() == (Index) ? Node##Index.dyn_cast<const T *>() \
|
||||
: nullptr; \
|
||||
}
|
||||
|
||||
public:
|
||||
CurMatchData() : Node0(nullptr) {}
|
||||
|
||||
IMPL(0)
|
||||
IMPL(1)
|
||||
|
||||
const MatchCallback *getCallback() const { return Callback.getPointer(); }
|
||||
|
||||
void SetBoundNodes(const BoundNodes &BN) {
|
||||
assertHoldsState();
|
||||
BNodes = &BN;
|
||||
}
|
||||
|
||||
void clearBoundNodes() {
|
||||
assertHoldsState();
|
||||
BNodes = nullptr;
|
||||
}
|
||||
|
||||
const BoundNodes *getBoundNodes() const {
|
||||
assertHoldsState();
|
||||
return BNodes;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
assertHoldsState();
|
||||
Callback.setPointerAndInt(nullptr, 0);
|
||||
Node0 = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void assertHoldsState() const {
|
||||
assert(Callback.getPointer() != nullptr && !Node0.isNull());
|
||||
}
|
||||
|
||||
void assertEmpty() const {
|
||||
assert(Callback.getPointer() == nullptr && Node0.isNull() &&
|
||||
BNodes == nullptr);
|
||||
}
|
||||
|
||||
llvm::PointerIntPair<const MatchCallback *, 1> Callback;
|
||||
union {
|
||||
llvm::PointerUnion<CMD_TYPES_0> Node0;
|
||||
llvm::PointerUnion<CMD_TYPES_1> Node1;
|
||||
};
|
||||
const BoundNodes *BNodes = nullptr;
|
||||
|
||||
#undef CMD_TYPES_0
|
||||
#undef CMD_TYPES_1
|
||||
#undef IMPL
|
||||
} CurMatchState;
|
||||
|
||||
struct CurMatchRAII {
|
||||
template <typename NodeType>
|
||||
CurMatchRAII(MatchASTVisitor &MV, const MatchCallback *CB,
|
||||
const NodeType &NT)
|
||||
: MV(MV) {
|
||||
MV.CurMatchState.SetCallbackAndRawNode(CB, NT);
|
||||
}
|
||||
|
||||
~CurMatchRAII() { MV.CurMatchState.reset(); }
|
||||
|
||||
private:
|
||||
MatchASTVisitor &MV;
|
||||
};
|
||||
|
||||
public:
|
||||
class TraceReporter : llvm::PrettyStackTraceEntry {
|
||||
static void dumpNode(const ASTContext &Ctx, const DynTypedNode &Node,
|
||||
raw_ostream &OS) {
|
||||
if (const auto *D = Node.get<Decl>()) {
|
||||
OS << D->getDeclKindName() << "Decl ";
|
||||
if (const auto *ND = dyn_cast<NamedDecl>(D)) {
|
||||
ND->printQualifiedName(OS);
|
||||
OS << " : ";
|
||||
} else
|
||||
OS << ": ";
|
||||
D->getSourceRange().print(OS, Ctx.getSourceManager());
|
||||
} else if (const auto *S = Node.get<Stmt>()) {
|
||||
OS << S->getStmtClassName() << " : ";
|
||||
S->getSourceRange().print(OS, Ctx.getSourceManager());
|
||||
} else if (const auto *T = Node.get<Type>()) {
|
||||
OS << T->getTypeClassName() << "Type : ";
|
||||
QualType(T, 0).print(OS, Ctx.getPrintingPolicy());
|
||||
} else if (const auto *QT = Node.get<QualType>()) {
|
||||
OS << "QualType : ";
|
||||
QT->print(OS, Ctx.getPrintingPolicy());
|
||||
} else {
|
||||
OS << Node.getNodeKind().asStringRef() << " : ";
|
||||
Node.getSourceRange().print(OS, Ctx.getSourceManager());
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpNodeFromState(const ASTContext &Ctx,
|
||||
const CurMatchData &State, raw_ostream &OS) {
|
||||
if (const DynTypedNode *MatchNode = State.getNode<DynTypedNode>()) {
|
||||
dumpNode(Ctx, *MatchNode, OS);
|
||||
} else if (const auto *QT = State.getNode<QualType>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*QT), OS);
|
||||
} else if (const auto *TL = State.getNode<TypeLoc>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*TL), OS);
|
||||
} else if (const auto *NNS = State.getNode<NestedNameSpecifier>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*NNS), OS);
|
||||
} else if (const auto *NNSL = State.getNode<NestedNameSpecifierLoc>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*NNSL), OS);
|
||||
} else if (const auto *CtorInit = State.getNode<CXXCtorInitializer>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*CtorInit), OS);
|
||||
} else if (const auto *TAL = State.getNode<TemplateArgumentLoc>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*TAL), OS);
|
||||
} else if (const auto *At = State.getNode<Attr>()) {
|
||||
dumpNode(Ctx, DynTypedNode::create(*At), OS);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
TraceReporter(const MatchASTVisitor &MV) : MV(MV) {}
|
||||
void print(raw_ostream &OS) const override {
|
||||
if (!MV.CurMatched) {
|
||||
const CurMatchData &State = MV.CurMatchState;
|
||||
const MatchCallback *CB = State.getCallback();
|
||||
if (!CB) {
|
||||
OS << "ASTMatcher: Not currently matching\n";
|
||||
return;
|
||||
}
|
||||
|
||||
assert(MV.ActiveASTContext &&
|
||||
"ActiveASTContext should be set if there is a matched callback");
|
||||
|
||||
OS << "ASTMatcher: Processing '" << MV.CurMatched->getID() << "'\n";
|
||||
const BoundNodes::IDToNodeMap &Map = MV.CurBoundNodes->getMap();
|
||||
if (Map.empty()) {
|
||||
OS << "No bound nodes\n";
|
||||
return;
|
||||
}
|
||||
OS << "--- Bound Nodes Begin ---\n";
|
||||
for (const auto &Item : Map) {
|
||||
OS << " " << Item.first << " - { ";
|
||||
if (const auto *D = Item.second.get<Decl>()) {
|
||||
OS << D->getDeclKindName() << "Decl ";
|
||||
if (const auto *ND = dyn_cast<NamedDecl>(D)) {
|
||||
ND->printQualifiedName(OS);
|
||||
OS << " : ";
|
||||
} else
|
||||
OS << ": ";
|
||||
D->getSourceRange().print(OS,
|
||||
MV.ActiveASTContext->getSourceManager());
|
||||
} else if (const auto *S = Item.second.get<Stmt>()) {
|
||||
OS << S->getStmtClassName() << " : ";
|
||||
S->getSourceRange().print(OS,
|
||||
MV.ActiveASTContext->getSourceManager());
|
||||
} else if (const auto *T = Item.second.get<Type>()) {
|
||||
OS << T->getTypeClassName() << "Type : ";
|
||||
QualType(T, 0).print(OS, MV.ActiveASTContext->getPrintingPolicy());
|
||||
} else if (const auto *QT = Item.second.get<QualType>()) {
|
||||
OS << "QualType : ";
|
||||
QT->print(OS, MV.ActiveASTContext->getPrintingPolicy());
|
||||
} else {
|
||||
OS << Item.second.getNodeKind().asStringRef() << " : ";
|
||||
Item.second.getSourceRange().print(
|
||||
OS, MV.ActiveASTContext->getSourceManager());
|
||||
ASTContext &Ctx = MV.getASTContext();
|
||||
|
||||
if (const BoundNodes *Nodes = State.getBoundNodes()) {
|
||||
OS << "ASTMatcher: Processing '" << CB->getID() << "' against:\n\t";
|
||||
dumpNodeFromState(Ctx, State, OS);
|
||||
const BoundNodes::IDToNodeMap &Map = Nodes->getMap();
|
||||
if (Map.empty()) {
|
||||
OS << "\nNo bound nodes\n";
|
||||
return;
|
||||
}
|
||||
OS << " }\n";
|
||||
OS << "\n--- Bound Nodes Begin ---\n";
|
||||
for (const auto &Item : Map) {
|
||||
OS << " " << Item.first << " - { ";
|
||||
dumpNode(Ctx, Item.second, OS);
|
||||
OS << " }\n";
|
||||
}
|
||||
OS << "--- Bound Nodes End ---\n";
|
||||
} else {
|
||||
OS << "ASTMatcher: Matching '" << CB->getID() << "' against:\n\t";
|
||||
dumpNodeFromState(Ctx, State, OS);
|
||||
OS << '\n';
|
||||
}
|
||||
OS << "--- Bound Nodes End ---\n";
|
||||
}
|
||||
|
||||
private:
|
||||
@ -815,13 +951,6 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
bool TraversingASTNodeNotSpelledInSource = false;
|
||||
bool TraversingASTNodeNotAsIs = false;
|
||||
bool TraversingASTChildrenNotSpelledInSource = false;
|
||||
|
||||
const MatchCallback *CurMatched = nullptr;
|
||||
const BoundNodes *CurBoundNodes = nullptr;
|
||||
|
||||
struct ASTNodeNotSpelledInSourceScope {
|
||||
ASTNodeNotSpelledInSourceScope(MatchASTVisitor *V, bool B)
|
||||
: MV(V), MB(V->TraversingASTNodeNotSpelledInSource) {
|
||||
@ -887,6 +1016,7 @@ private:
|
||||
if (EnableCheckProfiling)
|
||||
Timer.setBucket(&TimeByBucket[MP.second->getID()]);
|
||||
BoundNodesTreeBuilder Builder;
|
||||
CurMatchRAII RAII(*this, MP.second, Node);
|
||||
if (MP.first.matches(Node, this, &Builder)) {
|
||||
MatchVisitor Visitor(*this, ActiveASTContext, MP.second);
|
||||
Builder.visitMatches(&Visitor);
|
||||
@ -919,6 +1049,7 @@ private:
|
||||
continue;
|
||||
}
|
||||
|
||||
CurMatchRAII RAII(*this, MP.second, DynNode);
|
||||
if (MP.first.matches(DynNode, this, &Builder)) {
|
||||
MatchVisitor Visitor(*this, ActiveASTContext, MP.second);
|
||||
Builder.visitMatches(&Visitor);
|
||||
@ -1107,35 +1238,30 @@ private:
|
||||
// the aggregated bound nodes for each match.
|
||||
class MatchVisitor : public BoundNodesTreeBuilder::Visitor {
|
||||
struct CurBoundScope {
|
||||
CurBoundScope(MatchASTVisitor &MV, const BoundNodes &BN) : MV(MV) {
|
||||
assert(MV.CurMatched && !MV.CurBoundNodes);
|
||||
MV.CurBoundNodes = &BN;
|
||||
CurBoundScope(MatchASTVisitor::CurMatchData &State, const BoundNodes &BN)
|
||||
: State(State) {
|
||||
State.SetBoundNodes(BN);
|
||||
}
|
||||
|
||||
~CurBoundScope() { MV.CurBoundNodes = nullptr; }
|
||||
~CurBoundScope() { State.clearBoundNodes(); }
|
||||
|
||||
private:
|
||||
MatchASTVisitor &MV;
|
||||
MatchASTVisitor::CurMatchData &State;
|
||||
};
|
||||
|
||||
public:
|
||||
MatchVisitor(MatchASTVisitor &MV, ASTContext *Context,
|
||||
MatchFinder::MatchCallback *Callback)
|
||||
: MV(MV), Context(Context), Callback(Callback) {
|
||||
assert(!MV.CurMatched && !MV.CurBoundNodes);
|
||||
MV.CurMatched = Callback;
|
||||
}
|
||||
|
||||
~MatchVisitor() { MV.CurMatched = nullptr; }
|
||||
: State(MV.CurMatchState), Context(Context), Callback(Callback) {}
|
||||
|
||||
void visitMatch(const BoundNodes& BoundNodesView) override {
|
||||
TraversalKindScope RAII(*Context, Callback->getCheckTraversalKind());
|
||||
CurBoundScope RAII2(MV, BoundNodesView);
|
||||
CurBoundScope RAII2(State, BoundNodesView);
|
||||
Callback->run(MatchFinder::MatchResult(BoundNodesView, Context));
|
||||
}
|
||||
|
||||
private:
|
||||
MatchASTVisitor &MV;
|
||||
MatchASTVisitor::CurMatchData &State;
|
||||
ASTContext* Context;
|
||||
MatchFinder::MatchCallback* Callback;
|
||||
};
|
||||
|
@ -39,10 +39,24 @@ TEST(HasNameDeathTest, DiesOnEmptyPattern) {
|
||||
// FIXME: Figure out why back traces aren't being generated on clang builds on
|
||||
// windows.
|
||||
#if ENABLE_BACKTRACES && (!defined(_MSC_VER) || !defined(__clang__))
|
||||
|
||||
AST_MATCHER(Decl, causeCrash) {
|
||||
abort();
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(MatcherCrashDeathTest, CrashOnMatcherDump) {
|
||||
llvm::EnablePrettyStackTrace();
|
||||
auto Matcher = testing::HasSubstr(
|
||||
"ASTMatcher: Matching '<unknown>' against:\n\tFunctionDecl foo : "
|
||||
"<input.cc:1:1, col:10>");
|
||||
ASSERT_DEATH(matches("void foo();", functionDecl(causeCrash())), Matcher);
|
||||
}
|
||||
|
||||
template <typename MatcherT>
|
||||
static void crashTestNodeDump(MatcherT Matcher,
|
||||
ArrayRef<StringRef> MatchedNodes,
|
||||
StringRef Code) {
|
||||
StringRef Against, StringRef Code) {
|
||||
llvm::EnablePrettyStackTrace();
|
||||
MatchFinder Finder;
|
||||
|
||||
@ -58,7 +72,9 @@ static void crashTestNodeDump(MatcherT Matcher,
|
||||
ASSERT_DEATH(tooling::runToolOnCode(
|
||||
newFrontendActionFactory(&Finder)->create(), Code),
|
||||
testing::HasSubstr(
|
||||
"ASTMatcher: Processing 'CrashTester'\nNo bound nodes"));
|
||||
("ASTMatcher: Processing 'CrashTester' against:\n\t" +
|
||||
Against + "\nNo bound nodes")
|
||||
.str()));
|
||||
} else {
|
||||
std::vector<testing::PolymorphicMatcher<
|
||||
testing::internal::HasSubstrMatcher<std::string>>>
|
||||
@ -69,7 +85,9 @@ static void crashTestNodeDump(MatcherT Matcher,
|
||||
}
|
||||
auto CrashMatcher = testing::AllOf(
|
||||
testing::HasSubstr(
|
||||
"ASTMatcher: Processing 'CrashTester'\n--- Bound Nodes Begin ---"),
|
||||
("ASTMatcher: Processing 'CrashTester' against:\n\t" + Against +
|
||||
"\n--- Bound Nodes Begin ---")
|
||||
.str()),
|
||||
testing::HasSubstr("--- Bound Nodes End ---"),
|
||||
testing::AllOfArray(Matchers));
|
||||
|
||||
@ -79,7 +97,8 @@ static void crashTestNodeDump(MatcherT Matcher,
|
||||
}
|
||||
}
|
||||
TEST(MatcherCrashDeathTest, CrashOnCallbackDump) {
|
||||
crashTestNodeDump(forStmt(), {}, "void foo() { for(;;); }");
|
||||
crashTestNodeDump(forStmt(), {}, "ForStmt : <input.cc:1:14, col:21>",
|
||||
"void foo() { for(;;); }");
|
||||
crashTestNodeDump(
|
||||
forStmt(hasLoopInit(declStmt(hasSingleDecl(
|
||||
varDecl(hasType(qualType().bind("QT")),
|
||||
@ -94,6 +113,7 @@ TEST(MatcherCrashDeathTest, CrashOnCallbackDump) {
|
||||
"IL - { IntegerLiteral : <input.cc:3:18> }", "QT - { QualType : int }",
|
||||
"T - { BuiltinType : int }",
|
||||
"VD - { VarDecl I : <input.cc:3:10, col:18> }"},
|
||||
"ForStmt : <input.cc:3:5, line:4:5>",
|
||||
R"cpp(
|
||||
void foo() {
|
||||
for (int I = 0; I < 5; ++I) {
|
||||
@ -106,12 +126,14 @@ TEST(MatcherCrashDeathTest, CrashOnCallbackDump) {
|
||||
{"Unnamed - { CXXRecordDecl (anonymous) : <input.cc:1:1, col:36> }",
|
||||
"Op+ - { CXXMethodDecl (anonymous struct)::operator+ : <input.cc:1:10, "
|
||||
"col:29> }"},
|
||||
"CXXRecordDecl (anonymous) : <input.cc:1:1, col:36>",
|
||||
"struct { int operator+(int) const; } Unnamed;");
|
||||
crashTestNodeDump(
|
||||
cxxRecordDecl(hasMethod(cxxConstructorDecl(isDefaulted()).bind("Ctor")),
|
||||
hasMethod(cxxDestructorDecl(isDefaulted()).bind("Dtor"))),
|
||||
{"Ctor - { CXXConstructorDecl Foo::Foo : <input.cc:1:14, col:28> }",
|
||||
"Dtor - { CXXDestructorDecl Foo::~Foo : <input.cc:1:31, col:46> }"},
|
||||
"CXXRecordDecl Foo : <input.cc:1:1, col:49>",
|
||||
"struct Foo { Foo() = default; ~Foo() = default; };");
|
||||
}
|
||||
#endif // ENABLE_BACKTRACES
|
||||
|
Loading…
Reference in New Issue
Block a user