TopDUContext* DUChainTestBase::parseAdditionalFile(const IndexedString& fileName, const QByteArray& contents) { ParseSession session; session.setContents(contents); StartAst* ast = nullptr; if (!session.parse(&ast)) qFatal("can't parse"); EditorIntegrator editor(&session); session.setCurrentDocument(fileName); DeclarationBuilder declarationBuilder(&editor); TopDUContext* top = declarationBuilder.build(fileName, ast); if ( fileName != internalFunctionFile() ) { UseBuilder useBuilder(&editor); useBuilder.buildUses(ast); } if (!session.problems().isEmpty()) { DUChainWriteLocker lock; foreach( const ProblemPointer& p, session.problems() ) { top->addProblem(p); } }
ReferencedTopDUContext ClangHelpers::buildDUChain(CXFile file, const Imports& imports, const ParseSession& session, TopDUContext::Features features, IncludeFileContexts& includedFiles, ClangIndex* index) { if (includedFiles.contains(file)) { return {}; } // prevent recursion includedFiles.insert(file, {}); // ensure DUChain for imports are build properly foreach(const auto& import, imports.values(file)) { buildDUChain(import.file, imports, session, features, includedFiles, index); } const IndexedString path(QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath()); if (path.isEmpty()) { // may happen when the file gets removed before the job is run return {}; } const auto& environment = session.environment(); bool update = false; UrlParseLock urlLock(path); ReferencedTopDUContext context; { DUChainWriteLocker lock; context = DUChain::self()->chainForDocument(path, &environment); if (!context) { context = ::createTopContext(path, environment); } else { update = true; } includedFiles.insert(file, context); if (update) { auto envFile = ClangParsingEnvironmentFile::Ptr(dynamic_cast<ClangParsingEnvironmentFile*>(context->parsingEnvironmentFile().data())); Q_ASSERT(envFile); if (!envFile->needsUpdate(&environment) && envFile->featuresSatisfied(features)) { return context; } else { //TODO: don't attempt to update if this environment is worse quality than the outdated one if (index && envFile->environmentQuality() < environment.quality()) { index->pinTranslationUnitForUrl(environment.translationUnitUrl(), path); } envFile->setEnvironment(environment); envFile->setModificationRevision(ModificationRevision::revisionForFile(context->url())); } context->clearImportedParentContexts(); } context->setFeatures(features); foreach(const auto& import, imports.values(file)) { Q_ASSERT(includedFiles.contains(import.file)); auto ctx = includedFiles.value(import.file); if (!ctx) { // happens for cyclic imports continue; } context->addImportedParentContext(ctx, import.location); } context->updateImportsCache(); } const auto problems = session.problemsForFile(file); { DUChainWriteLocker lock; context->setProblems(problems); } Builder::visit(session.unit(), file, includedFiles, update); return context; }
int main(int argc, char **argv) { if (argc == 1) { showUsage(); return EXIT_SUCCESS; } QCoreApplication app(argc, argv); const QStringList& args = app.arguments(); QFileInfo configFile; QString generator; bool addHeaders = false; bool hasCommandLineGenerator = false; QStringList classes; ParserOptions::notToBeResolved << "FILE"; for (int i = 1; i < args.count(); i++) { if ((args[i] == "-I" || args[i] == "-d" || args[i] == "-dm" || args[i] == "-g" || args[i] == "-config") && i + 1 >= args.count()) { qCritical() << "not enough parameters for option" << args[i]; return EXIT_FAILURE; } if (args[i] == "-I") { ParserOptions::includeDirs << QDir(args[++i]); } else if (args[i] == "-config") { configFile = QFileInfo(args[++i]); } else if (args[i] == "-d") { ParserOptions::definesList = QFileInfo(args[++i]); } else if (args[i] == "-dm") { ParserOptions::dropMacros += args[++i].split(','); } else if (args[i] == "-g") { generator = args[++i]; hasCommandLineGenerator = true; } else if ((args[i] == "-h" || args[i] == "--help") && argc == 2) { showUsage(); return EXIT_SUCCESS; } else if (args[i] == "-t") { ParserOptions::resolveTypedefs = true; } else if (args[i] == "-qt") { ParserOptions::qtMode = true; } else if (args[i] == "--") { addHeaders = true; } else if (addHeaders) { ParserOptions::headerList << QFileInfo(args[i]); } } if (configFile.exists()) { QFile file(configFile.filePath()); file.open(QIODevice::ReadOnly); QDomDocument doc; doc.setContent(file.readAll()); file.close(); QDomElement root = doc.documentElement(); QDomNode node = root.firstChild(); while (!node.isNull()) { QDomElement elem = node.toElement(); if (elem.isNull()) { node = node.nextSibling(); continue; } if (elem.tagName() == "resolveTypedefs") { ParserOptions::resolveTypedefs = (elem.text() == "true"); } else if (elem.tagName() == "qtMode") { ParserOptions::qtMode = (elem.text() == "true"); } else if (!hasCommandLineGenerator && elem.tagName() == "generator") { generator = elem.text(); } else if (elem.tagName() == "includeDirs") { QDomNode dir = elem.firstChild(); while (!dir.isNull()) { QDomElement elem = dir.toElement(); if (elem.isNull()) { dir = dir.nextSibling(); continue; } if (elem.tagName() == "dir") { ParserOptions::includeDirs << QDir(elem.text()); } dir = dir.nextSibling(); } } else if (elem.tagName() == "definesList") { // reference to an external file, so it can be auto-generated ParserOptions::definesList = QFileInfo(elem.text()); } else if (elem.tagName() == "dropMacros") { QDomNode macro = elem.firstChild(); while (!macro.isNull()) { QDomElement elem = macro.toElement(); if (elem.isNull()) { macro = macro.nextSibling(); continue; } if (elem.tagName() == "name") { ParserOptions::dropMacros << elem.text(); } macro = macro.nextSibling(); } } node = node.nextSibling(); } } else { qWarning() << "Couldn't find config file" << configFile.filePath(); } // first try to load plugins from the executable's directory QLibrary lib(app.applicationDirPath() + "/generator_" + generator); lib.load(); if (!lib.isLoaded()) { lib.unload(); lib.setFileName(app.applicationDirPath() + "/../lib" + LIB_SUFFIX + "/smokegen/generator_" + generator); lib.load(); } if (!lib.isLoaded()) { lib.unload(); lib.setFileName("generator_" + generator); lib.load(); } if (!lib.isLoaded()) { qCritical() << lib.errorString(); return EXIT_FAILURE; } qDebug() << "using generator" << lib.fileName(); GenerateFn generate = (GenerateFn) lib.resolve("generate"); if (!generate) { qCritical() << "couldn't resolve symbol 'generate', aborting"; return EXIT_FAILURE; } foreach (QDir dir, ParserOptions::includeDirs) { if (!dir.exists()) { qWarning() << "include directory" << dir.path() << "doesn't exist"; ParserOptions::includeDirs.removeAll(dir); } } QStringList defines; if (ParserOptions::definesList.exists()) { QFile file(ParserOptions::definesList.filePath()); file.open(QIODevice::ReadOnly); while (!file.atEnd()) { QByteArray array = file.readLine(); if (!array.isEmpty()) defines << array.trimmed(); } file.close(); } else if (!ParserOptions::definesList.filePath().isEmpty()) { qWarning() << "didn't find file" << ParserOptions::definesList.filePath(); } QFile log("generator.log"); bool logErrors = log.open(QFile::WriteOnly | QFile::Truncate); QTextStream logOut(&log); foreach (QFileInfo file, ParserOptions::headerList) { qDebug() << "parsing" << file.absoluteFilePath(); // this has already been parsed because it was included by some header if (parsedHeaders.contains(file.absoluteFilePath())) continue; Preprocessor pp(ParserOptions::includeDirs, defines, file); Control c; Parser parser(&c); ParseSession session; session.setContentsAndGenerateLocationTable(pp.preprocess()); TranslationUnitAST* ast = parser.parse(&session); // TODO: improve 'header => class' association GeneratorVisitor visitor(&session, file.fileName()); visitor.visit(ast); if (!logErrors) continue; foreach (const Problem* p, c.problems()) { logOut << file.fileName() << ": " << p->file << "(" << p->position.line << ", " << p->position.column << "): " << p->description << "\n"; } }
ReferencedTopDUContext ClangHelpers::buildDUChain(CXFile file, const Imports& imports, const ParseSession& session, TopDUContext::Features features, IncludeFileContexts& includedFiles, ClangIndex* index, const std::function<bool()>& abortFunction) { if (includedFiles.contains(file)) { return {}; } if (abortFunction && abortFunction()) { return {}; } // prevent recursion includedFiles.insert(file, {}); // ensure DUChain for imports are build properly foreach(const auto& import, imports.values(file)) { buildDUChain(import.file, imports, session, features, includedFiles, index, abortFunction); } const IndexedString path(QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath()); if (path.isEmpty()) { // may happen when the file gets removed before the job is run return {}; } const auto& environment = session.environment(); bool update = false; UrlParseLock urlLock(path); ReferencedTopDUContext context; { DUChainWriteLocker lock; context = DUChain::self()->chainForDocument(path, &environment); if (!context) { context = ::createTopContext(path, environment); } else { update = true; } includedFiles.insert(file, context); if (update) { auto envFile = ClangParsingEnvironmentFile::Ptr(dynamic_cast<ClangParsingEnvironmentFile*>(context->parsingEnvironmentFile().data())); Q_ASSERT(envFile); if (!envFile) return context; /* NOTE: When we are here, then either the translation unit or one of its headers was changed. * Thus we must always update the translation unit to propagate the change(s). * See also: https://bugs.kde.org/show_bug.cgi?id=356327 * This assumes that headers are independent, we may need to improve that in the future * and also update header files more often when other files included therein got updated. */ if (path != environment.translationUnitUrl() && !envFile->needsUpdate(&environment) && envFile->featuresSatisfied(features)) { return context; } else { //TODO: don't attempt to update if this environment is worse quality than the outdated one if (index && envFile->environmentQuality() < environment.quality()) { index->pinTranslationUnitForUrl(environment.translationUnitUrl(), path); } envFile->setEnvironment(environment); envFile->setModificationRevision(ModificationRevision::revisionForFile(context->url())); } context->clearImportedParentContexts(); } context->setFeatures(features); foreach(const auto& import, imports.values(file)) { auto ctx = includedFiles.value(import.file); if (!ctx) { // happens for cyclic imports continue; } context->addImportedParentContext(ctx, import.location); } context->updateImportsCache(); } const auto problems = session.problemsForFile(file); { DUChainWriteLocker lock; context->setProblems(problems); } Builder::visit(session.unit(), file, includedFiles, update); DUChain::self()->emitUpdateReady(path, context); return context; }