File indexing completed on 2024-03-24 04:38:28
0001 /* This file is part of KDevelop 0002 * 0003 * Copyright 2008-2010 Alexander Dymo <adymo@kdevelop.org> 0004 * Copyright (C) 2011-2015 Miquel Sabaté Solà <mikisabate@gmail.com> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU Library General Public License as 0008 * published by the Free Software Foundation; either version 2 of the 0009 * License, or (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public 0017 * License along with this program; if not, write to the 0018 * Free Software Foundation, Inc., 0019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include <parsejob.h> 0023 0024 #include <mutex> 0025 0026 #include <QtCore/QReadLocker> 0027 0028 #include <interfaces/icore.h> 0029 #include <interfaces/ilanguagecontroller.h> 0030 #include <language/backgroundparser/backgroundparser.h> 0031 #include <language/backgroundparser/urlparselock.h> 0032 #include <language/duchain/duchainutils.h> 0033 0034 #include <debug.h> 0035 #include <duchain/builders/declarationbuilder.h> 0036 #include <duchain/builders/usebuilder.h> 0037 #include <duchain/editorintegrator.h> 0038 #include <duchain/helpers.h> 0039 #include <languagesupport.h> 0040 #include <parser/parser.h> 0041 0042 using namespace KDevelop; 0043 namespace ruby { 0044 0045 ParseJob::ParseJob(const IndexedString &url, ILanguageSupport *languageSupport) 0046 : KDevelop::ParseJob(url, languageSupport) 0047 , m_parser(nullptr) 0048 , m_duContext (nullptr) 0049 { 0050 } 0051 0052 ParseJob::~ParseJob() 0053 { 0054 delete m_parser; 0055 } 0056 0057 LanguageSupport * ParseJob::ruby() const 0058 { 0059 return dynamic_cast<LanguageSupport *>(languageSupport()); 0060 } 0061 0062 void ParseJob::run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *) 0063 { 0064 // Make sure that the builtins file is already loaded. 0065 if (document() != builtinsFile()) { 0066 const auto &langSupport = languageSupport(); 0067 static std::once_flag once; 0068 0069 std::call_once(once, [langSupport] { 0070 qCDebug(KDEV_RUBY) << "Initializing internal function file" << builtinsFile(); 0071 ParseJob internalJob(builtinsFile(), langSupport); 0072 internalJob.setMinimumFeatures(TopDUContext::AllDeclarationsAndContexts); 0073 internalJob.run({}, nullptr); 0074 Q_ASSERT(internalJob.success()); 0075 }); 0076 } 0077 0078 UrlParseLock urlLock(document()); 0079 if (!(minimumFeatures() & Rescheduled) && 0080 !isUpdateRequired(languageString())) { 0081 return; 0082 } 0083 0084 QReadLocker parseLock(ruby()->parseLock()); 0085 ProblemPointer p = readContents(); 0086 if (p || abortRequested()) { 0087 return abortJob(); 0088 } 0089 0090 // NOTE: Although the parser can retrieve the contents on its own, 0091 // it's better to use contents().contents because this way the contents 0092 // are converted in utf8 format always. 0093 m_parser = new Parser(document(), contents().contents); 0094 m_parser->setRubyVersion(ruby()->version()); 0095 Ast *ast = m_parser->parse(); 0096 0097 /* Setting up the TopDUContext features */ 0098 ReferencedTopDUContext toUpdate; 0099 { 0100 DUChainReadLocker lock; 0101 toUpdate = DUChainUtils::standardContextForUrl(document().toUrl()); 0102 } 0103 0104 TopDUContext::Features newFeatures = minimumFeatures(); 0105 if (toUpdate) { 0106 newFeatures |= toUpdate->features(); 0107 } 0108 0109 /* Remove update-flags like 'Recursive' or 'ForceUpdate' */ 0110 newFeatures &= TopDUContext::AllDeclarationsContextsUsesAndAST; 0111 0112 // And finally we do all the work if parsing was successful. Otherwise, 0113 // we have to add a new problem 0114 if (ast) { 0115 // Empty document, do nothing 0116 if (!ast->tree) { 0117 return; 0118 } 0119 if (abortRequested()) { 0120 return abortJob(); 0121 } 0122 0123 EditorIntegrator editor; 0124 editor.setParseSession(m_parser); 0125 DeclarationBuilder builder(&editor); 0126 builder.setPriority(parsePriority()); 0127 m_duContext = builder.build(editor.url(), ast, toUpdate); 0128 0129 // Add warnings 0130 DUChainWriteLocker wlock; 0131 for (const ProblemPointer p : m_parser->problems) { 0132 m_duContext->addProblem(p); 0133 } 0134 wlock.unlock(); 0135 setDuChain(m_duContext); 0136 0137 if (abortRequested()) { 0138 return abortJob(); 0139 } 0140 0141 if (newFeatures & TopDUContext::AllDeclarationsContextsAndUses && 0142 document() != builtinsFile()) { 0143 0144 UseBuilder useBuilder(&editor); 0145 useBuilder.setPriority(parsePriority()); 0146 useBuilder.buildUses(ast); 0147 } 0148 0149 if (abortRequested()) { 0150 return abortJob(); 0151 } 0152 0153 const auto &unresolvedImports = builder.unresolvedImports(); 0154 if (!unresolvedImports.isEmpty()) { 0155 // Check whether one of the imports is queued for parsing, this 0156 // is to avoid deadlocks 0157 bool dependencyInQueue = false; 0158 for (const IndexedString &url : unresolvedImports) { 0159 dependencyInQueue = KDevelop::ICore::self()-> 0160 languageController()->backgroundParser()->isQueued(url); 0161 if (dependencyInQueue) { 0162 break; 0163 } 0164 } 0165 0166 // We check whether this document already has been re-scheduled 0167 // once and abort if that is the case. This prevents infinite 0168 // loops in case something goes wrong (optimally, shouldn't reach 0169 // here if the document was already rescheduled, but there's many 0170 // cases where this might still happen) 0171 if (!(minimumFeatures() & Rescheduled) && dependencyInQueue) { 0172 wlock.lock(); 0173 constexpr TopDUContext::Features features{TopDUContext::ForceUpdate}; 0174 ICore::self()->languageController()->backgroundParser()->addDocument( 0175 document(), 0176 static_cast<TopDUContext::Features>(features | Rescheduled), 0177 parsePriority(), 0178 nullptr, 0179 ParseJob::FullSequentialProcessing 0180 ); 0181 wlock.unlock(); 0182 } 0183 } 0184 0185 if (abortRequested()) { 0186 return abortJob(); 0187 } 0188 0189 wlock.lock(); 0190 m_duContext->setFeatures(newFeatures); 0191 ParsingEnvironmentFilePointer file = m_duContext->parsingEnvironmentFile(); 0192 file->setModificationRevision(contents().modification); 0193 DUChain::self()->updateContextEnvironment(m_duContext, file.data()); 0194 wlock.unlock(); 0195 0196 highlightDUChain(); 0197 qCDebug(KDEV_RUBY) << "**** Parsing Succeeded ****"; 0198 } else { 0199 qCWarning(KDEV_RUBY) << "**** Parsing Failed ****"; 0200 DUChainWriteLocker lock; 0201 m_duContext = DUChain::self()->chainForDocument(document()); 0202 if (m_duContext) { 0203 m_duContext->parsingEnvironmentFile()->clearModificationRevisions(); 0204 m_duContext->clearProblems(); 0205 } else { 0206 ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document()); 0207 file->setLanguage(languageString()); 0208 m_duContext = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); 0209 DUChain::self()->addDocumentChain(m_duContext); 0210 } 0211 0212 for (const ProblemPointer p : m_parser->problems) { 0213 qCDebug(KDEV_RUBY) << "Added problem to context"; 0214 m_duContext->addProblem(p); 0215 } 0216 setDuChain(m_duContext); 0217 } 0218 DUChain::self()->emitUpdateReady(document(), duChain()); 0219 } 0220 0221 } 0222