Improve contentContextFromProxyContext so that it never returns the wrong content...
[kdevelop:kdevelop.git] / languages / cpp / cppparsejob.cpp
1 /*
2 * This file is part of KDevelop
3 *
4 * Copyright 2006 Adam Treat <treat@kde.org>
5 * Copyright 2006-2007 Hamish Rodda <rodda@kde.org>
6 * Copyright 2007-2008 David Nolden<david.nolden@kdevelop.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the
20 * Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24 #include "cppparsejob.h"
25
26 #include <QFile>
27 #include <QByteArray>
28 #include <QReadWriteLock>
29 #include <QReadLocker>
30
31 #include <kdebug.h>
32 #include <klocale.h>
33
34 #include <threadweaver/Thread.h>
35
36 #include <interfaces/ilanguage.h>
37 #include <language/interfaces/iastcontainer.h>
38
39 #include "cpplanguagesupport.h"
40 #include "cpphighlighting.h"
41
42 // #include "parser/binder.h"
43 #include "parser/parser.h"
44 #include "parser/control.h"
45 #include "parser/dumptree.h"
46 #include <language/duchain/duchain.h>
47 #include <language/duchain/duchainpointer.h>
48 #include <language/duchain/duchainlock.h>
49 #include <language/duchain/dumpdotgraph.h>
50 #include <language/duchain/dumpchain.h>
51 #include <language/backgroundparser/parsejob.h>
52 #include <language/backgroundparser/urlparselock.h>
53 #include "cppeditorintegrator.h"
54 #include "declarationbuilder.h"
55 #include "usebuilder.h"
56 #include <language/duchain/topducontext.h>
57 #include "preprocessjob.h"
58 #include "environmentmanager.h"
59 #include <unistd.h>
60 #include <qwaitcondition.h>
61 #include <language/duchain/duchainutils.h>
62 #include "includepathcomputer.h"
63 #include <interfaces/iuicontroller.h>
64 #include <cpppreprocessenvironment.h>
65
66 //#define DUMP_AST
67 //#define DUMP_DUCHAIN
68
69 using namespace KDevelop;
70
71 bool importsContext(const QVector<DUContext::Import>& contexts, const DUContext* context) {
72   foreach(const DUContext::Import &listCtx, contexts)
73     if(listCtx.context(0) && listCtx.context(0)->imports(context))
74       return true;
75   return false;
76 }
77
78 QList<IndexedString> convertFromUrls(const QList<KUrl>& urlList) {
79   QList<IndexedString> ret;
80   foreach(const KUrl& url, urlList)
81     ret << IndexedString(url.pathOrUrl());
82   return ret;
83 }
84
85 bool CPPParseJob::needUpdateEverything() const {
86   return m_needUpdateEverything;
87 }
88
89 bool CPPParseJob::wasUpdated(const KDevelop::DUContext* context) const
90 {
91   return m_updated.contains(context);
92 }
93 void CPPParseJob::setWasUpdated(const KDevelop::DUContext* context)
94 {
95   m_updated.insert(context);
96 }
97
98 const QSet<const KDevelop::DUContext*>& CPPParseJob::updated() const
99 {
100   return m_updated;
101 }
102
103
104 void CPPParseJob::setNeedUpdateEverything(bool need) {
105   m_needUpdateEverything = need;
106 }
107
108 CPPParseJob::CPPParseJob( const KUrl &url,
109                     PreprocessJob* parentPreprocessor )
110         : KDevelop::ParseJob( url ),
111         m_needUpdateEverything( false ),
112         m_parentPreprocessor( parentPreprocessor ),
113         m_session( new ParseSession ),
114         m_includePathsComputed( 0 ),
115         m_keepDuchain( false ),
116         m_parsedIncludes( 0 ),
117         m_needsUpdate( true )
118 {
119     if( !m_parentPreprocessor ) {
120         addJob(m_preprocessJob = new PreprocessJob(this));
121         addJob(m_parseJob = new CPPInternalParseJob(this));
122     } else {
123         m_preprocessJob = 0;
124         m_parseJob = 0;
125         //The preprocessor will call parseForeground() to preprocess & parse instantly
126     }
127 }
128
129 void CPPParseJob::setKeepDuchain(bool b) {
130     m_keepDuchain = b;
131 }
132
133 bool CPPParseJob::keepDuchain() const {
134     return m_keepDuchain;
135 }
136
137 void CPPParseJob::setKeepEverything(bool b) {
138     m_keepEverything = b;
139 }
140
141 bool CPPParseJob::keepEverything() const {
142     return m_keepEverything;
143 }
144
145 void CPPParseJob::includedFileParsed() {
146   ++m_parsedIncludes;
147   const int estimateIncludes = 450;
148   float _progress = ((float)m_parsedIncludes) / estimateIncludes;
149   if(_progress > 0.8)
150     _progress = 0.8;
151
152   emit progress(this, _progress, i18n("Parsing included files"));
153 }
154
155 void CPPParseJob::setLocalProgress(float _progress, QString text) {
156   emit progress(this, 0.8+_progress*0.2, text);
157 }
158
159 void CPPParseJob::setNeedsUpdate(bool needs) {
160   m_needsUpdate = needs;
161 }
162
163 bool CPPParseJob::needsUpdate() const {
164   return m_needsUpdate;
165 }
166
167 void CPPParseJob::parseForeground() {
168     //Create the sub-jobs and directly execute them.
169     Q_ASSERT( !m_preprocessJob && !m_parseJob );
170
171     m_preprocessJob = new PreprocessJob(this);
172     m_parseJob = new CPPInternalParseJob(this);
173     m_preprocessJob->run();
174     m_parseJob->run();
175 }
176
177 KUrl CPPParseJob::includedFromPath() const {
178     return m_includedFromPath;
179 }
180
181 void CPPParseJob::setIncludedFromPath( const KUrl& path ) {
182     m_includedFromPath = path;
183 }
184
185 PreprocessJob* CPPParseJob::parentPreprocessor() const {
186     return m_parentPreprocessor;
187 }
188
189 void CPPParseJob::gotIncludePaths(IncludePathComputer* comp) {
190   m_includePathsComputed = comp;
191   m_waitForIncludePathsMutex.lock(); //This makes sure that the parse thread goes into the waiting state first
192   m_waitForIncludePathsMutex.unlock();
193   m_waitForIncludePaths.wakeAll();
194 }
195
196
197 const KUrl::List& CPPParseJob::includePathUrls() const {
198   includePaths();
199   return masterJob()->m_includePathUrls;
200 }
201
202 void CPPParseJob::mergeDefines(CppPreprocessEnvironment& env) const
203 {
204   //m_includePathsComputed is filled when includePaths() is called
205   masterJob()->includePaths();
206   
207   if(ICore::self()->shuttingDown())
208     return; //If the system is shutting down, include-paths were not computed properly
209   
210   Q_ASSERT(masterJob()->m_includePathsComputed);
211   
212   QHash<QString, QString> defines = masterJob()->m_includePathsComputed->defines();
213   
214   ///@todo Most probably, the same macro-sets will be calculated again and again.
215   ///           One ReferenceCountedMacroSet would be enough.
216   
217   kDebug() << "DEFINES:" << defines;
218   
219   for(QHash<QString, QString>::const_iterator it = defines.constBegin(); it != defines.constEnd(); ++it)
220   {
221     rpp::pp_macro* m = new rpp::pp_macro(IndexedString(it.key()));
222     m->setDefinitionText( *it );
223     
224     //Call rpp::Environment::setMacro directly, so we don't add the macro to the environment-file.
225     //It should be only part of the environment.
226     env.rpp::Environment::setMacro(m);
227   }
228 }
229
230 const QList<IndexedString>& CPPParseJob::includePaths() const {
231     //If a lock was held here, we would get deadlocks
232     if( ICore::self()->shuttingDown() )
233       return m_includePaths;
234     
235     if( masterJob() == this ) {
236         if( !m_includePathsComputed ) {
237             Q_ASSERT(!DUChain::lock()->currentThreadHasReadLock() && !DUChain::lock()->currentThreadHasWriteLock());
238             m_waitForIncludePathsMutex.lock();
239             qRegisterMetaType<CPPParseJob*>("CPPParseJob*");
240             QMetaObject::invokeMethod(cpp(), "findIncludePathsForJob", Qt::QueuedConnection, Q_ARG(CPPParseJob*, const_cast<CPPParseJob*>(this)));
241             //Will be woken once the include-paths are computed
242             while(!m_waitForIncludePaths.wait(&m_waitForIncludePathsMutex, 1000))
243             {
244               if(ICore::self()->shuttingDown())
245               {
246                 return m_includePaths;
247               }
248             }
249             m_waitForIncludePathsMutex.unlock();
250             Q_ASSERT(m_includePathsComputed);
251             m_includePathsComputed->computeBackground();
252             m_includePathUrls = m_includePathsComputed->result();
253             m_includePaths = convertFromUrls(m_includePathUrls);
254
255         }
256         return m_includePaths;
257     } else {
258         return masterJob()->includePaths();
259     }
260 }
261
262 CPPParseJob::~CPPParseJob()
263 {
264   delete m_includePathsComputed;
265 }
266
267 KDevelop::ModificationRevisionSet CPPParseJob::includePathDependencies() const {
268   if(m_includePathsComputed)
269     return m_includePathsComputed->m_includePathDependency;
270
271   return KDevelop::ModificationRevisionSet();
272 }
273
274 CPPParseJob* CPPParseJob::masterJob() {
275     if( parentPreprocessor() )
276         return static_cast<CPPParseJob*>(parentPreprocessor()->parent())->masterJob();
277     else
278         return this;
279 }
280
281 const CPPParseJob* CPPParseJob::masterJob() const {
282     if( parentPreprocessor() )
283         return static_cast<CPPParseJob*>(parentPreprocessor()->parent())->masterJob();
284     else
285         return this;
286 }
287
288 void CPPParseJob::setUpdatingProxyContext( const ReferencedTopDUContext& context ) {
289     m_updatingProxyContext = context;
290 }
291
292 ReferencedTopDUContext CPPParseJob::updatingProxyContext() const {
293     return m_updatingProxyContext;
294 }
295
296 void CPPParseJob::setUpdatingContentContext( const ReferencedTopDUContext& context ) {
297     m_updatingContentContext = context;
298 }
299
300 ReferencedTopDUContext CPPParseJob::updatingContentContext() const {
301     return m_updatingContentContext;
302 }
303
304 CppLanguageSupport * CPPParseJob::cpp() const
305 {
306   return CppLanguageSupport::self();
307 }
308
309 void CPPParseJob::addIncludedFile(KDevelop::ReferencedTopDUContext duChain, int sourceLine) {
310     if(duChain.data()) {
311       DUChainReadLocker lock(DUChain::lock());
312       m_includedFiles.push_back(LineContextPair(duChain, sourceLine));
313     }
314 }
315
316 void CPPParseJob::setProxyEnvironmentFile( Cpp::EnvironmentFile* file ) {
317     m_proxyEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>(file);
318 }
319
320 Cpp::EnvironmentFile* CPPParseJob::proxyEnvironmentFile() {
321     return m_proxyEnvironmentFile.data();
322 }
323
324 void CPPParseJob::setContentEnvironmentFile( Cpp::EnvironmentFile* file ) {
325     //Q_ASSERT(!file || file->identity().flags() & IdentifiedFile::Content);
326     m_contentEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>(file);
327 }
328
329 void CPPParseJob::addPreprocessorProblem(const ProblemPointer problem) {
330   m_preprocessorProblems << problem;
331 }
332
333 QList<ProblemPointer> CPPParseJob::preprocessorProblems() const {
334   return m_preprocessorProblems;
335 }
336
337 QList<ProblemPointer>* CPPParseJob::preprocessorProblemsPointer() {
338   return &m_preprocessorProblems;
339 }
340
341 Cpp::EnvironmentFile* CPPParseJob::contentEnvironmentFile() {
342     return m_contentEnvironmentFile.data();
343 }
344
345 const IncludeFileList& CPPParseJob::includedFiles() const {
346     return m_includedFiles;
347 }
348
349 CPPParseJob * CPPInternalParseJob::parentJob() const
350 {
351     Q_ASSERT(parent());
352     return static_cast<CPPParseJob*>(const_cast<QObject*>(parent()));
353 }
354
355 const KTextEditor::Range& CPPParseJob::textRangeToParse() const
356 {
357     return m_textRangeToParse;
358 }
359
360 CPPInternalParseJob::CPPInternalParseJob(CPPParseJob * parent)
361     : ThreadWeaver::Job(parent)
362     , m_initialized(false)
363     , m_priority(0)
364 {
365 }
366
367 ///If @param ctx is a proxy-context, returns the target-context. Else returns ctx. @warning du-chain must be locked
368 LineContextPair contentFromProxy(LineContextPair ctx) {
369     if( ctx.context->parsingEnvironmentFile() && ctx.context->parsingEnvironmentFile()->isProxyContext() ) {
370         {
371           ReferencedTopDUContext ref(ctx.context);
372         }
373         if(ctx.context->importedParentContexts().isEmpty()) {
374           kWarning() << "proxy-context for" << ctx.context->url().str() << "has no imports!" << ctx.context->ownIndex();
375           ///@todo Find out how this can happen
376 //           Q_ASSERT(0);
377           return LineContextPair(0, 0);
378         }
379
380         Q_ASSERT(!ctx.context->importedParentContexts().isEmpty());
381         return LineContextPair( dynamic_cast<TopDUContext*>(ctx.context->importedParentContexts().first().context(0)), ctx.sourceLine );
382     }else{
383         return ctx;
384     }
385 }
386
387 void CPPInternalParseJob::initialize() {
388     if(m_initialized)
389       return;
390     m_initialized = true;
391
392     updatingProxyContext = parentJob()->updatingProxyContext().data();
393     updatingContentContext = parentJob()->updatingContentContext().data();
394
395     proxyContext = updatingProxyContext;
396     contentContext = updatingContentContext;
397 /*    if(!contentContext)
398       contentContext = ContextBuilder::createEmptyTopContext(parentJob()->document());
399
400     if(!proxyContext && Cpp::EnvironmentManager::self()->isSimplifiedMatching())
401       proxyContext = ContextBuilder::createEmptyTopContext(parentJob()->document());*/
402 }
403
404 void CPPInternalParseJob::highlightIfNeeded()
405 {
406     if(!ICore::self()->languageController()->backgroundParser()->trackerForUrl(parentJob()->document()))
407       return;
408
409     DUChainReadLocker l(DUChain::lock());
410     ReferencedTopDUContext standardContext = DUChainUtils::standardContextForUrl(parentJob()->document().toUrl());
411
412     kDebug( 9007 ) << "Highlighting" << parentJob()->document().str();
413     //If the document has a smart-range, at least re-do the highlighting
414     l.unlock();
415     if ( parentJob()->cpp() && parentJob()->cpp()->codeHighlighting() )
416       parentJob()->cpp()->codeHighlighting()->highlightDUChain( standardContext.data() );
417 }
418
419 void CPPInternalParseJob::run()
420 {
421     //Happens during shutdown
422     if(!ICore::self()->languageController()->language("C++")->languageSupport() || !parentJob()->cpp())
423       return;
424     
425     //If we have a parent, the parse-mutex is already locked
426     QReadLocker lock(parentJob()->parentPreprocessor() ? 0 : parentJob()->cpp()->language()->parseLock());
427     if(!ICore::self()->languageController()->language("C++")->languageSupport() || !parentJob()->cpp())
428       return;
429     
430     initialize();
431
432     if(updatingContentContext)
433       parentJob()->translateDUChainToRevision(updatingContentContext.data());
434     
435     if(updatingProxyContext)
436       parentJob()->translateDUChainToRevision(updatingProxyContext.data());
437     
438     UrlParseLock urlLock(parentJob()->document());
439
440     if(!parentJob()->needsUpdate()) {
441       parentJob()->processDelayedImports();
442       kDebug( 9007 ) << "===-- ALREADY UP TO DATE --===> " << parentJob()->document().str();
443       highlightIfNeeded();
444       return;
445     }
446     if(!parentJob()->contentEnvironmentFile()) {
447       //May happen when the file could not be opened or similar
448       kDebug( 9007 ) << "===-- Problem: Preprocessor did not create environment-file, skipping --===> " << parentJob()->document().str();
449       return;
450     }
451     kDebug( 9007 ) << "===-- PARSING --===> "
452     << parentJob()->document().str();
453
454
455     if (parentJob()->abortRequested())
456         return /*parentJob()->abortJob()*/;
457
458     parentJob()->setLocalProgress(0, i18n("Parsing actual file"));
459
460     Cpp::EnvironmentFilePointer proxyEnvironmentFile(parentJob()->proxyEnvironmentFile());
461     Cpp::EnvironmentFilePointer contentEnvironmentFile(parentJob()->contentEnvironmentFile());
462
463     //Eventually remove old imports
464     if(contentContext) {
465       /**
466       * If we are updating everything, and the context has not been updated yet in this run,
467        * remove all contexts previously imported into the content-context,
468       * because they may contain badly parsed data, that might later override new parsed data.
469       * If simplified environment-matching is disabled, always remove the imports if the file is reparsed,
470       * their new versions will be re-added.
471       * */
472         DUChainWriteLocker lock(DUChain::lock());
473       if(!parentJob()->keepDuchain() &&
474         ((!parentJob()->masterJob()->wasUpdated(contentContext) && parentJob()->needUpdateEverything())
475           || !proxyContext)) {
476
477           foreach(const DUContext::Import& ctx, contentContext->importedParentContexts())
478             contentContext->removeImportedParentContext(ctx.context(0));
479           }
480           ///@todo Make this possible without a permanent write-lock
481           contentContext->updateImportsCache();
482     }
483
484     QList<LineContextPair> importedContentChains; //All content-chains imported while this parse-run. Also contains the temporary ones.
485     QList<ReferencedTopDUContext> importedTemporaryChains; //All imported content-chains that were imported temporarily from parents.
486     QSet<KDevelop::IndexedString> encounteredIncludeUrls; //All imported file-urls that were encountered this run.
487
488     {
489         DUChainReadLocker lock(DUChain::lock());
490         foreach ( const LineContextPair &context, parentJob()->includedFiles() ) {
491             LineContextPair pair = contentFromProxy(context);
492             if(!pair.context)
493               continue;
494
495             importedContentChains << pair;
496             encounteredIncludeUrls << context.context->url();
497         }
498     }
499
500     //Temporarily import contexts imported by parents, because in C++ those are visible from here too
501     if( parentJob()->parentPreprocessor() ) {
502         DUChainReadLocker lock(DUChain::lock());
503         //In theory, we would need to insert the whole until-now parsed part of the parent, but that's not possible because
504         //we mix preprocessing and parsing, by triggering the parsing of included files by the preprocessor of the including file
505         //So we simply assume that the includes are at the top of the file.
506         CPPParseJob* currentJob = parentJob()->parentPreprocessor()->parentJob();
507
508         while(currentJob) {
509           foreach ( const LineContextPair &topContext, currentJob->includedFiles() ) {
510               //As above, we work with the content-contexts.
511               LineContextPair context = contentFromProxy(topContext);
512               if(!context.context)
513                 continue;
514               DUContextPointer ptr(context.context);
515               if( !importsContext(importedContentChains, context.context)  && (!updatingContentContext ||       !importsContext(updatingContentContext->importedParentContexts(), context.context)) ) {
516                 if(!updatingContentContext || !updatingContentContext->imports(context.context, updatingContentContext->range().end)) {
517                     //This must be conditional, else we might remove needed includes later because we think they were purely temporary
518                     importedContentChains << context;
519                     importedContentChains.back().temporary = true;
520                     importedTemporaryChains << context.context;
521                 }
522               }
523           }
524           if(currentJob->parentPreprocessor())
525             currentJob = currentJob->parentPreprocessor()->parentJob();
526           else
527             currentJob = 0;
528         }
529     }
530
531     ///Now we build/update the content-context
532
533     bool doNotChangeDUChain = false;
534     bool isOpenInEditor = ICore::self()->languageController()->backgroundParser()->trackerForUrl(parentJob()->document());
535
536     if(!parentJob()->keepDuchain()) {
537
538       TopDUContext::Features newFeatures = parentJob()->minimumFeatures();
539       if(contentContext)
540         newFeatures = (TopDUContext::Features)(newFeatures | contentContext->features());
541
542       if(newFeatures & TopDUContext::ForceUpdate)
543         kDebug() << "update enforced";
544
545       ///At some point, we have to give up on features again, else processing will be just too slow.
546       ///Simple solution for now: Always go down to the minimum required level.
547       if(!contentContext || !isOpenInEditor)
548         newFeatures = parentJob()->minimumFeatures();
549
550       bool keepAST = newFeatures & TopDUContext::AST;
551       
552       //Remove update-flags like 'Recursive' or 'ForceUpdate', and the AST flag
553       newFeatures = (TopDUContext::Features)(newFeatures & TopDUContext::AllDeclarationsContextsAndUses);
554       
555       TranslationUnitAST* ast = 0L;
556
557       Control control;
558       Parser parser(&control);
559
560       if(newFeatures != TopDUContext::Empty)
561       {
562         ast = parser.parse( parentJob()->parseSession().data() );
563
564         //This will be set to true if the duchain data should be left untouched
565         if((ast->hadMissingCompoundTokens || control.hasProblem(KDevelop::ProblemData::Lexer)) && updatingContentContext) {
566           //Make sure we don't update into a completely invalid state where everything is invalidated temporarily.
567           DUChainWriteLocker l(DUChain::lock());
568
569           if((updatingContentContext->features() & parentJob()->minimumFeatures()) ==  parentJob()->minimumFeatures() &&
570             isOpenInEditor &&
571               updatingContentContext->parsingEnvironmentFile()->modificationRevision().modificationTime == ModificationRevision::revisionForFile(updatingContentContext->url()).modificationTime && CppLanguageSupport::self()->codeHighlighting()->hasHighlighting(parentJob()->document())) {
572             kDebug() << "not processing" << updatingContentContext->url().str() << "because of missing compound tokens";
573             ICore::self()->uiController()->showErrorMessage(i18n("Not updating duchain for %1", parentJob()->document().toUrl().fileName()), 1);
574             l.unlock();
575             doNotChangeDUChain = true;
576             ProblemPointer problem(new Problem);
577             problem->setSource(ProblemData::Parser);
578             problem->setDescription("Not updating the DUChain because of serious document inconsistency");
579             control.reportProblem(problem);
580           }
581         }
582
583         if (parentJob()->abortRequested())
584             return /*parentJob()->abortJob()*/;
585
586         if ( ast ) {
587             ast->session = parentJob()->parseSession().data();
588 #ifdef DUMP_AST
589             DumpTree dump;
590             dump.dump(ast, parentJob()->parseSession()->token_stream);
591 #endif
592         }
593       }
594
595       bool isStandardContext = false;
596       {
597         DUChainWriteLocker l(DUChain::lock());
598         TopDUContext* knownStandardContext = DUChainUtils::standardContextForUrl(parentJob()->document().toUrl());
599
600         isStandardContext = (parentJob()->masterJob() == parentJob() || knownStandardContext == updatingContentContext || !knownStandardContext);
601       }
602
603       kDebug( 9007 ) << (contentContext ? "updating" : "building") << "duchain for" << parentJob()->document().str();
604
605       uint oldItemCount = 0;
606       if(contentContext) {
607         DUChainWriteLocker l(DUChain::lock());
608         contentContext->clearProblems();
609         oldItemCount = contentContext->childContexts().size() + contentContext->localDeclarations().size();
610       }
611
612       DeclarationBuilder declarationBuilder(parentJob()->parseSession().data());
613
614       if(newFeatures == TopDUContext::VisibleDeclarationsAndContexts) {
615         declarationBuilder.setOnlyComputeVisible(true); //Only visible declarations/contexts need to be built.
616       }
617       else if(newFeatures == TopDUContext::SimplifiedVisibleDeclarationsAndContexts)
618       {
619         declarationBuilder.setOnlyComputeVisible(true);
620         declarationBuilder.setComputeSimplified(true);
621         kDebug() << "computing simplified";
622       }else if(newFeatures == TopDUContext::Empty) {
623         kDebug() << "computing empty";
624         declarationBuilder.setComputeEmpty(true);
625       }
626
627
628       if(keepAST)
629         declarationBuilder.setMapAst(true); //Set the property to map the AST & DUChain
630
631       if(!doNotChangeDUChain) {
632
633         if(Cpp::EnvironmentManager::self()->matchingLevel() == Cpp::EnvironmentManager::Disabled) {
634             DUChainWriteLocker lock(DUChain::lock());
635             if(contentContext)
636               contentContext->clearImportedParentContexts();
637         }
638
639         contentContext = declarationBuilder.buildDeclarations(contentEnvironmentFile, ast, &importedContentChains, contentContext, false);
640
641         //If publically visible declarations were added/removed, all following parsed files need to be updated
642         if(declarationBuilder.changeWasSignificant()) {
643           ///@todo The right solution to the whole problem: Do not put any imports into the content-contexts. Instead, Represent the complete import-structure in the proxy-contexts.
644           ///      While searching, always use the perspective of the proxy. Even better: Change the context-system so proxy-contexts become completely valid contexts from the outside perspective,
645           ///      that import all their imports, and that share all their content except the imports/environment-information with all the other proxy contexts for that file, and with one content-context.
646           ///      Main problem: Contained Declarations/DUContexts point at their parent top-context. Which proxy-context should they point at?
647           //kDebug() << "A significant change was recorded, all following contexts will be updated";
648           //parentJob()->masterJob()->setNeedUpdateEverything(true);
649         }
650
651         {
652           DUChainReadLocker l(DUChain::lock());
653           Q_ASSERT(!updatingContentContext || updatingContentContext == contentContext);
654           Q_ASSERT(contentContext->parsingEnvironmentFile().data() == contentEnvironmentFile.data());
655
656           if(contentContext->childContexts().size() + contentContext->localDeclarations().size() == 0) {
657             if(oldItemCount != 0) {
658               //To catch some problems
659               kDebug(9007) << "All items in" << parentJob()->document().str() << "have been extincted, previous count:" << oldItemCount << "current identity offset:" << contentEnvironmentFile->identityOffset();
660             }
661           }
662         }
663       }
664
665       {
666         DUChainWriteLocker l(DUChain::lock());
667
668         foreach( const ProblemPointer& problem, parentJob()->preprocessorProblems() ) {
669           contentContext->addProblem(problem);
670         }
671
672         foreach( KDevelop::ProblemPointer p, control.problems() ) {
673           p->setLocationStack(parentJob()->includeStack());
674           contentContext->addProblem(p);
675         }
676       }
677
678       if(!doNotChangeDUChain) {
679         if(contentContext) {
680             DUChainWriteLocker l(DUChain::lock());
681             QList<TopDUContext*> remove;
682             foreach(const ReferencedTopDUContext &ctx, importedTemporaryChains)
683                 remove << ctx.data();
684             contentContext->removeImportedParentContexts(remove);
685         }
686
687         ///When simplified environment-matching is enabled, we will accumulate many different
688         ///versions of imports into a single top-context. To reduce that a little, we remove all
689         ///with urls we didn't encounter.
690         if(updatingContentContext) {
691             DUChainWriteLocker l(DUChain::lock());
692
693             //Remove the temporary chains first, so we don't get warnings from them
694
695             QVector<DUContext::Import> imports = contentContext->importedParentContexts();
696             foreach(const DUContext::Import &ctx, imports) {
697                 if(ctx.context(0) && !encounteredIncludeUrls.contains(ctx.context(0)->url()) && contentEnvironmentFile->missingIncludeFiles().set().count() == 0 && (!proxyEnvironmentFile || proxyEnvironmentFile->missingIncludeFiles().set().count() == 0)) {
698                     contentContext->removeImportedParentContext(ctx.context(0));
699                     kDebug( 9007 ) << "removing not encountered import " << ctx.context(0)->url().str();
700                 }
701             }
702         }
703
704         if(contentContext) {
705           DUChainWriteLocker l(DUChain::lock());
706           contentContext->updateImportsCache();
707         }
708
709         if (!parentJob()->abortRequested()) {
710           if ((newFeatures & TopDUContext::AllDeclarationsContextsAndUses) == TopDUContext::AllDeclarationsContextsAndUses) {
711               parentJob()->setLocalProgress(0.5, i18n("Building uses"));
712
713               UseBuilder useBuilder(parentJob()->parseSession().data());
714               useBuilder.setMapAst(keepAST);
715               useBuilder.buildUses(ast);
716               DUChainWriteLocker l(DUChain::lock());
717               foreach(KDevelop::ProblemPointer problem, useBuilder.problems())
718                 contentContext->addProblem(problem);
719           }else{
720               //Delete existing uses
721               DUChainWriteLocker lock( DUChain::lock() );
722               contentContext->deleteUsesRecursively();
723           }
724         }
725       }
726       
727       if (!parentJob()->abortRequested() && isOpenInEditor) {
728         if ( parentJob()->cpp() && parentJob()->cpp()->codeHighlighting() )
729         {
730           parentJob()->cpp()->codeHighlighting()->highlightDUChain( contentContext );
731         }
732       }
733       
734
735       ///Now mark the context as not being updated. This MUST be done or we will be waiting forever in a loop
736       {
737         DUChainWriteLocker l(DUChain::lock());
738         contentContext->setFeatures(newFeatures);
739         if(proxyContext)
740           proxyContext->setFeatures(newFeatures);
741         contentContext->setFlags( (TopDUContext::Flags)(contentContext->flags() & (~TopDUContext::UpdatingContext)) );
742
743         //Now that the Ast is fully built, add it to the TopDUContext if requested
744         if(keepAST)
745         {
746           kDebug() << "AST Is being kept for" << parentJob()->document().toUrl();
747           contentContext->setAst(IAstContainer::Ptr( parentJob()->parseSession().data() ));
748           parentJob()->parseSession()->setASTNodeParents();
749         }
750
751         else
752           contentContext->clearAst();
753       }
754
755       if (parentJob()->abortRequested())
756         return /*parentJob()->abortJob()*/;
757
758     }else{
759       {
760         DUChainWriteLocker l(DUChain::lock());
761         if(proxyContext && contentContext)
762           proxyContext->setFeatures(contentContext->features());
763       }
764       kDebug() << "keeping duchain";
765       highlightIfNeeded();
766     }
767
768     //Even if doNotChangeDUChain is enabled, add new imported contexts.
769     //This is very useful so new added includes always work.
770     if(parentJob()->keepDuchain() || doNotChangeDUChain) {
771       DUChainWriteLocker l(DUChain::lock());
772       ///Add all our imports to the re-used context, just to make sure they are there.
773       foreach( const LineContextPair& import, importedContentChains )
774           if(!import.temporary)
775             contentContext->addImportedParentContext(import.context, CursorInRevision(import.sourceLine, 0));
776       contentContext->updateImportsCache();
777     }
778
779     if(!doNotChangeDUChain) {
780       DUChainReadLocker lock(DUChain::lock());
781       foreach(const LineContextPair& import, parentJob()->includedFiles()) {
782         if(import.temporary)
783           continue;
784         LineContextPair context = contentFromProxy(import);
785         if(!context.context)
786           continue;
787         Q_ASSERT(context.context);
788         if(!contentContext->imports(context.context.data(), CursorInRevision::invalid())) {
789           kWarning() << "Context should be imported, but is not:" << contentContext->url().str() << " <- " << context.context->url().str();
790         }
791       }
792     }
793
794     ///Build/update the proxy-context
795
796     if( proxyEnvironmentFile ) {
797         ContextBuilder builder(parentJob()->parseSession().data());
798         if(Cpp::EnvironmentManager::self()->matchingLevel() == Cpp::EnvironmentManager::Disabled) {
799             DUChainWriteLocker lock(DUChain::lock());
800             if(updatingProxyContext)
801               updatingProxyContext->clearImportedParentContexts();
802         }
803
804         proxyContext = builder.buildProxyContextFromContent(proxyEnvironmentFile, TopDUContextPointer(contentContext), TopDUContextPointer(updatingProxyContext));
805
806         DUChainWriteLocker lock(DUChain::lock());
807
808         Q_ASSERT(!updatingProxyContext || updatingProxyContext == proxyContext);
809
810         if(proxyContext->importedParentContexts().isEmpty()) {
811           Q_ASSERT(0); //Failure
812         }
813
814         Q_ASSERT(contentContextFromProxyContext(proxyContext) == contentContext.data());
815
816         //Make sure the imported contextsa re added
817         foreach ( const LineContextPair &context, parentJob()->includedFiles() )
818           proxyContext->addImportedParentContext(context.context, CursorInRevision(context.sourceLine, 0));
819
820         proxyContext->updateImportsCache();
821
822         proxyContext->clearProblems();
823
824         //Put the problems into the proxy-context
825         foreach( const ProblemPointer& problem, parentJob()->preprocessorProblems() ) {
826           if(problem->finalLocation().start.line < proxyEnvironmentFile->contentStartLine())
827             proxyContext->addProblem(problem);
828         }
829
830         //Copy all problems into the proxy, because it's used to represent them.
831         foreach( const ProblemPointer& problem, contentContext->problems() )
832           proxyContext->addProblem(problem);
833     }
834
835     if(proxyContext) {
836       DUChainReadLocker lock(DUChain::lock());
837       Q_ASSERT(!proxyContext->importedParentContexts().isEmpty());
838     }
839
840     {
841       //Update include-path dependencies
842       DUChainWriteLocker lock(DUChain::lock());
843       if(proxyEnvironmentFile)
844         proxyEnvironmentFile->setIncludePathDependencies(parentJob()->includePathDependencies());
845
846       if(contentEnvironmentFile)
847         contentEnvironmentFile->setIncludePathDependencies(parentJob()->includePathDependencies());
848     }
849
850     ///In the end, mark the contexts as updated.
851     if(contentContext)
852       parentJob()->masterJob()->setWasUpdated(contentContext);
853     if(proxyContext)
854       parentJob()->masterJob()->setWasUpdated(proxyContext);
855
856     parentJob()->setDuChain(proxyContext ? proxyContext : contentContext);
857
858     //Indicate progress
859     parentJob()->setLocalProgress(1, i18n("Ready"));
860
861     if(parentJob()->masterJob() != parentJob())
862       parentJob()->masterJob()->includedFileParsed();
863
864     // Debug output...
865
866     if ( !parentJob()->parentPreprocessor() ) {
867             DUChainReadLocker lock(DUChain::lock());
868 #ifdef DUMP_DUCHAIN
869         kDebug( 9007 ) << "================== duchain ==================";
870         KDevelop::dumpDUContext(contentContext);
871 #endif
872
873         //KDevelop::DumpDotGraph dumpGraph;
874         //kDebug(9007) << "Dot-graph:\n" << dumpGraph.dotGraph(topContext, true);
875     }
876
877     kDebug( 9007 ) << "===-- Parsing finished --===>" << parentJob()->document().str();
878
879     parentJob()->processDelayedImports();
880 }
881
882 void CPPParseJob::processDelayedImports() {
883   if(!m_delayedImports.isEmpty()) {
884     foreach(const LineJobPair& job, m_delayedImports)
885       job.first->addDelayedImporter(LineContextPair(m_parseJob->proxyContext ? m_parseJob->proxyContext : m_parseJob->contentContext, job.second));
886     m_delayedImports.clear();
887   }
888   if(!m_delayedImporters.isEmpty()) {
889     DUChainWriteLocker l(DUChain::lock());
890     foreach(const LineContextPair& context, m_delayedImporters) {
891       Q_ASSERT(context.context->parsingEnvironmentFile());
892       if(context.context->parsingEnvironmentFile()->isProxyContext()) {
893         Q_ASSERT(m_parseJob->proxyContext);
894         context.context->addImportedParentContext(m_parseJob->proxyContext.data(), CursorInRevision(context.sourceLine, 0));
895         Cpp::EnvironmentFile* cppEnvFile = dynamic_cast<Cpp::EnvironmentFile*>(context.context->parsingEnvironmentFile().data());
896         Q_ASSERT(cppEnvFile);
897         cppEnvFile->merge(dynamic_cast<Cpp::EnvironmentFile&>(*m_parseJob->proxyContext->parsingEnvironmentFile().data()));
898         context.context->updateImportsCache();
899       }
900       Q_ASSERT(m_parseJob->contentContext);
901       LineContextPair content = contentFromProxy(context);
902       if(!content.context)
903         continue;
904       Q_ASSERT(content.context);
905       content.context->addImportedParentContext(m_parseJob->proxyContext.data(), CursorInRevision(content.sourceLine, 0));
906       content.context->updateImportsCache();
907       Cpp::EnvironmentFile* cppEnvFile = dynamic_cast<Cpp::EnvironmentFile*>(content.context->parsingEnvironmentFile().data());
908       Q_ASSERT(cppEnvFile);
909       cppEnvFile->merge(dynamic_cast<Cpp::EnvironmentFile&>(*m_parseJob->contentContext->parsingEnvironmentFile().data()));
910     }
911   }
912 }
913
914 void CPPParseJob::addDelayedImport(LineJobPair job) {
915   m_delayedImports << job;
916 }
917
918 void CPPParseJob::addDelayedImporter(LineContextPair duChain) {
919   m_delayedImporters << duChain;
920 }
921
922 void CPPParseJob::requestDependancies()
923 {
924 }
925
926 ParseSession::Ptr CPPParseJob::parseSession() const
927 {
928     return m_session;
929 }
930
931 CPPInternalParseJob * CPPParseJob::parseJob() const
932 {
933     return m_parseJob;
934 }
935
936 int CPPInternalParseJob::priority() const
937 {
938     return m_priority;
939 }
940
941 void CPPInternalParseJob::setPriority(int priority)
942 {
943     m_priority = priority;
944 }
945
946 const QStack< DocumentCursor > & CPPParseJob::includeStack() const
947 {
948   return m_includeStack;
949 }
950
951 void CPPParseJob::setIncludeStack(const QStack< DocumentCursor > & includeStack)
952 {
953   m_includeStack = includeStack;
954 }
955
956 TopDUContext::Features CPPParseJob::standardMinimumFeatures() const
957 {
958     return TopDUContext::SimplifiedVisibleDeclarationsAndContexts;
959 }
960
961 TopDUContext::Features CPPParseJob::slaveMinimumFeatures() const
962 {
963     TopDUContext::Features slaveMinimumFeatures = standardMinimumFeatures();
964     
965     if(minimumFeatures() & TopDUContext::Recursive)
966       slaveMinimumFeatures = (TopDUContext::Features)(minimumFeatures() & (~TopDUContext::ForceUpdate));
967     else if((minimumFeatures() & TopDUContext::VisibleDeclarationsAndContexts) == TopDUContext::VisibleDeclarationsAndContexts)
968       slaveMinimumFeatures = TopDUContext::VisibleDeclarationsAndContexts;
969     
970     if((minimumFeatures() & TopDUContext::ForceUpdateRecursive) == TopDUContext::ForceUpdateRecursive)
971       slaveMinimumFeatures = (TopDUContext::Features)(slaveMinimumFeatures | TopDUContext::ForceUpdateRecursive);
972
973     //The selected minimum features are required on all imported contexts recursively
974     return (TopDUContext::Features)(slaveMinimumFeatures | TopDUContext::Recursive);
975 }
976
977 #include "cppparsejob.moc"
978