Improve contentContextFromProxyContext so that it never returns the wrong content...
[kdevelop:kdevelop.git] / languages / cpp / preprocessjob.cpp
1 /*
2 * This file is part of KDevelop
3 *
4 * Copyright 2006 Adam Treat <treat@kde.org>
5 * Copyright 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 #include "preprocessjob.h"
24
25 //#include <valgrind/memcheck.h>
26
27
28 #include <QFile>
29 #include <QFileInfo>
30 #include <QByteArray>
31 #include <QMutexLocker>
32 #include <QReadWriteLock>
33
34 #include <kdebug.h>
35 #include <klocale.h>
36
37 #include <language/backgroundparser/backgroundparser.h>
38 #include <language/codegen/coderepresentation.h>
39 #include <language/duchain/duchain.h>
40 #include <language/duchain/duchainlock.h>
41 #include <language/duchain/topducontext.h>
42 #include <language/interfaces/iproblem.h>
43
44 #include <threadweaver/Thread.h>
45
46 #include <interfaces/ilanguage.h>
47
48 #include "cpplanguagesupport.h"
49 #include "cppparsejob.h"
50 #include "parser/ast.h"
51 #include "parser/parsesession.h"
52 #include "parser/rpp/pp-engine.h"
53 #include "parser/rpp/pp-macro.h"
54 #include "parser/rpp/preprocessor.h"
55 #include "environmentmanager.h"
56 #include "cpppreprocessenvironment.h"
57
58 #include "cppdebughelper.h"
59 #include "codegen/unresolvedincludeassistant.h"
60
61 // #define ifDebug(x) x
62 #include "cpputils.h"
63 #include <rpp/pp-location.h>
64
65 const uint maxIncludeDepth = 50;
66
67 QString urlsToString(const QList<KUrl>& urlList) {
68   QString paths;
69   foreach( const KUrl& u, urlList )
70       paths += u.pathOrUrl() + "\n";
71
72   return paths;
73 }
74
75 TopDUContext* contentContextFromProxyContext(TopDUContext* top)
76 {
77   if(!top)
78     return 0;
79   if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) {
80     if(!top->importedParentContexts().isEmpty())
81     {
82       TopDUContext* ret = top->importedParentContexts()[0].context(0)->topContext();
83       if(ret->url() != top->url())
84         kDebug() << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl();
85       if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext())
86         return ret;
87     }
88     else {
89       kDebug() << "Proxy-context imports no content-context";
90     }
91   } else
92     return top;
93   return 0;
94 }
95
96 PreprocessJob::PreprocessJob(CPPParseJob * parent)
97     : ThreadWeaver::Job(parent)
98     , m_currentEnvironment(0)
99     , m_firstEnvironmentFile( new Cpp::EnvironmentFile( parent->document(), 0 ) )
100     , m_success(true)
101     , m_headerSectionEnded(false)
102     , m_pp(0)
103 {
104 }
105
106 PreprocessJob::~PreprocessJob() {
107   delete m_currentEnvironment;
108 }
109
110 KDevelop::ParsingEnvironment* PreprocessJob::createStandardEnvironment() {
111     CppPreprocessEnvironment* ret = new CppPreprocessEnvironment(0, Cpp::EnvironmentFilePointer());
112     ret->merge( CppUtils::standardMacros() );
113     
114     return ret;
115 }
116
117 CPPParseJob * PreprocessJob::parentJob() const
118 {
119     return static_cast<CPPParseJob*>(const_cast<QObject*>(parent()));
120 }
121
122 void PreprocessJob::foundHeaderGuard(rpp::Stream& stream, KDevelop::IndexedString guardName)
123 {
124   Q_UNUSED(stream);
125   
126   KDevelop::DUChainWriteLocker lock(KDevelop::DUChain::lock());
127   
128   m_currentEnvironment->environmentFile()->setHeaderGuard(guardName);
129   
130   //In naive matching mode, we ignore the dependence on header-guards
131   if(Cpp::EnvironmentManager::self()->matchingLevel() <= Cpp::EnvironmentManager::Naive)
132     m_currentEnvironment->removeString(guardName);
133 }
134
135 void PreprocessJob::run()
136 {
137     if(!ICore::self()->languageController()->language("C++")->languageSupport())
138       return;
139   
140     //If we have a parent, that parent already has locked the parse-lock
141     QReadLocker lock(parentJob()->parentPreprocessor() ? 0 : parentJob()->cpp()->language()->parseLock());
142     
143     //It seems like we cannot influence the actual thread priority in thread-weaver, so for now set it here.
144     //It must be low so the GUI stays fluid.
145     if(QThread::currentThread())
146       QThread::currentThread()->setPriority(QThread::LowestPriority);
147
148     //kDebug(9007) << "Started pp job" << this << "parse" << parentJob()->parseJob() << "parent" << parentJob();
149
150     kDebug(9007) << "PreprocessJob: preprocessing" << parentJob()->document().str();
151
152     if (checkAbort())
153         return;
154
155     {
156       KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
157       
158       if(Cpp::EnvironmentManager::self()->isSimplifiedMatching()) {
159         //Make sure that proxy-contexts and content-contexts never have the same identity, even if they have the same content.
160         m_firstEnvironmentFile->setIdentityOffset(1); //Mark the first environment-file as the proxy
161         IndexedString u = parentJob()->document();
162         m_secondEnvironmentFile = new Cpp::EnvironmentFile(  u, 0 );
163       }
164     }
165
166     rpp::pp preprocessor(this);
167     m_pp = &preprocessor;
168
169     //Eventually initialize the environment with the parent-environment to get its macros
170     m_currentEnvironment = new CppPreprocessEnvironment( &preprocessor, m_firstEnvironmentFile );
171
172     //If we are included from another preprocessor, copy its macros
173     if( parentJob()->parentPreprocessor() ) {
174         m_currentEnvironment->swapMacros( parentJob()->parentPreprocessor()->m_currentEnvironment );
175     } else {
176         //Insert standard-macros
177         KDevelop::ParsingEnvironment* standardEnv = createStandardEnvironment();
178         parentJob()->mergeDefines(static_cast<CppPreprocessEnvironment&>(*standardEnv));
179         
180         m_currentEnvironment->swapMacros( dynamic_cast<CppPreprocessEnvironment*>(standardEnv) );
181         delete standardEnv;
182     }
183     
184     Cpp::ReferenceCountedStringSet macroNamesAtBeginning = m_currentEnvironment->macroNameSet();
185     
186     KDevelop::ParsingEnvironmentFilePointer updatingEnvironmentFile;
187     
188     {
189         ///Find a context that can be updated, and eventually break processing right here, if we notice we don't need to update
190         KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
191         
192         //Make sure we only match proxy-contexts for updating
193         m_currentEnvironment->setIdentityOffsetRestriction(m_firstEnvironmentFile->identityOffset());
194         
195         updatingEnvironmentFile = KDevelop::ParsingEnvironmentFilePointer( KDevelop::DUChain::self()->environmentFileForDocument(parentJob()->document(), m_currentEnvironment, (bool)m_secondEnvironmentFile) );
196         
197         if(parentJob()->masterJob() == parentJob() && updatingEnvironmentFile) {
198           //Check whether we need to run at all, or whether the file is already up to date
199           if(updatingEnvironmentFile->featuresSatisfied(parentJob()->minimumFeatures()) && updatingEnvironmentFile->featuresSatisfied(parentJob()->slaveMinimumFeatures())) {
200             KUrl localPath(parentJob()->document().toUrl());
201             localPath.setFileName(QString());
202             Cpp::EnvironmentFile* cppEnv = dynamic_cast<Cpp::EnvironmentFile*>(updatingEnvironmentFile.data());
203             Q_ASSERT(cppEnv);
204             //When possible, we determine whether an update is needed without getting the include-paths, because that's very expensive
205             bool needsUpdate = cppEnv->needsUpdate();
206               if(!cppEnv->missingIncludeFiles().isEmpty() && !needsUpdate) {
207                 for(Cpp::ReferenceCountedStringSet::Iterator it = cppEnv->missingIncludeFiles().iterator(); it; ++it)
208                   kDebug(9007) << updatingEnvironmentFile->url().str() << "has missing include:" << (*it).str();
209                 
210                 readLock.unlock();
211                 KUrl::List includePaths = parentJob()->includePathUrls();
212                 readLock.lock();
213                 
214                 needsUpdate = CppUtils::needsUpdate(Cpp::EnvironmentFilePointer(cppEnv), localPath, includePaths);
215               }
216             
217             if(!needsUpdate) {
218               parentJob()->setNeedsUpdate(false);
219               return;
220             }
221           }
222         }
223     }
224     
225     //We do this down here, so we eventually can prevent determining the include-paths if nothing needs to be updated
226     m_firstEnvironmentFile->setIncludePaths( parentJob()->masterJob()->includePaths() );
227     
228     if(m_secondEnvironmentFile)
229       m_secondEnvironmentFile->setIncludePaths(m_firstEnvironmentFile->includePaths());
230
231     if (checkAbort() || !readContents())
232         return;
233
234     {
235         ///Find a context that can be updated
236         KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
237         
238         KDevelop::ReferencedTopDUContext updating;
239         if(updatingEnvironmentFile)
240           updating = updatingEnvironmentFile->topContext();
241
242         if(m_secondEnvironmentFile)
243           parentJob()->setUpdatingProxyContext( updating ); //The content-context to be updated will be searched later
244         else
245           parentJob()->setUpdatingContentContext( updating );
246       
247         if( updating ) {
248           //We don't need to change anything, because the EnvironmentFile will be replaced with a new one
249           m_updatingEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>( dynamic_cast<Cpp::EnvironmentFile*>(updating->parsingEnvironmentFile().data()) );
250         }
251         if( m_secondEnvironmentFile && parentJob()->updatingProxyContext() ) {
252             // Must be true, because we explicitly passed the flag to chainForDocument
253             Q_ASSERT((parentJob()->updatingProxyContext()->parsingEnvironmentFile()->isProxyContext()));
254         }
255     }
256     
257     preprocessor.setEnvironment( m_currentEnvironment );
258
259     PreprocessedContents result = preprocessor.processFile(parentJob()->document().str(), m_contents);
260
261     if(Cpp::EnvironmentManager::self()->matchingLevel() <= Cpp::EnvironmentManager::Naive && !m_headerSectionEnded && !m_firstEnvironmentFile->headerGuard().isEmpty()) {
262       if(macroNamesAtBeginning.contains(m_firstEnvironmentFile->headerGuard())) {
263         //Remove the header-guard, and re-preprocess, since we don't do real environment-management(We don't allow empty versions)
264         // We also have to clear the location-table here, because it already contains 'wrong' contents from the previous preprocessing
265         kDebug() << "Re-processing header with the header-guard disabled";
266         delete m_currentEnvironment->takeLocationTable();
267         m_currentEnvironment->removeMacro(m_firstEnvironmentFile->headerGuard());
268         result = preprocessor.processFile(parentJob()->document().str(), m_contents);
269       }
270     }
271     
272     if(!m_headerSectionEnded) {
273       ifDebug( kDebug(9007) << parentJob()->document().str() << ": header-section was not ended"; )
274       headerSectionEndedInternal(0);
275     }
276     
277     m_currentEnvironment->finishEnvironment(m_currentEnvironment->environmentFile() == m_updatingEnvironmentFile);
278     
279     foreach (KDevelop::ProblemPointer p, preprocessor.problems()) {
280       p->setLocationStack(parentJob()->includeStack());
281       p->setSource(KDevelop::ProblemData::Preprocessor);
282       parentJob()->addPreprocessorProblem(p);
283     }
284
285     parentJob()->parseSession()->setContents( result, m_currentEnvironment->takeLocationTable() );
286     parentJob()->parseSession()->setUrl( parentJob()->document() );
287
288     
289     if(m_secondEnvironmentFile)
290       parentJob()->setProxyEnvironmentFile( m_firstEnvironmentFile.data() );
291     else
292       parentJob()->setContentEnvironmentFile( m_firstEnvironmentFile.data() );
293     
294     if(m_secondEnvironmentFile) {//Copy some information from the environment-file to its content-part
295         KDevelop::DUChainWriteLocker readLock(KDevelop::DUChain::lock());
296         m_secondEnvironmentFile->setModificationRevision(m_firstEnvironmentFile->modificationRevision());
297         if(m_firstEnvironmentFile->headerGuard().isEmpty())
298           m_firstEnvironmentFile->setHeaderGuard(m_secondEnvironmentFile->headerGuard());
299         else
300           m_secondEnvironmentFile->setHeaderGuard(m_firstEnvironmentFile->headerGuard());
301     }
302     
303     if( m_secondEnvironmentFile ) {
304         //kDebug(9008) << parentJob()->document().str() << "Merging content-environment file into header environment-file";
305         KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
306         m_firstEnvironmentFile->merge(*m_secondEnvironmentFile);
307         parentJob()->setContentEnvironmentFile(m_secondEnvironmentFile.data());
308     }
309     
310     if( PreprocessJob* parentPreprocessor = parentJob()->parentPreprocessor() ) {
311         //If we are included from another preprocessor, give it back the modified macros,
312         parentPreprocessor->m_currentEnvironment->swapMacros( m_currentEnvironment );
313         //Merge include-file-set, defined macros, used macros, and string-set
314         KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
315         parentPreprocessor->m_currentEnvironment->environmentFile()->merge(*m_firstEnvironmentFile);
316     }else{
317 /*        kDebug(9007) << "Macros:";
318         for( rpp::Environment::EnvironmentMap::const_iterator it = m_currentEnvironment->environment().begin(); it != m_currentEnvironment->environment().end(); ++it ) {
319             kDebug(9007) << (*it)->name.str() << "                  from: " << (*it)->file << ":" << (*it)->sourceLine;
320         }*/
321     }
322     ifDebug( kDebug(9007) << "PreprocessJob: finished" << parentJob()->document().str(); )
323
324     m_pp = 0;
325     m_currentEnvironment = 0; //Was given to the pp-engine, and will be destroyed by that
326 }
327
328 void PreprocessJob::headerSectionEnded(rpp::Stream& stream)
329 {
330   headerSectionEndedInternal(&stream);
331 }
332
333 void PreprocessJob::headerSectionEndedInternal(rpp::Stream* stream)
334 {
335     bool closeStream = false;
336     m_headerSectionEnded = true;
337
338     ifDebug( kDebug(9007) << parentJob()->document().str() << "PreprocessJob::headerSectionEnded, " << parentJob()->includedFiles().count() << " included in header-section" << "upcoming identity-offset:" << m_pp->branchingHash()*19; )
339     
340     if( m_secondEnvironmentFile ) {
341         m_secondEnvironmentFile->setIdentityOffset(m_pp->branchingHash()*19);
342
343         if( stream ) {
344           m_secondEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
345           m_firstEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
346         }
347
348         ///Only allow content-contexts that have the same branching hash,
349         ///because else they were differently influenced earlier by macros in the header-section
350         ///Example: A file that has completely different content depending on an #ifdef
351
352         m_currentEnvironment->setIdentityOffsetRestriction(m_secondEnvironmentFile->identityOffset());
353         
354         IndexedString u = parentJob()->document();
355
356         ///Find a matching content-context
357         KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
358
359         KDevelop::ReferencedTopDUContext content;
360
361         if(m_updatingEnvironmentFile)
362           content = KDevelop::ReferencedTopDUContext(contentContextFromProxyContext(m_updatingEnvironmentFile->topContext()));
363         else
364           content = KDevelop::DUChain::self()->chainForDocument(u, m_currentEnvironment, false);
365
366         m_currentEnvironment->disableIdentityOffsetRestriction();
367
368         if(content && content->parsingEnvironmentFile()->isProxyContext())
369         {
370           kWarning() << "Got proxy-context as content-context for file" << content->url().str() << ", not updating";
371           content = KDevelop::ReferencedTopDUContext();
372         }
373         
374         if(content) {
375             //We have found a content-context that we can use
376             parentJob()->setUpdatingContentContext(content);
377
378             Q_ASSERT(!content->parsingEnvironmentFile()->isProxyContext());
379             
380             Cpp::EnvironmentFilePointer contentEnvironment(dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data()));
381             Q_ASSERT(m_updatingEnvironmentFile || contentEnvironment->identityOffset() == m_secondEnvironmentFile->identityOffset());            
382
383             ///@todo think whether localPath is needed
384             KUrl localPath(parentJob()->document().str());
385             localPath.setFileName(QString());
386             
387             if(contentEnvironment->matchEnvironment(m_currentEnvironment) && !CppUtils::needsUpdate(contentEnvironment, localPath, parentJob()->includePathUrls()) && (!parentJob()->masterJob()->needUpdateEverything() || parentJob()->masterJob()->wasUpdated(content)) && (content->parsingEnvironmentFile()->featuresSatisfied(parentJob()->minimumFeatures()) && content->parsingEnvironmentFile()->featuresSatisfied(parentJob()->slaveMinimumFeatures())) 
388               && Cpp::EnvironmentManager::self()->matchingLevel() != Cpp::EnvironmentManager::Disabled) {
389               ///@todo We never keep the duchain while updating now in disabled environment matching mode.
390               ///           We don't need it there, and changes in imports may be simply ignored when the keeping is enabled.
391               ///           However when full environment management is enabled this is needed, as the same content may be shared for multiple proxy contexts.
392                 //We can completely re-use the specialized context:
393                 m_secondEnvironmentFile = dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data());
394                 m_updatingEnvironmentFile = m_secondEnvironmentFile;
395                 
396                 //Merge the macros etc. into the current environment
397                 m_currentEnvironment->merge( m_secondEnvironmentFile.data() );
398
399                 ifDebug( kDebug(9007) << "closing data-stream, body does not need to be processed"; )
400                 closeStream = true;
401                 parentJob()->setKeepDuchain(true); //We truncate all following content, so we don't want to update the du-chain.
402                 Q_ASSERT(m_secondEnvironmentFile);
403             } else {
404                 ifDebug( kDebug(9007) << "updating content-context"; )
405                 m_updatingEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>(dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data()));
406                 //We will re-use the specialized context, but it needs updating. So we keep processing here.
407                 //We don't need to change m_updatingEnvironmentFile, because we will create a new one.
408             }
409         } else {
410             //We need to process the content ourselves
411             ifDebug( kDebug(9007) << "could not find a matching content-context"; )
412         }
413
414         m_currentEnvironment->finishEnvironment();
415
416         m_currentEnvironment->setEnvironmentFile(m_secondEnvironmentFile);
417     }
418
419     if( stream ) {
420       if( closeStream )
421         stream->toEnd();
422     }
423 }
424
425 rpp::Stream* PreprocessJob::sourceNeeded(QString& _fileName, IncludeType type, int sourceLine, bool skipCurrentPath)
426 {
427     Q_UNUSED(type)
428     if(0){
429       uint currentDepth = 0;
430       CPPParseJob* job = parentJob();
431       while(job->parentPreprocessor()) {
432         ++currentDepth;
433         job = job->parentPreprocessor()->parentJob();
434       }
435       if(currentDepth > maxIncludeDepth) {
436         kDebug(9007) << "maximum depth reached while including" << _fileName << "into" << parentJob()->document().str();
437         return 0;
438       }
439     }
440     
441     KUrl fileNameUrl(_fileName);
442     
443     TopDUContext::Features slaveMinimumFeatures = parentJob()->slaveMinimumFeatures();
444     QString fileName = fileNameUrl.pathOrUrl();
445     
446     if (checkAbort())
447         return 0;
448
449     ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": searching for include" << fileName; )
450
451     KUrl localPath(parentJob()->document().str());
452     localPath.setFileName(QString());
453     QStack<DocumentCursor> includeStack = parentJob()->includeStack();
454
455     KUrl from;
456     if (skipCurrentPath)
457       from = parentJob()->includedFromPath();
458
459     QPair<KUrl, KUrl> included = CppUtils::findInclude( parentJob()->includePathUrls(), localPath, fileName, type, from );
460     KUrl includedFile = included.first;
461     if (includedFile.isValid()) {
462       
463         IndexedString indexedFile(includedFile);
464         
465         {
466           //Prevent recursion that may cause a crash
467           PreprocessJob* current = this;
468           while(current) {
469             if(current->parentJob()->document() == indexedFile) {
470               KDevelop::ProblemPointer p(new Problem()); ///@todo create special include-problem
471               p->setSource(KDevelop::ProblemData::Preprocessor);
472               p->setDescription(i18n("File was included recursively from within itself: %1", fileName ));
473               p->setFinalLocation(DocumentRange(parentJob()->document(), SimpleRange(sourceLine,0, sourceLine+1,0)));
474               p->setLocationStack(parentJob()->includeStack());
475               parentJob()->addPreprocessorProblem(p);
476               return 0;
477             }
478             current = current->parentJob()->parentPreprocessor();
479           }
480         }
481       
482         ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": found include-file" << fileName << ":" << includedFile; )
483
484         KDevelop::ReferencedTopDUContext includedContext;
485         bool updateNeeded = false;
486         bool updateForbidden = false;
487
488         {
489             KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
490             includedContext = KDevelop::DUChain::self()->chainForDocument(includedFile, m_currentEnvironment, (bool)m_secondEnvironmentFile);
491             
492             //Check if the same file is being processed by one of the parents, and if it is, import it later on
493             if(Cpp::EnvironmentManager::self()->matchingLevel() <= Cpp::EnvironmentManager::Naive) {
494               
495               CPPParseJob* job = parentJob();
496               while(job->parentPreprocessor()) {
497                 job = job->parentPreprocessor()->parentJob();
498                 if(job->document() == indexedFile) {
499                   parentJob()->addDelayedImport(CPPParseJob::LineJobPair(job, sourceLine));
500                   return 0;
501                 }
502               }
503               
504 //               if(!includedContext) {
505 //                 //Check if there is a parsed version that is disabled by its header-guard right now, and enventually use that one.
506 //                 QList<ParsingEnvironmentFilePointer> allVersions = DUChain::self()->allEnvironmentFiles(indexedFile);
507 //                 foreach(ParsingEnvironmentFilePointer version, allVersions) {
508 //                   Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(version.data());
509 //                   
510 //                   if(envFile && (envFile->isProxyContext() || !m_secondEnvironmentFile) && !envFile->headerGuard().isEmpty()) {
511 //                     if(m_currentEnvironment->macroNameSet().contains(envFile->headerGuard())) {
512 //                       includedContext = envFile->topContext();
513 //                       
514 //                       break;
515 //                     }
516 //                   }
517 //                 }
518 //               }
519             }
520             
521             if(includedContext) {
522               Cpp::EnvironmentFilePointer includedEnvironment(dynamic_cast<Cpp::EnvironmentFile*>(includedContext->parsingEnvironmentFile().data()));
523               if( includedEnvironment ) {
524                 updateNeeded = CppUtils::needsUpdate(includedEnvironment, localPath, parentJob()->includePathUrls());
525                 //The ForceUpdateRecursive flag is removed before checking for satisfied features, so we can prevent double-updating through "wasUpdated()" below (see *1)
526                 updateNeeded |= !includedEnvironment->featuresSatisfied((TopDUContext::Features)(slaveMinimumFeatures & (~TopDUContext::ForceUpdateRecursive)));
527                 //(*1) Do not update again if ForceUpdate is given and the context was already updated during this run
528                 updateNeeded |= (slaveMinimumFeatures & TopDUContext::ForceUpdate) && !parentJob()->masterJob()->wasUpdated(includedContext.data());
529                 
530                 #if 0
531                 //If header-guards should be ignored, unguard the file
532                 if(Cpp::EnvironmentManager::self()->ignoreGuardsForImporting() &&
533                   !includedEnvironment->headerGuard().isEmpty() && m_currentEnvironment->macroNameSet().contains(includedEnvironment->headerGuard()))
534                 {
535                   m_currentEnvironment->removeMacro(includedEnvironment->headerGuard());
536                 }
537                 #endif
538                 
539                 if(!includedEnvironment->headerGuard().isEmpty() && m_currentEnvironment->macroNameSet().contains(includedEnvironment->headerGuard())) {
540                   updateForbidden = true;
541                   kDebug() << "forbidding update of" << includedFile;
542                   updateNeeded = false;         
543                 }
544               }
545             }
546         }
547
548         if( includedContext && (updateForbidden || (!updateNeeded && (!parentJob()->masterJob()->needUpdateEverything() || parentJob()->masterJob()->wasUpdated(includedContext)))) ) {
549             ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": took included file from the du-chain" << fileName; )
550
551             KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
552             parentJob()->addIncludedFile(includedContext, sourceLine);
553             KDevelop::ParsingEnvironmentFilePointer file = includedContext->parsingEnvironmentFile();
554             Cpp::EnvironmentFile* environmentFile = dynamic_cast<Cpp::EnvironmentFile*> (file.data());
555             if( environmentFile ) {
556                 m_currentEnvironment->merge( environmentFile, true );
557                 ifDebug( kDebug() << "PreprocessJob" << parentJob()->document().str() << "Merging included file into environment-file"; )
558             } else {
559                 ifDebug( kDebug(9007) << "preprocessjob: included file" << includedFile << "found in du-chain, but it has no parse-environment information, or it was not parsed by c++ support"; )
560             }
561         } else {
562             if(updateNeeded)
563               kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": need to update" << includedFile;
564             else if(parentJob()->masterJob()->needUpdateEverything() && includedContext)
565               kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": needUpateEverything, updating" << includedFile;
566             else
567               kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": no fitting entry for" << includedFile << "in du-chain, parsing";
568
569 /*            if( updateNeeded && !parentJob()->masterJob()->needUpdateEverything() ) {
570               //When a new include-file was found, that can influence not found declarations in all following encountered contexts, so they all need updating.
571               kDebug(9007) << "Marking every following encountered context to be updated";
572               parentJob()->masterJob()->setNeedUpdateEverything( true ); //@todo make this a bit more intelligent, instead of updating everything that follows
573             }*/
574             /// Why bother the threadweaver? We need the preprocessed text NOW so we simply parse the
575             /// included file right here. Parallel parsing cannot be used here, because we need the
576             /// macros before we can continue.
577
578             // Create a slave-job that will take over our macros.
579             // It will itself take our macros modify them, copy them back,
580             // and merge information into our m_firstEnvironmentFile
581
582             ///The second parameter is zero because we are in a background-thread and we here
583             ///cannot create a slave of the foreground cpp-support-part.
584             CPPParseJob* slaveJob = new CPPParseJob(includedFile, this);
585             
586             slaveJob->setMinimumFeatures(slaveMinimumFeatures);
587
588             slaveJob->setIncludedFromPath(included.second);
589
590             includeStack.append(DocumentCursor(parentJob()->document(), SimpleCursor(sourceLine, 0)));
591             slaveJob->setIncludeStack(includeStack);
592
593             slaveJob->parseForeground();
594
595             // Add the included file.
596             if(slaveJob->duChain())
597               parentJob()->addIncludedFile(slaveJob->duChain(), sourceLine);
598             else
599               kDebug(9007) << "parse-job for" << includedFile << "did not return a top-context";
600             delete slaveJob;
601         }
602         ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": file included"; )
603     
604         {
605           DUChainReadLocker lock(DUChain::lock());
606           if( m_updatingEnvironmentFile && m_updatingEnvironmentFile->missingIncludeFiles().contains(IndexedString(fileName)) ) {
607             //We are finding a file that was not in the include-path last time
608             //All following contexts need to be updated, because they may contain references to missing declarations
609             parentJob()->masterJob()->setNeedUpdateEverything( true );
610             kDebug(9007) << "Marking every following encountered context to be updated";
611           }
612         }
613     
614     } else {
615         kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": include not found:" << fileName;
616         KDevelop::ProblemPointer p(new Problem()); ///@todo create special include-problem
617         p->setSource(KDevelop::ProblemData::Preprocessor);
618         p->setDescription(i18n("Included file was not found: %1", fileName ));
619         p->setExplanation(i18n("Searched include path:\n%1", urlsToString(parentJob()->includePathUrls())));
620         p->setFinalLocation(DocumentRange(parentJob()->document(), SimpleRange(sourceLine,0, sourceLine+1,0)));
621         p->setLocationStack(parentJob()->includeStack());
622         p->setSolutionAssistant(KSharedPtr<KDevelop::IAssistant>(new Cpp::MissingIncludePathAssistant(parentJob()->masterJob()->document(), _fileName)));
623         parentJob()->addPreprocessorProblem(p);
624
625         ///@todo respect all the specialties like starting search at a specific path
626         ///Before doing that, model findInclude(..) exactly after the standard
627         m_firstEnvironmentFile->addMissingIncludeFile(IndexedString(fileName));
628     }
629     
630     return 0;
631 }
632
633 bool PreprocessJob::checkAbort()
634 {
635   if(ICore::self()->shuttingDown()) {
636     kDebug(9007) << "The application is shutting down";
637     return true;
638   }
639   
640   if(!ICore::self()->languageController()->language("C++") || !ICore::self()->languageController()->language("C++")->languageSupport()) {
641     kDebug(9007) << "Environment-manager disappeared" ;
642     return true;
643   }
644     if (CPPParseJob* parent = parentJob()) {
645         if (parent->abortRequested()) {
646             parent->abortJob();
647             m_success = false;
648             setFinished(true);
649             return true;
650         }
651
652     } else {
653         // What... the parent job got deleted??
654         kWarning(9007) << "Parent job disappeared!!" ;
655         m_success = false;
656         setFinished(true);
657         return true;
658     }
659
660     return false;
661 }
662
663 bool PreprocessJob::readContents()
664 {
665   KDevelop::ProblemPointer p = parentJob()->readContents();
666   
667   if(p)
668   {
669     p->setLocationStack(parentJob()->includeStack());
670     parentJob()->addPreprocessorProblem(p);
671     return false;
672   }
673   
674   m_firstEnvironmentFile->setModificationRevision( parentJob()->contents().modification );
675
676   m_contents = parentJob()->contents().contents;
677     
678     ifDebug( kDebug( 9007 ) << "===-- PREPROCESSING --===> "
679     << parentJob()->document().str()
680     << "<== readFromDisk:" << readFromDisk
681     << "size:" << contents.contents.length()
682     << endl; )
683     
684     return true;
685 }
686
687 bool PreprocessJob::success() const
688 {
689     return m_success;
690 }
691
692 KDevelop::ParsingEnvironment * PreprocessJob::m_standardEnvironment = 0;
693
694 const KDevelop::ParsingEnvironment * PreprocessJob::standardEnvironment()
695 {
696   if(!m_standardEnvironment)
697     m_standardEnvironment = createStandardEnvironment();
698
699   return m_standardEnvironment;
700 }
701
702 #include "preprocessjob.moc"
703