File indexing completed on 2024-05-05 04:40:49

0001 /*
0002     SPDX-FileCopyrightText: 2012 Aleix Pol <aleixpol@kde.org>
0003     SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "qmljsparsejob.h"
0009 
0010 #include <language/backgroundparser/urlparselock.h>
0011 #include <custom-definesandincludes/idefinesandincludesmanager.h>
0012 
0013 #include <language/duchain/duchainlock.h>
0014 #include <language/duchain/duchainutils.h>
0015 #include <language/duchain/duchain.h>
0016 #include <language/duchain/parsingenvironment.h>
0017 #include <language/interfaces/ilanguagesupport.h>
0018 #include <interfaces/icore.h>
0019 #include <interfaces/iprojectcontroller.h>
0020 #include <interfaces/iproject.h>
0021 #include <project/projectmodel.h>
0022 
0023 #include "duchain/cache.h"
0024 #include "duchain/declarationbuilder.h"
0025 #include "duchain/parsesession.h"
0026 #include "duchain/usebuilder.h"
0027 
0028 #include "debug.h"
0029 
0030 #include <QReadLocker>
0031 
0032 using namespace KDevelop;
0033 
0034 /*
0035  * This function has been copied from kdev-clang
0036  *
0037  * SPDX-FileCopyrightText: 2013 Olivier de Gaalon <olivier.jg@gmail.com> and Milian Wolff <mail@milianw.de>
0038  * Licensed under the GPL v2+
0039  */
0040 ProjectFileItem* findProjectFileItem(const IndexedString& url)
0041 {
0042     ProjectFileItem* file = nullptr;
0043 
0044     const auto& projects = ICore::self()->projectController()->projects();
0045     for (auto project: projects) {
0046         const auto files = project->filesForPath(url);
0047         if (files.isEmpty()) {
0048             continue;
0049         }
0050 
0051         file = files.last();
0052 
0053         // A file might be defined in different targets.
0054         // Prefer file items defined inside a target with non-empty includes.
0055         for (auto f: files) {
0056             if (!dynamic_cast<ProjectTargetItem*>(f->parent())) {
0057                 continue;
0058             }
0059             file = f;
0060             if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) {
0061                 break;
0062             }
0063         }
0064     }
0065     return file;
0066 }
0067 
0068 QmlJsParseJob::QmlJsParseJob(const IndexedString& url, ILanguageSupport* languageSupport)
0069 : ParseJob(url, languageSupport)
0070 {
0071     // Tell the cache that this file has custom include directories
0072     if (auto file = findProjectFileItem(url)) {
0073         QmlJS::Cache::instance().setFileCustomIncludes(
0074             url,
0075             IDefinesAndIncludesManager::manager()->includes(file,
0076                 IDefinesAndIncludesManager::Type(
0077                     IDefinesAndIncludesManager::ProjectSpecific | IDefinesAndIncludesManager::UserDefined))
0078         );
0079     } else {
0080         QmlJS::Cache::instance().setFileCustomIncludes(
0081             url,
0082             IDefinesAndIncludesManager::manager()->includes(url.str(),
0083                 IDefinesAndIncludesManager::ProjectSpecific)
0084         );
0085     }
0086 }
0087 
0088 void QmlJsParseJob::run(ThreadWeaver::JobPointer pointer, ThreadWeaver::Thread* thread)
0089 {
0090     Q_UNUSED(pointer)
0091     Q_UNUSED(thread)
0092 
0093     UrlParseLock urlLock(document());
0094     if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) {
0095         return;
0096     }
0097 
0098     // Don't parse this file if one of its dependencies is not up to date
0099     const auto& dependencies = QmlJS::Cache::instance().dependencies(document());
0100     for (auto& dependency : dependencies) {
0101         if (!QmlJS::Cache::instance().isUpToDate(dependency)) {
0102             QmlJS::Cache::instance().setUpToDate(document(), false);
0103             return;
0104         }
0105     }
0106 
0107     qCDebug(KDEV_QMLJS) << "parsing" << document().str();
0108 
0109     ProblemPointer p = readContents();
0110     if (p) {
0111         //TODO: associate problem with topducontext
0112         return;
0113     }
0114 
0115     ParseSession session(document(), QString::fromUtf8(contents().contents), priority());
0116 
0117     if (abortRequested()) {
0118         return;
0119     }
0120 
0121     ReferencedTopDUContext context;
0122     {
0123         DUChainReadLocker lock;
0124         context = DUChainUtils::standardContextForUrl(document().toUrl());
0125     }
0126     if (context) {
0127         translateDUChainToRevision(context);
0128         context->setRange(RangeInRevision(0, 0, INT_MAX, INT_MAX));
0129     }
0130 
0131     if (session.ast()) {
0132         QReadLocker parseLock(languageSupport()->parseLock());
0133 
0134         if (abortRequested()) {
0135             abortJob();
0136             return;
0137         }
0138 
0139         DeclarationBuilder builder(&session);
0140         context = builder.build(document(), session.ast(), context);
0141 
0142         if (abortRequested()) {
0143             abortJob();
0144             return;
0145         }
0146 
0147         if ( context && (minimumFeatures() & TopDUContext::AllDeclarationsContextsAndUses) ) {
0148             UseBuilder useBuilder(&session);
0149             useBuilder.buildUses(session.ast());
0150         }
0151     }
0152 
0153     if (abortRequested()) {
0154         abortJob();
0155         return;
0156     }
0157 
0158     if (!context) {
0159         DUChainWriteLocker lock;
0160         auto* file = new ParsingEnvironmentFile(document());
0161         file->setLanguage(ParseSession::languageString());
0162         context = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file);
0163         DUChain::self()->addDocumentChain(context);
0164     }
0165 
0166     setDuChain(context);
0167 
0168     // If the file has become up to date, reparse its importers
0169     bool dependenciesOk = session.allDependenciesSatisfied();
0170 
0171     QmlJS::Cache::instance().setUpToDate(document(), dependenciesOk);
0172 
0173     if (dependenciesOk) {
0174         session.reparseImporters();
0175     }
0176 
0177     {
0178         DUChainWriteLocker lock;
0179         context->setProblems(session.problems());
0180 
0181         context->setFeatures(minimumFeatures());
0182         ParsingEnvironmentFilePointer file = context->parsingEnvironmentFile();
0183         Q_ASSERT(file);
0184         file->setModificationRevision(contents().modification);
0185         DUChain::self()->updateContextEnvironment( context->topContext(), file.data() );
0186     }
0187     highlightDUChain();
0188 
0189     DUChain::self()->emitUpdateReady(document(), duChain());
0190 
0191     if (session.isParsedCorrectly()) {
0192         qCDebug(KDEV_QMLJS) << "===Success===" << document().str();
0193     } else {
0194         qCDebug(KDEV_QMLJS) << "===Failed===" << document().str() << session.problems();
0195     }
0196 }
0197 
0198 #include "moc_qmljsparsejob.cpp"