2
* This file is part of KDevelop
4
* Copyright 2006 Adam Treat <treat@kde.org>
5
* Copyright 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
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.
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.
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.
23
#include "preprocessjob.h"
25
//#include <valgrind/memcheck.h>
31
#include <QMutexLocker>
32
#include <QReadWriteLock>
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/editor/editorintegrator.h>
43
#include <language/interfaces/iproblem.h>
45
#include <threadweaver/Thread.h>
47
#include <interfaces/ilanguage.h>
49
#include "cpplanguagesupport.h"
50
#include "cppparsejob.h"
51
#include "parser/ast.h"
52
#include "parser/parsesession.h"
53
#include "parser/rpp/pp-engine.h"
54
#include "parser/rpp/pp-macro.h"
55
#include "parser/rpp/preprocessor.h"
56
#include "environmentmanager.h"
57
#include "cpppreprocessenvironment.h"
59
#include "cppdebughelper.h"
60
#include "codegen/unresolvedincludeassistant.h"
62
// #define ifDebug(x) x
65
const uint maxIncludeDepth = 50;
67
QString urlsToString(const QList<KUrl>& urlList) {
69
foreach( const KUrl& u, urlList )
70
paths += u.pathOrUrl() + "\n";
75
PreprocessJob::PreprocessJob(CPPParseJob * parent)
76
: ThreadWeaver::Job(parent)
77
, m_currentEnvironment(0)
78
, m_firstEnvironmentFile( new Cpp::EnvironmentFile( parent->document(), 0 ) )
80
, m_headerSectionEnded(false)
85
PreprocessJob::~PreprocessJob() {
86
delete m_currentEnvironment;
89
KDevelop::ParsingEnvironment* PreprocessJob::createStandardEnvironment() {
90
CppPreprocessEnvironment* ret = new CppPreprocessEnvironment(0, Cpp::EnvironmentFilePointer());
91
ret->merge( CppUtils::standardMacros() );
96
CPPParseJob * PreprocessJob::parentJob() const
98
return static_cast<CPPParseJob*>(const_cast<QObject*>(parent()));
101
void PreprocessJob::foundHeaderGuard(rpp::Stream& stream, KDevelop::IndexedString guardName)
105
KDevelop::DUChainWriteLocker lock(KDevelop::DUChain::lock());
107
m_currentEnvironment->environmentFile()->setHeaderGuard(guardName);
109
//In naive matching mode, we ignore the dependence on header-guards
110
if(Cpp::EnvironmentManager::matchingLevel() <= Cpp::EnvironmentManager::Naive)
111
m_currentEnvironment->removeString(guardName);
114
void PreprocessJob::run()
116
if(!ICore::self()->languageController()->language("C++")->languageSupport())
119
//If we have a parent, that parent already has locked the parse-lock
120
QReadLocker lock(parentJob()->parentPreprocessor() ? 0 : parentJob()->cpp()->language()->parseLock());
122
//It seems like we cannot influence the actual thread priority in thread-weaver, so for now set it here.
123
//It must be low so the GUI stays fluid.
124
if(QThread::currentThread())
125
QThread::currentThread()->setPriority(QThread::LowestPriority);
127
//kDebug(9007) << "Started pp job" << this << "parse" << parentJob()->parseJob() << "parent" << parentJob();
129
kDebug(9007) << "PreprocessJob: preprocessing" << parentJob()->document().str();
135
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
137
if(Cpp::EnvironmentManager::isSimplifiedMatching()) {
138
//Make sure that proxy-contexts and content-contexts never have the same identity, even if they have the same content.
139
m_firstEnvironmentFile->setIdentityOffset(1); //Mark the first environment-file as the proxy
140
IndexedString u = parentJob()->document();
141
m_secondEnvironmentFile = new Cpp::EnvironmentFile( u, 0 );
145
rpp::pp preprocessor(this);
146
m_pp = &preprocessor;
148
//Eventually initialize the environment with the parent-environment to get its macros
149
m_currentEnvironment = new CppPreprocessEnvironment( &preprocessor, m_firstEnvironmentFile );
151
//If we are included from another preprocessor, copy its macros
152
if( parentJob()->parentPreprocessor() ) {
153
m_currentEnvironment->swapMacros( parentJob()->parentPreprocessor()->m_currentEnvironment );
155
//Insert standard-macros
156
KDevelop::ParsingEnvironment* standardEnv = createStandardEnvironment();
157
parentJob()->mergeDefines(static_cast<CppPreprocessEnvironment&>(*standardEnv));
159
m_currentEnvironment->swapMacros( dynamic_cast<CppPreprocessEnvironment*>(standardEnv) );
163
Cpp::ReferenceCountedStringSet macroNamesAtBeginning = m_currentEnvironment->macroNameSet();
165
KDevelop::ParsingEnvironmentFilePointer updatingEnvironmentFile;
168
///Find a context that can be updated, and eventually break processing right here, if we notice we don't need to update
169
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
171
//Make sure we only match proxy-contexts for updating
172
m_currentEnvironment->setIdentityOffsetRestriction(m_firstEnvironmentFile->identityOffset());
174
updatingEnvironmentFile = KDevelop::ParsingEnvironmentFilePointer( KDevelop::DUChain::self()->environmentFileForDocument(parentJob()->document(), m_currentEnvironment, (bool)m_secondEnvironmentFile) );
176
if(parentJob()->masterJob() == parentJob() && updatingEnvironmentFile) {
177
//Check whether we need to run at all, or whether the file is already up to date
178
if(updatingEnvironmentFile->featuresSatisfied(parentJob()->minimumFeatures()) && updatingEnvironmentFile->featuresSatisfied(parentJob()->slaveMinimumFeatures())) {
179
KUrl localPath(parentJob()->document().toUrl());
180
localPath.setFileName(QString());
181
Cpp::EnvironmentFile* cppEnv = dynamic_cast<Cpp::EnvironmentFile*>(updatingEnvironmentFile.data());
183
//When possible, we determine whether an update is needed without getting the include-paths, because that's very expensive
184
bool needsUpdate = cppEnv->needsUpdate();
185
if(!cppEnv->missingIncludeFiles().isEmpty() && !needsUpdate) {
186
for(Cpp::ReferenceCountedStringSet::Iterator it = cppEnv->missingIncludeFiles().iterator(); it; ++it)
187
kDebug(9007) << updatingEnvironmentFile->url().str() << "has missing include:" << (*it).str();
190
KUrl::List includePaths = parentJob()->includePathUrls();
193
needsUpdate = CppUtils::needsUpdate(Cpp::EnvironmentFilePointer(cppEnv), localPath, includePaths);
197
parentJob()->setNeedsUpdate(false);
204
//We do this down here, so we eventually can prevent determining the include-paths if nothing needs to be updated
205
m_firstEnvironmentFile->setIncludePaths( parentJob()->masterJob()->includePaths() );
207
if(m_secondEnvironmentFile)
208
m_secondEnvironmentFile->setIncludePaths(m_firstEnvironmentFile->includePaths());
210
if (checkAbort() || !readContents())
214
///Find a context that can be updated
215
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
217
KDevelop::ReferencedTopDUContext updating;
218
if(updatingEnvironmentFile)
219
updating = updatingEnvironmentFile->topContext();
221
if(m_secondEnvironmentFile)
222
parentJob()->setUpdatingProxyContext( updating ); //The content-context to be updated will be searched later
224
parentJob()->setUpdatingContentContext( updating );
227
//We don't need to change anything, because the EnvironmentFile will be replaced with a new one
228
m_updatingEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>( dynamic_cast<Cpp::EnvironmentFile*>(updating->parsingEnvironmentFile().data()) );
230
if( m_secondEnvironmentFile && parentJob()->updatingProxyContext() ) {
231
// Must be true, because we explicitly passed the flag to chainForDocument
232
Q_ASSERT((parentJob()->updatingProxyContext()->parsingEnvironmentFile()->isProxyContext()));
236
preprocessor.setEnvironment( m_currentEnvironment );
238
PreprocessedContents result = preprocessor.processFile(parentJob()->document().str(), m_contents);
240
if(Cpp::EnvironmentManager::matchingLevel() <= Cpp::EnvironmentManager::Naive && !m_headerSectionEnded && !m_firstEnvironmentFile->headerGuard().isEmpty()) {
241
if(macroNamesAtBeginning.contains(m_firstEnvironmentFile->headerGuard())) {
242
//Remove the header-guard, and re-preprocess, since we don't do real environment-management(We don't allow empty versions)
243
m_currentEnvironment->removeMacro(m_firstEnvironmentFile->headerGuard());
244
result = preprocessor.processFile(parentJob()->document().str(), m_contents);
248
if(!m_headerSectionEnded) {
249
ifDebug( kDebug(9007) << parentJob()->document().str() << ": header-section was not ended"; )
250
headerSectionEndedInternal(0);
253
m_currentEnvironment->finishEnvironment(m_currentEnvironment->environmentFile() == m_updatingEnvironmentFile);
255
foreach (KDevelop::ProblemPointer p, preprocessor.problems()) {
256
p->setLocationStack(parentJob()->includeStack());
257
p->setSource(KDevelop::ProblemData::Preprocessor);
258
parentJob()->addPreprocessorProblem(p);
261
parentJob()->parseSession()->setContents( result, m_currentEnvironment->takeLocationTable() );
262
parentJob()->parseSession()->setUrl( parentJob()->document() );
265
if(m_secondEnvironmentFile)
266
parentJob()->setProxyEnvironmentFile( m_firstEnvironmentFile.data() );
268
parentJob()->setContentEnvironmentFile( m_firstEnvironmentFile.data() );
270
if(m_secondEnvironmentFile) {//Copy some information from the environment-file to its content-part
271
KDevelop::DUChainWriteLocker readLock(KDevelop::DUChain::lock());
272
m_secondEnvironmentFile->setModificationRevision(m_firstEnvironmentFile->modificationRevision());
273
if(m_firstEnvironmentFile->headerGuard().isEmpty())
274
m_firstEnvironmentFile->setHeaderGuard(m_secondEnvironmentFile->headerGuard());
276
m_secondEnvironmentFile->setHeaderGuard(m_firstEnvironmentFile->headerGuard());
279
if( m_secondEnvironmentFile ) {
280
//kDebug(9008) << parentJob()->document().str() << "Merging content-environment file into header environment-file";
281
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
282
m_firstEnvironmentFile->merge(*m_secondEnvironmentFile);
283
parentJob()->setContentEnvironmentFile(m_secondEnvironmentFile.data());
286
if( PreprocessJob* parentPreprocessor = parentJob()->parentPreprocessor() ) {
287
//If we are included from another preprocessor, give it back the modified macros,
288
parentPreprocessor->m_currentEnvironment->swapMacros( m_currentEnvironment );
289
//Merge include-file-set, defined macros, used macros, and string-set
290
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
291
parentPreprocessor->m_currentEnvironment->environmentFile()->merge(*m_firstEnvironmentFile);
293
/* kDebug(9007) << "Macros:";
294
for( rpp::Environment::EnvironmentMap::const_iterator it = m_currentEnvironment->environment().begin(); it != m_currentEnvironment->environment().end(); ++it ) {
295
kDebug(9007) << (*it)->name.str() << " from: " << (*it)->file << ":" << (*it)->sourceLine;
298
ifDebug( kDebug(9007) << "PreprocessJob: finished" << parentJob()->document().str(); )
301
m_currentEnvironment = 0; //Was given to the pp-engine, and will be destroyed by that
304
void PreprocessJob::headerSectionEnded(rpp::Stream& stream)
306
headerSectionEndedInternal(&stream);
309
TopDUContext* contentFromProxy(TopDUContext* ctx) {
310
if( ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->isProxyContext() ) {
312
ReferencedTopDUContext ref(ctx);
314
if(ctx->importedParentContexts().isEmpty()) {
315
kDebug() << "proxy-context for" << ctx->url().str() << "has no imports!" << ctx->ownIndex();
318
Q_ASSERT(!ctx->importedParentContexts().isEmpty());
319
return dynamic_cast<TopDUContext*>(ctx->importedParentContexts().first().context(0));
326
void PreprocessJob::headerSectionEndedInternal(rpp::Stream* stream)
328
bool closeStream = false;
329
m_headerSectionEnded = true;
331
ifDebug( kDebug(9007) << parentJob()->document().str() << "PreprocessJob::headerSectionEnded, " << parentJob()->includedFiles().count() << " included in header-section" << "upcoming identity-offset:" << m_pp->branchingHash()*19; )
333
if( m_secondEnvironmentFile ) {
334
m_secondEnvironmentFile->setIdentityOffset(m_pp->branchingHash()*19);
337
m_secondEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
338
m_firstEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
341
///Only allow content-contexts that have the same branching hash,
342
///because else they were differently influenced earlier by macros in the header-section
343
///Example: A file that has completely different content depending on an #ifdef
345
m_currentEnvironment->setIdentityOffsetRestriction(m_secondEnvironmentFile->identityOffset());
347
IndexedString u = parentJob()->document();
349
///Find a matching content-context
350
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
352
KDevelop::ReferencedTopDUContext content;
354
if(m_updatingEnvironmentFile)
355
content = KDevelop::ReferencedTopDUContext(contentFromProxy(m_updatingEnvironmentFile->topContext()));
357
content = KDevelop::DUChain::self()->chainForDocument(u, m_currentEnvironment);
359
m_currentEnvironment->disableIdentityOffsetRestriction();
362
//We have found a content-context that we can use
363
parentJob()->setUpdatingContentContext(content);
365
Q_ASSERT(!content->parsingEnvironmentFile()->isProxyContext());
367
Cpp::EnvironmentFilePointer contentEnvironment(dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data()));
368
Q_ASSERT(m_updatingEnvironmentFile || contentEnvironment->identityOffset() == m_secondEnvironmentFile->identityOffset());
370
///@todo think whether localPath is needed
371
KUrl localPath(parentJob()->document().str());
372
localPath.setFileName(QString());
374
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()))
375
&& Cpp::EnvironmentManager::matchingLevel() != Cpp::EnvironmentManager::Disabled) {
376
///@todo We never keep the duchain while updating now in disabled environment matching mode.
377
/// We don't need it there, and changes in imports may be simply ignored when the keeping is enabled.
378
/// However when full environment management is enabled this is needed, as the same content may be shared for multiple proxy contexts.
379
//We can completely re-use the specialized context:
380
m_secondEnvironmentFile = dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data());
381
m_updatingEnvironmentFile = m_secondEnvironmentFile;
383
//Merge the macros etc. into the current environment
384
m_currentEnvironment->merge( m_secondEnvironmentFile.data() );
386
ifDebug( kDebug(9007) << "closing data-stream, body does not need to be processed"; )
388
parentJob()->setKeepDuchain(true); //We truncate all following content, so we don't want to update the du-chain.
389
Q_ASSERT(m_secondEnvironmentFile);
391
ifDebug( kDebug(9007) << "updating content-context"; )
392
m_updatingEnvironmentFile = KSharedPtr<Cpp::EnvironmentFile>(dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data()));
393
//We will re-use the specialized context, but it needs updating. So we keep processing here.
394
//We don't need to change m_updatingEnvironmentFile, because we will create a new one.
397
//We need to process the content ourselves
398
ifDebug( kDebug(9007) << "could not find a matching content-context"; )
401
m_currentEnvironment->finishEnvironment();
403
m_currentEnvironment->setEnvironmentFile(m_secondEnvironmentFile);
412
rpp::Stream* PreprocessJob::sourceNeeded(QString& _fileName, IncludeType type, int sourceLine, bool skipCurrentPath)
416
uint currentDepth = 0;
417
CPPParseJob* job = parentJob();
418
while(job->parentPreprocessor()) {
420
job = job->parentPreprocessor()->parentJob();
422
if(currentDepth > maxIncludeDepth) {
423
kDebug(9007) << "maximum depth reached while including" << _fileName << "into" << parentJob()->document().str();
428
KUrl fileNameUrl(_fileName);
430
TopDUContext::Features slaveMinimumFeatures = parentJob()->slaveMinimumFeatures();
431
QString fileName = fileNameUrl.pathOrUrl();
436
ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": searching for include" << fileName; )
438
KUrl localPath(parentJob()->document().str());
439
localPath.setFileName(QString());
440
QStack<DocumentCursor> includeStack = parentJob()->includeStack();
444
from = parentJob()->includedFromPath();
446
QPair<KUrl, KUrl> included = CppUtils::findInclude( parentJob()->includePathUrls(), localPath, fileName, type, from );
447
KUrl includedFile = included.first;
448
if (includedFile.isValid()) {
450
IndexedString indexedFile(includedFile);
453
//Prevent recursion that may cause a crash
454
PreprocessJob* current = this;
456
if(current->parentJob()->document() == indexedFile) {
457
KDevelop::ProblemPointer p(new Problem()); ///@todo create special include-problem
458
p->setSource(KDevelop::ProblemData::Preprocessor);
459
p->setDescription(i18n("File was included recursively from within itself: %1", fileName ));
460
p->setFinalLocation(DocumentRange(parentJob()->document().str(), KTextEditor::Cursor(sourceLine,0), KTextEditor::Cursor(sourceLine+1,0)));
461
p->setLocationStack(parentJob()->includeStack());
462
parentJob()->addPreprocessorProblem(p);
465
current = current->parentJob()->parentPreprocessor();
469
ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": found include-file" << fileName << ":" << includedFile; )
471
KDevelop::ReferencedTopDUContext includedContext;
472
bool updateNeeded = false;
473
bool updateForbidden = false;
476
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
477
includedContext = KDevelop::DUChain::self()->chainForDocument(includedFile, m_currentEnvironment, (bool)m_secondEnvironmentFile);
479
//Check if the same file is being processed by one of the parents, and if it is, import it later on
480
if(Cpp::EnvironmentManager::matchingLevel() <= Cpp::EnvironmentManager::Naive) {
482
CPPParseJob* job = parentJob();
483
while(job->parentPreprocessor()) {
484
job = job->parentPreprocessor()->parentJob();
485
if(job->document() == indexedFile) {
486
parentJob()->addDelayedImport(CPPParseJob::LineJobPair(job, sourceLine));
491
// if(!includedContext) {
492
// //Check if there is a parsed version that is disabled by its header-guard right now, and enventually use that one.
493
// QList<ParsingEnvironmentFilePointer> allVersions = DUChain::self()->allEnvironmentFiles(indexedFile);
494
// foreach(ParsingEnvironmentFilePointer version, allVersions) {
495
// Cpp::EnvironmentFile* envFile = dynamic_cast<Cpp::EnvironmentFile*>(version.data());
497
// if(envFile && (envFile->isProxyContext() || !m_secondEnvironmentFile) && !envFile->headerGuard().isEmpty()) {
498
// if(m_currentEnvironment->macroNameSet().contains(envFile->headerGuard())) {
499
// includedContext = envFile->topContext();
508
if(includedContext) {
509
Cpp::EnvironmentFilePointer includedEnvironment(dynamic_cast<Cpp::EnvironmentFile*>(includedContext->parsingEnvironmentFile().data()));
510
if( includedEnvironment ) {
511
updateNeeded = CppUtils::needsUpdate(includedEnvironment, localPath, parentJob()->includePathUrls());
512
//The ForceUpdateRecursive flag is removed before checking for satisfied features, so we can prevent double-updating through "wasUpdated()" below (see *1)
513
updateNeeded |= !includedEnvironment->featuresSatisfied((TopDUContext::Features)(slaveMinimumFeatures & (~TopDUContext::ForceUpdateRecursive)));
514
//(*1) Do not update again if ForceUpdate is given and the context was already updated during this run
515
updateNeeded |= (slaveMinimumFeatures & TopDUContext::ForceUpdate) && !parentJob()->masterJob()->wasUpdated(includedContext.data());
518
//If header-guards should be ignored, unguard the file
519
if(Cpp::EnvironmentManager::ignoreGuardsForImporting() &&
520
!includedEnvironment->headerGuard().isEmpty() && m_currentEnvironment->macroNameSet().contains(includedEnvironment->headerGuard()))
522
m_currentEnvironment->removeMacro(includedEnvironment->headerGuard());
526
if(!includedEnvironment->headerGuard().isEmpty() && m_currentEnvironment->macroNameSet().contains(includedEnvironment->headerGuard())) {
527
updateForbidden = true;
528
kDebug() << "forbidding update of" << includedFile;
529
updateNeeded = false;
535
if( includedContext && (updateForbidden || (!updateNeeded && (!parentJob()->masterJob()->needUpdateEverything() || parentJob()->masterJob()->wasUpdated(includedContext)))) ) {
536
ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": took included file from the du-chain" << fileName; )
538
KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
539
parentJob()->addIncludedFile(includedContext, sourceLine);
540
KDevelop::ParsingEnvironmentFilePointer file = includedContext->parsingEnvironmentFile();
541
Cpp::EnvironmentFile* environmentFile = dynamic_cast<Cpp::EnvironmentFile*> (file.data());
542
if( environmentFile ) {
543
m_currentEnvironment->merge( environmentFile, true );
544
ifDebug( kDebug() << "PreprocessJob" << parentJob()->document().str() << "Merging included file into environment-file"; )
546
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"; )
550
kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": need to update" << includedFile;
551
else if(parentJob()->masterJob()->needUpdateEverything() && includedContext)
552
kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": needUpateEverything, updating" << includedFile;
554
kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": no fitting entry for" << includedFile << "in du-chain, parsing";
556
/* if( updateNeeded && !parentJob()->masterJob()->needUpdateEverything() ) {
557
//When a new include-file was found, that can influence not found declarations in all following encountered contexts, so they all need updating.
558
kDebug(9007) << "Marking every following encountered context to be updated";
559
parentJob()->masterJob()->setNeedUpdateEverything( true ); //@todo make this a bit more intelligent, instead of updating everything that follows
561
/// Why bother the threadweaver? We need the preprocessed text NOW so we simply parse the
562
/// included file right here. Parallel parsing cannot be used here, because we need the
563
/// macros before we can continue.
565
// Create a slave-job that will take over our macros.
566
// It will itself take our macros modify them, copy them back,
567
// and merge information into our m_firstEnvironmentFile
569
///The second parameter is zero because we are in a background-thread and we here
570
///cannot create a slave of the foreground cpp-support-part.
571
CPPParseJob* slaveJob = new CPPParseJob(includedFile, this);
573
slaveJob->setMinimumFeatures(slaveMinimumFeatures);
575
slaveJob->setIncludedFromPath(included.second);
577
includeStack.append(DocumentCursor(HashedString(parentJob()->document().str()), KTextEditor::Cursor(sourceLine, 0)));
578
slaveJob->setIncludeStack(includeStack);
580
slaveJob->parseForeground();
582
// Add the included file.
583
if(slaveJob->duChain())
584
parentJob()->addIncludedFile(slaveJob->duChain(), sourceLine);
586
kDebug(9007) << "parse-job for" << includedFile << "did not return a top-context";
589
ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": file included"; )
592
DUChainReadLocker lock(DUChain::lock());
593
if( m_updatingEnvironmentFile && m_updatingEnvironmentFile->missingIncludeFiles().contains(IndexedString(fileName)) ) {
594
//We are finding a file that was not in the include-path last time
595
//All following contexts need to be updated, because they may contain references to missing declarations
596
parentJob()->masterJob()->setNeedUpdateEverything( true );
597
kDebug(9007) << "Marking every following encountered context to be updated";
602
kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": include not found:" << fileName;
603
KDevelop::ProblemPointer p(new Problem()); ///@todo create special include-problem
604
p->setSource(KDevelop::ProblemData::Preprocessor);
605
p->setDescription(i18n("Included file was not found: %1", fileName ));
606
p->setExplanation(i18n("Searched include path:\n%1", urlsToString(parentJob()->includePathUrls())));
607
p->setFinalLocation(DocumentRange(parentJob()->document().str(), KTextEditor::Cursor(sourceLine,0), KTextEditor::Cursor(sourceLine+1,0)));
608
p->setLocationStack(parentJob()->includeStack());
609
p->setSolutionAssistant(KSharedPtr<KDevelop::IAssistant>(new Cpp::MissingIncludePathAssistant(parentJob()->masterJob()->document(), _fileName)));
610
parentJob()->addPreprocessorProblem(p);
612
///@todo respect all the specialties like starting search at a specific path
613
///Before doing that, model findInclude(..) exactly after the standard
614
m_firstEnvironmentFile->addMissingIncludeFile(IndexedString(fileName));
620
bool PreprocessJob::checkAbort()
622
if(ICore::self()->shuttingDown()) {
623
kDebug(9007) << "The application is shutting down";
627
if(!ICore::self()->languageController()->language("C++") || !ICore::self()->languageController()->language("C++")->languageSupport()) {
628
kDebug(9007) << "Environment-manager disappeared" ;
631
if (CPPParseJob* parent = parentJob()) {
632
if (parent->abortRequested()) {
640
// What... the parent job got deleted??
641
kWarning(9007) << "Parent job disappeared!!" ;
650
bool PreprocessJob::readContents()
652
bool readFromDisk = !parentJob()->contentsAvailableFromEditor();
653
parentJob()->setReadFromDisk(readFromDisk);
655
QString localFile(parentJob()->document().toUrl().toLocalFile());
657
QFileInfo fileInfo( localFile );
661
QFile file( localFile );
662
if ( !file.open( QIODevice::ReadOnly ) )
664
//Try using a code-representation, as artificial code may have been inserted
665
if(artificialCodeRepresentationExists(parentJob()->document())) {
666
CodeRepresentation::Ptr repr = createCodeRepresentation(parentJob()->document());
667
m_contents = repr->text().toUtf8();
668
kDebug() << "took contents for " << parentJob()->document().toUrl() << " from code-representation:\n" << m_contents;
672
KDevelop::ProblemPointer p(new Problem());
673
p->setSource(KDevelop::ProblemData::Disk);
674
p->setDescription(i18n( "Could not open file '%1'", localFile ));
675
switch (file.error()) {
676
case QFile::ReadError:
677
p->setExplanation(i18n("File could not be read from."));
679
case QFile::OpenError:
680
p->setExplanation(i18n("File could not be opened."));
682
case QFile::PermissionsError:
683
p->setExplanation(i18n("File permissions prevent opening for read."));
688
p->setFinalLocation(DocumentRange(parentJob()->document().str(), KTextEditor::Cursor::invalid(), KTextEditor::Cursor::invalid()));
689
p->setLocationStack(parentJob()->includeStack());
690
parentJob()->addPreprocessorProblem(p);
692
kWarning( 9007 ) << "Could not open file" << parentJob()->document().str() << "(path" << localFile << ")" ;
695
m_contents = file.readAll(); ///@todo respect local encoding settings. Currently, the file is expected to be utf-8
697
m_firstEnvironmentFile->setModificationRevision( KDevelop::ModificationRevision(fileInfo.lastModified()) );
700
///This has to be done, before parentJob()->revisionToken() is read, as it sets the token.
701
m_contents = parentJob()->contentsFromEditor().toUtf8();
702
m_firstEnvironmentFile->setModificationRevision( KDevelop::ModificationRevision( fileInfo.lastModified(), parentJob()->revisionToken() ) );
705
///@todo Modify parsing foronly changed ranges on editor files
707
//===--- Incremental Parsing!!! yay :) ---===//
708
kDebug() << "We could have just parsed the changed ranges:";
709
foreach (KTextEditor::SmartRange* range, parentJob()->changedRanges())
710
kDebug() << *range << range->text().join("\n").left(20) << "...";
713
ifDebug( kDebug( 9007 ) << "===-- PREPROCESSING --===> "
714
<< parentJob()->document().str()
715
<< "<== readFromDisk:" << readFromDisk
716
<< "size:" << contents.length()
722
bool PreprocessJob::success() const
727
KDevelop::ParsingEnvironment * PreprocessJob::m_standardEnvironment = 0;
729
const KDevelop::ParsingEnvironment * PreprocessJob::standardEnvironment()
731
if(!m_standardEnvironment)
732
m_standardEnvironment = createStandardEnvironment();
734
return m_standardEnvironment;
737
#include "preprocessjob.moc"