File indexing completed on 2023-05-30 11:20:06
0001 /* 0002 SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com> 0003 SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com> 0004 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "phpparsejob.h" 0010 #include <QFile> 0011 #include <QReadWriteLock> 0012 0013 #include <ktexteditor/document.h> 0014 0015 #include <language/duchain/duchainlock.h> 0016 #include <language/duchain/duchain.h> 0017 #include <language/duchain/topducontext.h> 0018 #include <interfaces/icore.h> 0019 #include <interfaces/ilanguagecontroller.h> 0020 #include <language/backgroundparser/backgroundparser.h> 0021 #include <language/backgroundparser/urlparselock.h> 0022 #include <language/editor/documentrange.h> 0023 0024 #include "editorintegrator.h" 0025 #include "parsesession.h" 0026 #include "phplanguagesupport.h" 0027 #include "phpdebugvisitor.h" 0028 #include "duchain/builders/declarationbuilder.h" 0029 #include "duchain/builders/usebuilder.h" 0030 #include "duchain/helper.h" 0031 #include "phpducontext.h" 0032 #include "phpdebug.h" 0033 0034 #include <QReadLocker> 0035 #include <QThread> 0036 #include <language/duchain/duchainutils.h> 0037 0038 #include <mutex> 0039 #include <memory> 0040 0041 using namespace KDevelop; 0042 0043 namespace Php 0044 { 0045 0046 ParseJob::ParseJob(const IndexedString& url, ILanguageSupport* languageSupport) 0047 : KDevelop::ParseJob(url, languageSupport) 0048 , m_parentJob(nullptr) 0049 { 0050 } 0051 0052 ParseJob::~ParseJob() 0053 { 0054 } 0055 0056 LanguageSupport* ParseJob::php() const 0057 { 0058 return dynamic_cast<LanguageSupport*>(languageSupport()); 0059 } 0060 0061 void ParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread * /*thread*/) 0062 { 0063 if (document() != internalFunctionFile()) { 0064 // make sure we loaded the internal file already 0065 const auto &phpSupport = languageSupport(); 0066 static std::once_flag once; 0067 std::call_once(once, [phpSupport] { 0068 qCDebug(PHP) << "Initializing internal function file" << internalFunctionFile(); 0069 auto internalJob = std::unique_ptr<ParseJob>(static_cast<ParseJob*>(phpSupport->createParseJob(internalFunctionFile()))); 0070 internalJob->run({}, nullptr); 0071 Q_ASSERT(internalJob->success()); 0072 }); 0073 } 0074 0075 UrlParseLock urlLock(document()); 0076 0077 if (!(minimumFeatures() & Rescheduled) && !isUpdateRequired(phpLanguageString())) { 0078 return; 0079 } 0080 qCDebug(PHP) << "parsing" << document().str(); 0081 0082 KDevelop::ProblemPointer p = readContents(); 0083 if (p) { 0084 //TODO: associate problem with topducontext 0085 return abortJob();; 0086 } 0087 0088 ParseSession session; 0089 //TODO: support different charsets 0090 session.setContents(QString::fromUtf8(contents().contents)); 0091 session.setCurrentDocument(document()); 0092 0093 // 2) parse 0094 StartAst* ast = nullptr; 0095 bool matched = session.parse(&ast); 0096 0097 if (abortRequested() || ICore::self()->shuttingDown()) { 0098 return abortJob(); 0099 } 0100 0101 KDevelop::ReferencedTopDUContext toUpdate; 0102 { 0103 KDevelop::DUChainReadLocker duchainlock(KDevelop::DUChain::lock()); 0104 toUpdate = KDevelop::DUChainUtils::standardContextForUrl(document().toUrl()); 0105 } 0106 0107 KDevelop::TopDUContext::Features newFeatures = minimumFeatures(); 0108 if (toUpdate) 0109 newFeatures |= toUpdate->features(); 0110 0111 //Remove update-flags like 'Recursive' or 'ForceUpdate' 0112 newFeatures &= KDevelop::TopDUContext::AllDeclarationsContextsUsesAndAST; 0113 0114 if (matched) { 0115 if (abortRequested()) { 0116 return abortJob(); 0117 } 0118 0119 EditorIntegrator editor(&session); 0120 0121 QReadLocker parseLock(php()->parseLock()); 0122 0123 DeclarationBuilder builder(&editor); 0124 KDevelop::ReferencedTopDUContext chain = builder.build(document(), ast, toUpdate); 0125 0126 if (abortRequested()) { 0127 return abortJob(); 0128 } 0129 0130 setDuChain(chain); 0131 0132 bool hadUnresolvedIdentifiers = builder.hadUnresolvedIdentifiers(); 0133 0134 if ( newFeatures & TopDUContext::AllDeclarationsContextsAndUses 0135 && document() != internalFunctionFile() ) 0136 { 0137 UseBuilder useBuilder(&editor); 0138 useBuilder.buildUses(ast); 0139 0140 if (useBuilder.hadUnresolvedIdentifiers()) 0141 hadUnresolvedIdentifiers = true; 0142 } 0143 0144 if (hadUnresolvedIdentifiers) { 0145 if (!(minimumFeatures() & Rescheduled)) { 0146 // Need to create new parse job with lower priority 0147 qCDebug(PHP) << "Reschedule file " << document().str() << "for parsing"; 0148 KDevelop::TopDUContext::Features feat = static_cast<KDevelop::TopDUContext::Features>( 0149 minimumFeatures() | KDevelop::TopDUContext::VisibleDeclarationsAndContexts | Rescheduled 0150 ); 0151 int priority = qMin(parsePriority()+100, (int)KDevelop::BackgroundParser::WorstPriority); 0152 KDevelop::ICore::self()->languageController()->backgroundParser() 0153 ->addDocument(document(), feat, priority); 0154 0155 } else { 0156 // We haven't resolved all identifiers, but by now, we don't expect to 0157 qCDebug(PHP) << "Builder found unresolved identifiers when they should have been resolved! (if there was no coding error)"; 0158 } 0159 } 0160 0161 if (abortRequested()) { 0162 return abortJob(); 0163 } 0164 0165 if (abortRequested()) { 0166 return abortJob(); 0167 } 0168 0169 { 0170 DUChainWriteLocker lock(DUChain::lock()); 0171 0172 foreach(const ProblemPointer &p, session.problems()) { 0173 chain->addProblem(p); 0174 } 0175 0176 chain->setFeatures(newFeatures); 0177 ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile(); 0178 file->setModificationRevision(contents().modification); 0179 DUChain::self()->updateContextEnvironment( chain->topContext(), file.data() ); 0180 } 0181 0182 highlightDUChain(); 0183 } else { 0184 ReferencedTopDUContext top; 0185 DUChainWriteLocker lock; 0186 { 0187 top = DUChain::self()->chainForDocument(document()); 0188 } 0189 if (top) { 0190 ///NOTE: if we clear the imported parent contexts, autocompletion of built-in PHP stuff won't work! 0191 //top->clearImportedParentContexts(); 0192 top->parsingEnvironmentFile()->clearModificationRevisions(); 0193 top->clearProblems(); 0194 } else { 0195 ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document()); 0196 file->setLanguage(phpLanguageString()); 0197 top = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); 0198 DUChain::self()->addDocumentChain(top); 0199 } 0200 foreach(const ProblemPointer &p, session.problems()) { 0201 top->addProblem(p); 0202 } 0203 setDuChain(top); 0204 qCDebug(PHP) << "===Failed===" << document().str(); 0205 } 0206 0207 DUChain::self()->emitUpdateReady(document(), duChain()); 0208 } 0209 0210 void ParseJob::setParentJob(ParseJob *job) 0211 { 0212 m_parentJob = job; 0213 } 0214 0215 0216 bool ParseJob::hasParentDocument(const IndexedString &doc) 0217 { 0218 if (document() == doc) return true; 0219 if (!m_parentJob) return false; 0220 if (m_parentJob->document() == doc) return true; 0221 return m_parentJob->hasParentDocument(doc); 0222 } 0223 0224 ProblemPointer ParseJob::createProblem(const QString &description, AstNode* node, 0225 EditorIntegrator * editor, IProblem::Source source, 0226 IProblem::Severity severity) 0227 { 0228 ProblemPointer p(new Problem()); 0229 p->setSource(source); 0230 p->setSeverity(severity); 0231 p->setDescription(description); 0232 p->setFinalLocation(DocumentRange(document(), editor->findRange(node).castToSimpleRange())); 0233 qCDebug(PHP) << p->description(); 0234 return p; 0235 } 0236 0237 } 0238 0239 // kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on