File indexing completed on 2024-05-19 04:39:57

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "main.h"
0008 
0009 #include <shell/core.h>
0010 #include <shell/shellextension.h>
0011 
0012 #include <language/backgroundparser/backgroundparser.h>
0013 #include <language/duchain/definitions.h>
0014 #include <language/duchain/duchain.h>
0015 #include <language/duchain/duchainlock.h>
0016 #include <language/duchain/duchaindumper.h>
0017 #include <language/duchain/dumpdotgraph.h>
0018 #include <language/duchain/problem.h>
0019 #include <language/duchain/persistentsymboltable.h>
0020 
0021 #include <interfaces/ilanguagecontroller.h>
0022 #include <tests/autotestshell.h>
0023 #include <tests/testcore.h>
0024 
0025 #include <QCoreApplication>
0026 #include <QCommandLineParser>
0027 #include <QCommandLineOption>
0028 #include <QDebug>
0029 #include <QDirIterator>
0030 #include <QStringList>
0031 #include <QTimer>
0032 
0033 #include <cstdio>
0034 
0035 #include <KAboutData>
0036 #include <KLocalizedString>
0037 
0038 bool verbose = false, warnings = false;
0039 
0040 using namespace KDevelop;
0041 
0042 void messageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
0043 {
0044     Q_UNUSED(context);
0045 
0046     switch (type) {
0047     case QtInfoMsg:     // fall-through
0048     case QtDebugMsg:
0049         if (verbose)
0050             std::cerr << qPrintable(msg) << std::endl;
0051         break;
0052     case QtWarningMsg:
0053         if (warnings)
0054             std::cerr << qPrintable(msg) << std::endl;
0055         break;
0056     case QtCriticalMsg:
0057         std::cerr << qPrintable(msg) << std::endl;
0058         break;
0059     case QtFatalMsg:
0060         std::cerr << qPrintable(msg) << std::endl;
0061         abort();
0062     }
0063 }
0064 
0065 Manager::Manager(QCommandLineParser* args) : m_total(0)
0066     , m_args(args)
0067     , m_allFilesAdded(0)
0068 {
0069 }
0070 
0071 void Manager::init()
0072 {
0073     if (m_args->positionalArguments().isEmpty()) {
0074         std::cerr << "Need file or directory to duchainify" << std::endl;
0075         QCoreApplication::exit(1);
0076     }
0077 
0078     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0079     if (m_args->isSet(QStringLiteral("features"))) {
0080         QString featuresStr = m_args->value(QStringLiteral("features"));
0081         if (featuresStr == QLatin1String("visible-declarations")) {
0082             features = TopDUContext::VisibleDeclarationsAndContexts;
0083         } else if (featuresStr == QLatin1String("all-declarations")) {
0084             features = TopDUContext::AllDeclarationsAndContexts;
0085         } else if (featuresStr == QLatin1String("all-declarations-and-uses")) {
0086             features = TopDUContext::AllDeclarationsContextsAndUses;
0087         } else if (featuresStr == QLatin1String("all-declarations-and-uses-and-AST")) {
0088             features = TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST;
0089         } else if (featuresStr == QLatin1String("empty")) {
0090             features = TopDUContext::Empty;
0091         } else if (featuresStr == QLatin1String("simplified-visible-declarations")) {
0092             features = TopDUContext::SimplifiedVisibleDeclarationsAndContexts;
0093         } else {
0094             std::cerr << "Wrong feature-string given\n";
0095             QCoreApplication::exit(2);
0096             return;
0097         }
0098     }
0099     if (m_args->isSet(QStringLiteral("force-update")))
0100         features |= TopDUContext::ForceUpdate;
0101     if (m_args->isSet(QStringLiteral("force-update-recursive")))
0102         features |= TopDUContext::ForceUpdateRecursive;
0103 
0104     if (m_args->isSet(QStringLiteral("threads"))) {
0105         bool ok = false;
0106         int count = m_args->value(QStringLiteral("threads")).toInt(&ok);
0107         ICore::self()->languageController()->backgroundParser()->setThreadCount(count);
0108         if (!ok) {
0109             std::cerr << "bad thread count\n";
0110             QCoreApplication::exit(3);
0111             return;
0112         }
0113     }
0114 
0115     // quit when everything is done
0116     // background parser emits hideProgress() signal in two situations:
0117     // when everything is done and when bgparser is suspended
0118     // later doesn't happen in duchain, so just rely on hideProgress()
0119     // and quit when it's emitted
0120     connect(
0121         ICore::self()->languageController()->backgroundParser(), &BackgroundParser::hideProgress, this,
0122         [this]() {
0123             if (ICore::self()->languageController()->backgroundParser()->isIdle())
0124                 QTimer::singleShot(0, this, &Manager::finish);
0125         });
0126 
0127     const auto files = m_args->positionalArguments();
0128     for (const auto& file : files) {
0129         addToBackgroundParser(file, features);
0130     }
0131 
0132     m_allFilesAdded = 1;
0133 
0134     if (m_total) {
0135         std::cerr << "Added " << m_total << " files to the background parser" << std::endl;
0136         const int threads = ICore::self()->languageController()->backgroundParser()->threadCount();
0137         std::cerr << "parsing with " << threads << " threads" << std::endl;
0138         ICore::self()->languageController()->backgroundParser()->parseDocuments();
0139     } else {
0140         std::cerr << "no files added to the background parser" << std::endl;
0141         QCoreApplication::exit(0);
0142         return;
0143     }
0144 }
0145 
0146 void Manager::updateReady(const IndexedString& url, const ReferencedTopDUContext& topContext)
0147 {
0148     qDebug() << "finished" << url.toUrl().toLocalFile() << "success: " << ( bool )topContext;
0149 
0150     m_waiting.remove(url.toUrl());
0151 
0152     std::cerr << "processed " << (m_total - m_waiting.size()) << " out of " << m_total << "\n";
0153     dump(topContext);
0154 }
0155 
0156 void Manager::dump(const ReferencedTopDUContext& topContext)
0157 {
0158     if (!topContext) {
0159         return;
0160     }
0161 
0162     QTextStream stream(stdout);
0163 
0164     std::cerr << "\n";
0165 
0166     if (m_args->isSet(QStringLiteral("dump-definitions"))) {
0167         DUChainReadLocker lock;
0168         std::cerr << "Definitions:" << std::endl;
0169         DUChain::definitions()->dump(stream);
0170         std::cerr << std::endl;
0171     }
0172 
0173     if (m_args->isSet(QStringLiteral("dump-symboltable"))) {
0174         DUChainReadLocker lock;
0175         std::cerr << "PersistentSymbolTable:" << std::endl;
0176         PersistentSymbolTable::self().dump(stream);
0177         std::cerr << std::endl;
0178     }
0179 
0180     DUChainDumper::Features features;
0181     if (m_args->isSet(QStringLiteral("dump-context"))) {
0182         features |= DUChainDumper::DumpContext;
0183     }
0184     if (m_args->isSet(QStringLiteral("dump-errors"))) {
0185         features |= DUChainDumper::DumpProblems;
0186     }
0187 
0188     if (auto depth = m_args->value(QStringLiteral("dump-depth")).toInt()) {
0189         DUChainReadLocker lock;
0190         std::cerr << "Context:" << std::endl;
0191         DUChainDumper dumpChain(features);
0192         dumpChain.dump(topContext, depth);
0193     }
0194 
0195     if (m_args->isSet(QStringLiteral("dump-graph"))) {
0196         DUChainReadLocker lock;
0197         DumpDotGraph dumpGraph;
0198         const QString dotOutput = dumpGraph.dotGraph(topContext);
0199         std::cout << qPrintable(dotOutput) << std::endl;
0200     }
0201 
0202     if (m_args->isSet(QStringLiteral("dump-imported-errors"))) {
0203         DUChainReadLocker lock;
0204         const auto imports = topContext->importedParentContexts();
0205         for (const auto& import : imports) {
0206             auto top = dynamic_cast<TopDUContext*>(import.indexedContext().context());
0207             if (top && top != topContext && !top->problems().isEmpty()) {
0208                 DUChainDumper dumpChain(DUChainDumper::DumpProblems);
0209                 dumpChain.dump(top, 0);
0210             }
0211         }
0212     }
0213 }
0214 
0215 void Manager::addToBackgroundParser(const QString& path, TopDUContext::Features features)
0216 {
0217     QFileInfo info(path);
0218 
0219     if (info.isFile()) {
0220         qDebug() << "adding file" << path;
0221         QUrl pathUrl = QUrl::fromLocalFile(info.canonicalFilePath());
0222 
0223         m_waiting << pathUrl;
0224         ++m_total;
0225 
0226         KDevelop::DUChain::self()->updateContextForUrl(KDevelop::IndexedString(pathUrl), features, this);
0227 
0228     } else if (info.isDir()) {
0229         QDirIterator contents(path);
0230         while (contents.hasNext()) {
0231             QString newPath = contents.next();
0232             if (!newPath.endsWith(QLatin1Char('.')))
0233                 addToBackgroundParser(newPath, features);
0234         }
0235     }
0236 }
0237 
0238 QSet<QUrl> Manager::waiting()
0239 {
0240     return m_waiting;
0241 }
0242 
0243 void Manager::finish()
0244 {
0245     std::cerr << "ready" << std::endl;
0246     QCoreApplication::quit();
0247 }
0248 
0249 using namespace KDevelop;
0250 
0251 int main(int argc, char** argv)
0252 {
0253     QCoreApplication app(argc, argv);
0254 
0255     KAboutData aboutData(QStringLiteral("duchainify"), i18n("duchainify"),
0256         QStringLiteral("1"), i18n("DUChain builder application"), KAboutLicense::GPL,
0257         i18n("(c) 2009 David Nolden"), QString(), QStringLiteral("https://www.kdevelop.org/"));
0258     KAboutData::setApplicationData(aboutData);
0259 
0260     QCommandLineParser parser;
0261     aboutData.setupCommandLine(&parser);
0262 
0263     parser.addPositionalArgument(QStringLiteral("paths"), i18n("file or directory"), QStringLiteral("[PATH...]"));
0264 
0265     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("w"), QStringLiteral("warnings")},
0266                                         i18n("Show warnings")});
0267     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("V"), QStringLiteral("verbose")},
0268                                         i18n("Show warnings and debug output")});
0269     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("u"), QStringLiteral("force-update")},
0270                                         i18n("Enforce an update of the top-contexts corresponding to the given files")});
0271     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("r"), QStringLiteral(
0272                                                         "force-update-recursive")},
0273                                         i18n(
0274                                             "Enforce an update of the top-contexts corresponding to the given files and all included files")});
0275     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("t"), QStringLiteral("threads")},
0276                                         i18n("Number of threads to use"), QStringLiteral("count")});
0277     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("f"), QStringLiteral("features")},
0278                                         i18n(
0279                                             "Features to build. Options: empty, simplified-visible-declarations, visible-declarations (default), all-declarations, all-declarations-and-uses, all-declarations-and-uses-and-AST"),
0280                                         QStringLiteral("features")});
0281 
0282     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-context")},
0283                                         i18n("Print complete Definition-Use Chain on successful parse")});
0284     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-definitions")},
0285                                         i18n("Print complete DUChain Definitions repository on successful parse")});
0286     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-symboltable")},
0287                                         i18n(
0288                                             "Print complete DUChain PersistentSymbolTable repository on successful parse")});
0289     parser.addOption(
0290         QCommandLineOption { QStringList { QStringLiteral("dump-depth") },
0291                              i18n("Number defining the maximum depth where declaration details are printed"),
0292                              QStringLiteral("depth"), QStringLiteral("100") });
0293     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-graph")},
0294                                         i18n("Dump DUChain graph (in .dot format)")});
0295     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("d"), QStringLiteral("dump-errors")},
0296                                         i18n("Print problems encountered during parsing")});
0297     parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-imported-errors")},
0298                                         i18n("Recursively dump errors from imported contexts.")});
0299 
0300     parser.process(app);
0301 
0302     aboutData.processCommandLine(&parser);
0303 
0304     verbose = parser.isSet(QStringLiteral("verbose"));
0305     warnings = parser.isSet(QStringLiteral("warnings"));
0306     qInstallMessageHandler(messageOutput);
0307 
0308     AutoTestShell::init();
0309     TestCore::initialize(Core::NoUi, QStringLiteral("duchainify"));
0310     Manager manager(&parser);
0311 
0312     QTimer::singleShot(0, &manager, &Manager::init);
0313     int ret = app.exec();
0314 
0315     TestCore::shutdown();
0316 
0317     return ret;
0318 }
0319 
0320 #include "moc_main.cpp"