[clang] 6e33e45 - [ASTMatchers] Output currently matching node on crash
Nathan James via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 30 09:14:06 PDT 2022
Author: Nathan James
Date: 2022-03-30T17:14:00+01:00
New Revision: 6e33e45b943061f79ab6de1b60d04d4c6f32f8f9
URL: https://github.com/llvm/llvm-project/commit/6e33e45b943061f79ab6de1b60d04d4c6f32f8f9
DIFF: https://github.com/llvm/llvm-project/commit/6e33e45b943061f79ab6de1b60d04d4c6f32f8f9.diff
LOG: [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
Added:
Modified:
clang/lib/ASTMatchers/ASTMatchFinder.cpp
clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 70598460151ae..e4141232d1c6f 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -761,53 +761,166 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
D);
}
+private:
+ bool TraversingASTNodeNotSpelledInSource = false;
+ bool TraversingASTNodeNotAsIs = false;
+ bool TraversingASTChildrenNotSpelledInSource = false;
+
+ class CurMatchData {
+ public:
+ CurMatchData() = default;
+
+ template <typename NodeType>
+ void SetCallbackAndRawNode(const MatchCallback *CB, const NodeType &N) {
+ assertEmpty();
+ Callback = CB;
+ MatchingNode = &N;
+ }
+
+ const MatchCallback *getCallback() const { return Callback; }
+
+ void SetBoundNodes(const BoundNodes &BN) {
+ assertHoldsState();
+ BNodes = &BN;
+ }
+
+ void clearBoundNodes() {
+ assertHoldsState();
+ BNodes = nullptr;
+ }
+
+ template <typename T> const T *getNode() const {
+ assertHoldsState();
+ return MatchingNode.dyn_cast<const T *>();
+ }
+
+ const BoundNodes *getBoundNodes() const {
+ assertHoldsState();
+ return BNodes;
+ }
+
+ void reset() {
+ assertHoldsState();
+ Callback = nullptr;
+ MatchingNode = nullptr;
+ }
+
+ private:
+ void assertHoldsState() const {
+ assert(Callback != nullptr && !MatchingNode.isNull());
+ }
+
+ void assertEmpty() const {
+ assert(Callback == nullptr && MatchingNode.isNull() && BNodes == nullptr);
+ }
+
+ const MatchCallback *Callback = nullptr;
+ const BoundNodes *BNodes = nullptr;
+
+ llvm::PointerUnion<
+ const QualType *, const TypeLoc *, const NestedNameSpecifier *,
+ const NestedNameSpecifierLoc *, const CXXCtorInitializer *,
+ const TemplateArgumentLoc *, const Attr *, const DynTypedNode *>
+ MatchingNode;
+ } 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 +928,6 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
};
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 +993,7 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
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 +1026,7 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
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 +1215,30 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
// 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;
};
diff --git a/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
index b86917c36dcb1..6573461c9fc1e 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
@@ -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
More information about the cfe-commits
mailing list