ACAV f0ba4b7c9529
Abstract Syntax Tree (AST) visualization tool for C, C++, and Objective-C
Loading...
Searching...
No Matches
AstExtractorRunner.cpp
1/*$!{
2* Aurora Clang AST Viewer (ACAV)
3*
4* Copyright (c) 2026 Min Liu
5* Copyright (c) 2026 Michael David Adams
6*
7* SPDX-License-Identifier: GPL-2.0-or-later
8*
9* This program is free software; you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by
11* the Free Software Foundation; either version 2 of the License, or
12* (at your option) any later version.
13*
14* This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details.
18*
19* You should have received a copy of the GNU General Public License along
20* with this program; if not, see <https://www.gnu.org/licenses/>.
21}$!*/
22
24#include "common/ClangUtils.h"
26#include "core/AcavAstBuilder.h"
27#include "core/MemoryProfiler.h"
28#include "core/SourceLocation.h"
29#include <QDateTime>
30#include <QFileInfo>
31#include <QString>
32#include <chrono>
33// Qt defines 'emit' as a no-op macro which conflicts with Sema.h in LLVM 22+
34// (ASTUnit.h → ASTWriter.h → Sema.h has a method named 'emit')
35#undef emit
36#include <clang/Frontend/ASTUnit.h>
37#define emit
38
39namespace acav {
40
42 FileManager &fileManager,
43 QObject *parent)
44 : QObject(parent), context_(context), fileManager_(fileManager) {
45 diagnosticCallback_ = [this](const DiagnosticMessage &diag) {
46 LogEntry entry;
47 switch (diag.level) {
48 case clang::DiagnosticsEngine::Warning:
49 entry.level = LogLevel::Warning;
50 break;
51 case clang::DiagnosticsEngine::Error:
52 case clang::DiagnosticsEngine::Fatal:
53 entry.level = LogLevel::Error;
54 break;
55 case clang::DiagnosticsEngine::Remark:
56 case clang::DiagnosticsEngine::Note:
57 case clang::DiagnosticsEngine::Ignored:
58 default:
59 entry.level = LogLevel::Debug;
60 break;
61 }
62
63 QString message = QString::fromStdString(diag.message);
64 if (!diag.file.empty()) {
65 QString location = QString::fromStdString(diag.file);
66 if (diag.line > 0) {
67 location += QString(":%1").arg(diag.line);
68 }
69 if (diag.column > 0) {
70 location += QString(":%1").arg(diag.column);
71 }
72 message = QString("%1: %2").arg(location, message);
73 }
74
75 entry.source = "acav-clang";
76 entry.message = message;
77 entry.timestamp = QDateTime::currentDateTime();
78 emit logMessage(entry);
79 };
80
81 // Use the default loader: loadAstFromFile handles module path extraction
82 astLoader_ = [this](const std::string &path, std::string &errorMsg,
83 const std::string &compilationDbPath,
84 const std::string &sourcePath)
85 -> std::unique_ptr<clang::ASTUnit> {
86 return acav::loadAstFromFile(path, errorMsg, compilationDbPath,
87 sourcePath, diagnosticCallback_);
88 };
89}
90
92 FileManager &fileManager,
93 AstLoader loader, QObject *parent)
94 : QObject(parent), context_(context), fileManager_(fileManager),
95 astLoader_(std::move(loader)) {}
96
97AstExtractorRunner::~AstExtractorRunner() = default;
98
100 commentExtractionEnabled_.store(enabled);
101}
102
103void AstExtractorRunner::run(const QString &astFilePath,
104 const QStringList &tuFilePaths,
105 const QString &compilationDbPath) {
106 // Ensure a clean state for each extraction run
108
109 start_ = std::chrono::steady_clock::now();
110 emit started(astFilePath);
111
112 emit progress(QStringLiteral("Pre-registering files..."));
113 registerInputFiles(tuFilePaths);
114
115 emit progress(QStringLiteral("Loading AST from cache..."));
116 auto loadStart = std::chrono::steady_clock::now();
117
118 // Get first source file for module mapping extraction
119 std::string sourcePath;
120 if (!tuFilePaths.isEmpty()) {
121 sourcePath = tuFilePaths.first().toStdString();
122 }
123
124 // loadAstFromFile handles module path extraction internally
125 std::string stdError;
126 std::unique_ptr<clang::ASTUnit> astUnit =
127 astLoader_(astFilePath.toStdString(), stdError,
128 compilationDbPath.toStdString(), sourcePath);
129
130 if (!astUnit) {
131 QString errorMsg = stdError.empty() ? QStringLiteral("Failed to load AST")
132 : QString::fromStdString(stdError);
133 emit error(errorMsg);
134 return;
135 }
136
137 auto loadEnd = std::chrono::steady_clock::now();
138 emit progress(
139 QString("Loaded AST: %1s")
140 .arg(std::chrono::duration<double>(loadEnd - loadStart).count(), 0,
141 'f', 2));
142
143 // Checkpoint: Clang AST loaded into memory
144 MemoryProfiler::checkpoint("After loading Clang ASTUnit");
145
146 emit progress(QStringLiteral("Building ACAV AST ..."));
147 auto buildStart = std::chrono::steady_clock::now();
148
149 AstViewNode *root = buildTreeFromASTUnit(*astUnit);
150 if (!root) {
151 emit error(QStringLiteral("Failed to build AST"));
152 return;
153 }
154
155 auto buildEnd = std::chrono::steady_clock::now();
156 emit progress(
157 QString("Built tree: %1s")
158 .arg(std::chrono::duration<double>(buildEnd - buildStart).count(), 0,
159 'f', 2));
160
161 // Checkpoint: ACAV AST built, Clang AST still in memory
162 MemoryProfiler::checkpoint("After building ACAV AST (Clang still loaded)");
163
164#ifdef ACAV_ENABLE_STRING_STATS
165 InternedString::printStats("After AST extraction");
166 context_->printTypeDeduplicationStats("After AST extraction");
167#endif
168
169 // EXPLICITLY destroy Clang AST before returning
170 // This ensures cleanup happens on worker thread before we emit finished
171 astUnit.reset(); // Force Clang AST destruction NOW!
172
173 // Checkpoint: Clang AST explicitly destroyed
174 MemoryProfiler::checkpoint("After destroying Clang ASTUnit");
175
176 auto totalEnd = std::chrono::steady_clock::now();
177 emit progress(
178 QString("Total: %1s")
179 .arg(std::chrono::duration<double>(totalEnd - start_).count(), 0, 'f',
180 2));
181
182 emit finished(root);
183}
184
185void AstExtractorRunner::registerInputFiles(const QStringList &tuFilePaths) {
186 for (const QString &filePath : tuFilePaths) {
187 fileManager_.registerFile(filePath.toStdString());
188 }
189}
190
191AstViewNode *AstExtractorRunner::buildTreeFromASTUnit(clang::ASTUnit &astUnit) {
192 clang::ASTContext &ctx = astUnit.getASTContext();
193 clang::TranslationUnitDecl *tuDecl = ctx.getTranslationUnitDecl();
194 if (!tuDecl) {
195 return nullptr;
196 }
197
198 AstExtractionStats stats;
199 AstViewNode *root = AcavAstBuilder::buildFromASTUnit(
200 astUnit, context_, fileManager_, stats,
201 commentExtractionEnabled_.load());
202
203 emit statsUpdated(stats);
204
205 if (root) {
206 emit progress(QStringLiteral("Building location index..."));
207 auto indexStart = std::chrono::steady_clock::now();
208 buildLocationIndex(root);
209 context_->finalizeLocationIndex();
210 auto indexEnd = std::chrono::steady_clock::now();
211 emit progress(
212 QString("Index built: %1s")
213 .arg(std::chrono::duration<double>(indexEnd - indexStart).count(),
214 0, 'f', 2));
215 }
216
217 return root;
218}
219
220void AstExtractorRunner::buildLocationIndex(AstViewNode *node) {
221 if (!node) {
222 return;
223 }
224
225 context_->indexNode(node);
226 for (AstViewNode *child : node->getChildren()) {
227 buildLocationIndex(child);
228 }
229}
230
231} // namespace acav
Clang AST to ACAV AST transformation.
Qt runner for AST extraction pipeline.
Utilities for interacting with Clang at runtime This includes runtime detection of Clang paths and AS...
std::unique_ptr< clang::ASTUnit > loadAstFromFile(const std::string &astFilePath, std::string &errorMessage, const std::string &compilationDbPath="", const std::string &sourcePath="", const DiagnosticCallback &diagnosticCallback=nullptr)
Load AST from local file.
Memory-efficient immutable string with automatic deduplication.
Cross-platform memory profiling utility.
Source code location representation.
static AstViewNode * buildFromASTUnit(clang::ASTUnit &astUnit, AstContext *context, FileManager &fileManager, AstExtractionStats &stats, bool extractComments=false)
Build ACAV AST from Clang ASTUnit.
Memory manager for AST nodes in a translation unit.
Definition AstNode.h:66
void setCommentExtractionEnabled(bool enabled)
Enable or disable comment extraction in AST.
void run(const QString &astFilePath, const QStringList &tuFilePaths, const QString &compilationDbPath=QString())
Run extraction pipeline and emit lifecycle signals.
std::function< std::unique_ptr< clang::ASTUnit >( const std::string &astFilePath, std::string &errorOut, const std::string &compilationDbPath, const std::string &sourcePath)> AstLoader
Function type for loading AST from file.
AstExtractorRunner(AstContext *context, FileManager &fileManager, QObject *parent=nullptr)
Construct runner with default AST loader.
Represents node in AST tree hierarchy.
Definition AstNode.h:195
Centralized file registry providing path-to-FileID mapping.
Definition FileManager.h:45
FileID registerFile(std::string_view filePath)
Register a file and return its FileID.
static void checkpoint(const QString &label)
Log current memory usage with a label.
static void resetCache()
Reset internal caches (per extraction run).