diff --git a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp --- a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp @@ -39,10 +39,12 @@ } void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) { + Context->onResultEntry(CheckName, Result.Nodes); // For historical reasons, checks don't implement the MatchFinder run() // callback directly. We keep the run()/check() distinction to avoid interface // churn, and to allow us to add cross-cutting logic in the future. check(Result); + Context->onResultExit(); } ClangTidyCheck::OptionsView::OptionsView( diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -24,6 +24,7 @@ class SourceManager; namespace ast_matchers { class MatchFinder; +class BoundNodes; } // namespace ast_matchers namespace tooling { class CompilationDatabase; @@ -61,6 +62,8 @@ } }; +class CheckStackTraceDebug; + /// Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine /// provided by this context. /// @@ -204,12 +207,17 @@ DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID))); } + void onResultEntry(StringRef CheckName, + const ast_matchers::BoundNodes &Result) const; + void onResultExit() const; + private: // Writes to Stats. friend class ClangTidyDiagnosticConsumer; DiagnosticsEngine *DiagEngine; std::unique_ptr OptionsProvider; + std::unique_ptr Debugger; std::string CurrentFile; ClangTidyOptions CurrentOptions; diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -22,6 +22,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/Attr.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -35,6 +36,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Regex.h" #include #include @@ -151,6 +153,51 @@ }; } // end anonymous namespace +namespace clang { +namespace tidy { +class CheckStackTraceDebug : public llvm::PrettyStackTraceEntry { +public: + void print(raw_ostream &OS) const override { + if (CurrentCheckName.empty()) + return; + // This should be an assert, but asserts shouldn't be used in signal + // handlers + if (!CurContext) + return; + OS << "Processing check " << CurrentCheckName << '\n'; + CurrentCheckName = ""; + const clang::ast_matchers::BoundNodes::IDToNodeMap &Map = + CurNodes->getMap(); + if (Map.empty()) { + OS << "No bound nodes\n"; + return; + } + for (const auto &Item : Map) { + OS << "Node '" << Item.first << "' - "; + Item.second.dump(OS, *CurContext); + OS << "\n"; + } + } + void onResultEntry(StringRef CheckName, + const ast_matchers::BoundNodes &BoundNodes) { + CurrentCheckName = CheckName; + CurNodes = &BoundNodes; + } + + void onResultLeave() { + CurrentCheckName = ""; + CurNodes = nullptr; + } + void setContext(const ASTContext &Ctx) { CurContext = &Ctx; } + void clearContext() { CurContext = nullptr; } + +private: + mutable StringRef CurrentCheckName; + const ast_matchers::BoundNodes *CurNodes; + const ASTContext *CurContext; +}; +} // namespace tidy +} // namespace clang ClangTidyError::ClangTidyError(StringRef CheckName, ClangTidyError::Level DiagLevel, StringRef BuildDirectory, bool IsWarningAsError) @@ -161,7 +208,7 @@ std::unique_ptr OptionsProvider, bool AllowEnablingAnalyzerAlphaCheckers) : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)), - Profile(false), + Debugger(std::make_unique()), Profile(false), AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers) { // Before the first translation unit we can get errors related to command-line // parsing, use empty string for the file name in this case. @@ -231,6 +278,7 @@ void ClangTidyContext::setASTContext(ASTContext *Context) { DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context); LangOpts = Context->getLangOpts(); + Debugger->setContext(*Context); } const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const { @@ -338,6 +386,11 @@ return Result; } +void ClangTidyContext::onResultEntry( + StringRef CheckName, const ast_matchers::BoundNodes &Result) const { + Debugger->onResultEntry(CheckName, Result); +} +void ClangTidyContext::onResultExit() const { Debugger->onResultLeave(); } } // namespace tidy } // namespace clang