1 /*
2 SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
3 SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
4 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "phpparsejob.h"
10 #include <QFile>
11 #include <QReadWriteLock>
12
13 #include <ktexteditor/document.h>
14
15 #include <kdebug.h>
16 #include <klocale.h>
17
18 #include <language/duchain/duchainlock.h>
19 #include <language/duchain/duchain.h>
20 #include <language/duchain/topducontext.h>
21 #include <language/duchain/dumpchain.h>
22 #include <interfaces/ilanguage.h>
23 #include <interfaces/icore.h>
24 #include <interfaces/ilanguagecontroller.h>
25 #include <language/backgroundparser/backgroundparser.h>
26 #include <language/backgroundparser/urlparselock.h>
27
28 #include "editorintegrator.h"
29 #include "parsesession.h"
30 #include "phplanguagesupport.h"
31 #include "phpdebugvisitor.h"
32 #include "duchain/builders/declarationbuilder.h"
33 #include "duchain/builders/usebuilder.h"
34 #include "duchain/helper.h"
35 #include "phpducontext.h"
36
37 #include <QtCore/QReadLocker>
38 #include <QtCore/QThread>
39 #include <language/duchain/duchainutils.h>
40
41 using namespace KDevelop;
42
43 namespace Php
44 {
45
ParseJob(const IndexedString & url,ILanguageSupport * languageSupport)46 ParseJob::ParseJob(const IndexedString& url, ILanguageSupport* languageSupport)
47 : KDevelop::ParseJob(url, languageSupport)
48 , m_parentJob(0)
49 {
50 }
51
~ParseJob()52 ParseJob::~ParseJob()
53 {
54 }
55
php() const56 LanguageSupport* ParseJob::php() const
57 {
58 return dynamic_cast<LanguageSupport*>(languageSupport());
59 }
60
run()61 void ParseJob::run()
62 {
63 /// Indexed string for 'Php', identifies environment files from this language plugin
64 static const IndexedString phpLangString("Php");
65
66 // make sure we loaded the internal file already
67 if ( !php()->internalFunctionsLoaded() && !m_parentJob && document() != internalFunctionFile() ) {
68 kDebug() << "waiting for internal function file to finish parsing";
69 QReadLocker(php()->internalFunctionsLock());
70 }
71
72 UrlParseLock urlLock(document());
73
74 if (!(minimumFeatures() & Resheduled) && !isUpdateRequired(phpLangString)) {
75 return;
76 }
77 kDebug() << "parsing" << document().str();
78
79 KDevelop::ProblemPointer p = readContents();
80 if (p) {
81 //TODO: associate problem with topducontext
82 return abortJob();;
83 }
84
85 ParseSession session;
86 //TODO: support different charsets
87 session.setContents(QString::fromUtf8(contents().contents));
88 session.setCurrentDocument(document());
89
90 // 2) parse
91 StartAst* ast = 0;
92 bool matched = session.parse(&ast);
93
94 if (abortRequested() || ICore::self()->shuttingDown()) {
95 return abortJob();
96 }
97
98 KDevelop::ReferencedTopDUContext toUpdate;
99 {
100 KDevelop::DUChainReadLocker duchainlock(KDevelop::DUChain::lock());
101 toUpdate = KDevelop::DUChainUtils::standardContextForUrl(document().toUrl());
102 }
103
104 KDevelop::TopDUContext::Features newFeatures = minimumFeatures();
105 if (toUpdate)
106 newFeatures = (KDevelop::TopDUContext::Features)(newFeatures | toUpdate->features());
107
108 //Remove update-flags like 'Recursive' or 'ForceUpdate'
109 newFeatures = static_cast<KDevelop::TopDUContext::Features>(newFeatures & KDevelop::TopDUContext::AllDeclarationsContextsUsesAndAST);
110
111 if (matched) {
112 if (abortRequested()) {
113 return abortJob();
114 }
115
116 EditorIntegrator editor(&session);
117
118 QReadLocker parseLock(php()->language()->parseLock());
119
120 DeclarationBuilder builder(&editor);
121 KDevelop::ReferencedTopDUContext chain = builder.build(document(), ast, toUpdate);
122
123 if (abortRequested()) {
124 return abortJob();
125 }
126
127 setDuChain(chain);
128
129 bool hadUnresolvedIdentifiers = builder.hadUnresolvedIdentifiers();
130
131 if ( newFeatures & TopDUContext::AllDeclarationsContextsAndUses
132 && document() != internalFunctionFile() )
133 {
134 UseBuilder useBuilder(&editor);
135 useBuilder.buildUses(ast);
136
137 if (useBuilder.hadUnresolvedIdentifiers())
138 hadUnresolvedIdentifiers = true;
139 }
140
141 if (hadUnresolvedIdentifiers) {
142 if (!(minimumFeatures() & Resheduled) && KDevelop::ICore::self()->languageController()->backgroundParser()->queuedCount()) {
143 // Need to create new parse job with lower priority
144 kDebug() << "Reschedule file " << document().str() << "for parsing";
145 KDevelop::TopDUContext::Features feat = static_cast<KDevelop::TopDUContext::Features>(
146 minimumFeatures() | KDevelop::TopDUContext::VisibleDeclarationsAndContexts | Resheduled
147 );
148 int priority = qMin(parsePriority()+100, (int)KDevelop::BackgroundParser::WorstPriority);
149 KDevelop::ICore::self()->languageController()->backgroundParser()
150 ->addDocument(document(), feat, priority);
151
152 } else {
153 // We haven't resolved all identifiers, but by now, we don't expect to
154 kDebug() << "Builder found unresolved identifiers when they should have been resolved! (if there was no coding error)";
155 }
156 }
157
158 if (abortRequested()) {
159 return abortJob();
160 }
161
162 if (abortRequested()) {
163 return abortJob();
164 }
165
166 {
167 DUChainWriteLocker lock(DUChain::lock());
168
169 foreach(const ProblemPointer &p, session.problems()) {
170 chain->addProblem(p);
171 }
172
173 chain->setFeatures(newFeatures);
174 ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
175 file->setModificationRevision(contents().modification);
176 DUChain::self()->updateContextEnvironment( chain->topContext(), file.data() );
177 }
178
179 highlightDUChain();
180 } else {
181 ReferencedTopDUContext top;
182 DUChainWriteLocker lock;
183 {
184 top = DUChain::self()->chainForDocument(document());
185 }
186 if (top) {
187 ///NOTE: if we clear the imported parent contexts, autocompletion of built-in PHP stuff won't work!
188 //top->clearImportedParentContexts();
189 top->parsingEnvironmentFile()->clearModificationRevisions();
190 top->clearProblems();
191 } else {
192 ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document());
193 file->setLanguage(phpLangString);
194 top = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file);
195 DUChain::self()->addDocumentChain(top);
196 }
197 foreach(const ProblemPointer &p, session.problems()) {
198 top->addProblem(p);
199 }
200 setDuChain(top);
201 kDebug() << "===Failed===" << document().str();
202 }
203 }
204
setParentJob(ParseJob * job)205 void ParseJob::setParentJob(ParseJob *job)
206 {
207 m_parentJob = job;
208 }
209
210
hasParentDocument(const IndexedString & doc)211 bool ParseJob::hasParentDocument(const IndexedString &doc)
212 {
213 if (document() == doc) return true;
214 if (!m_parentJob) return false;
215 if (m_parentJob->document() == doc) return true;
216 return m_parentJob->hasParentDocument(doc);
217 }
218
createProblem(const QString & description,AstNode * node,EditorIntegrator * editor,ProblemData::Source source,ProblemData::Severity severity)219 ProblemPointer ParseJob::createProblem(const QString &description, AstNode* node,
220 EditorIntegrator * editor, ProblemData::Source source,
221 ProblemData::Severity severity)
222 {
223 ProblemPointer p(new Problem());
224 p->setSource(source);
225 p->setSeverity(severity);
226 p->setDescription(description);
227 p->setFinalLocation(DocumentRange(document(), editor->findRange(node).castToSimpleRange()));
228 kDebug() << p->description();
229 return p;
230 }
231
232 }
233
234 #include "phpparsejob.moc"
235 // kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on
236