~ubuntu-branches/debian/sid/kdevelop/sid

« back to all changes in this revision

Viewing changes to languages/cpp/preprocessjob.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Lainé
  • Date: 2010-05-05 07:21:55 UTC
  • mfrom: (1.2.3 upstream) (5.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100505072155-h78lx19pu04sbhtn
Tags: 4:4.0.0-2
* Upload to unstable (Closes: #579947, #481832).
* Acknowledge obsolete NMU fixes (Closes: #562410, #546961).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/editor/editorintegrator.h>
 
43
#include <language/interfaces/iproblem.h>
 
44
 
 
45
#include <threadweaver/Thread.h>
 
46
 
 
47
#include <interfaces/ilanguage.h>
 
48
 
 
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"
 
58
 
 
59
#include "cppdebughelper.h"
 
60
#include "codegen/unresolvedincludeassistant.h"
 
61
 
 
62
// #define ifDebug(x) x
 
63
#include "cpputils.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
PreprocessJob::PreprocessJob(CPPParseJob * parent)
 
76
    : ThreadWeaver::Job(parent)
 
77
    , m_currentEnvironment(0)
 
78
    , m_firstEnvironmentFile( new Cpp::EnvironmentFile( parent->document(), 0 ) )
 
79
    , m_success(true)
 
80
    , m_headerSectionEnded(false)
 
81
    , m_pp(0)
 
82
{
 
83
}
 
84
 
 
85
PreprocessJob::~PreprocessJob() {
 
86
  delete m_currentEnvironment;
 
87
}
 
88
 
 
89
KDevelop::ParsingEnvironment* PreprocessJob::createStandardEnvironment() {
 
90
    CppPreprocessEnvironment* ret = new CppPreprocessEnvironment(0, Cpp::EnvironmentFilePointer());
 
91
    ret->merge( CppUtils::standardMacros() );
 
92
    
 
93
    return ret;
 
94
}
 
95
 
 
96
CPPParseJob * PreprocessJob::parentJob() const
 
97
{
 
98
    return static_cast<CPPParseJob*>(const_cast<QObject*>(parent()));
 
99
}
 
100
 
 
101
void PreprocessJob::foundHeaderGuard(rpp::Stream& stream, KDevelop::IndexedString guardName)
 
102
{
 
103
  Q_UNUSED(stream);
 
104
  
 
105
  KDevelop::DUChainWriteLocker lock(KDevelop::DUChain::lock());
 
106
  
 
107
  m_currentEnvironment->environmentFile()->setHeaderGuard(guardName);
 
108
  
 
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);
 
112
}
 
113
 
 
114
void PreprocessJob::run()
 
115
{
 
116
    if(!ICore::self()->languageController()->language("C++")->languageSupport())
 
117
      return;
 
118
  
 
119
    //If we have a parent, that parent already has locked the parse-lock
 
120
    QReadLocker lock(parentJob()->parentPreprocessor() ? 0 : parentJob()->cpp()->language()->parseLock());
 
121
    
 
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);
 
126
 
 
127
    //kDebug(9007) << "Started pp job" << this << "parse" << parentJob()->parseJob() << "parent" << parentJob();
 
128
 
 
129
    kDebug(9007) << "PreprocessJob: preprocessing" << parentJob()->document().str();
 
130
 
 
131
    if (checkAbort())
 
132
        return;
 
133
 
 
134
    {
 
135
      KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
 
136
      
 
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 );
 
142
      }
 
143
    }
 
144
 
 
145
    rpp::pp preprocessor(this);
 
146
    m_pp = &preprocessor;
 
147
 
 
148
    //Eventually initialize the environment with the parent-environment to get its macros
 
149
    m_currentEnvironment = new CppPreprocessEnvironment( &preprocessor, m_firstEnvironmentFile );
 
150
 
 
151
    //If we are included from another preprocessor, copy its macros
 
152
    if( parentJob()->parentPreprocessor() ) {
 
153
        m_currentEnvironment->swapMacros( parentJob()->parentPreprocessor()->m_currentEnvironment );
 
154
    } else {
 
155
        //Insert standard-macros
 
156
        KDevelop::ParsingEnvironment* standardEnv = createStandardEnvironment();
 
157
        parentJob()->mergeDefines(static_cast<CppPreprocessEnvironment&>(*standardEnv));
 
158
        
 
159
        m_currentEnvironment->swapMacros( dynamic_cast<CppPreprocessEnvironment*>(standardEnv) );
 
160
        delete standardEnv;
 
161
    }
 
162
    
 
163
    Cpp::ReferenceCountedStringSet macroNamesAtBeginning = m_currentEnvironment->macroNameSet();
 
164
    
 
165
    KDevelop::ParsingEnvironmentFilePointer updatingEnvironmentFile;
 
166
    
 
167
    {
 
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());
 
170
        
 
171
        //Make sure we only match proxy-contexts for updating
 
172
        m_currentEnvironment->setIdentityOffsetRestriction(m_firstEnvironmentFile->identityOffset());
 
173
        
 
174
        updatingEnvironmentFile = KDevelop::ParsingEnvironmentFilePointer( KDevelop::DUChain::self()->environmentFileForDocument(parentJob()->document(), m_currentEnvironment, (bool)m_secondEnvironmentFile) );
 
175
        
 
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());
 
182
            Q_ASSERT(cppEnv);
 
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();
 
188
                
 
189
                readLock.unlock();
 
190
                KUrl::List includePaths = parentJob()->includePathUrls();
 
191
                readLock.lock();
 
192
                
 
193
                needsUpdate = CppUtils::needsUpdate(Cpp::EnvironmentFilePointer(cppEnv), localPath, includePaths);
 
194
              }
 
195
            
 
196
            if(!needsUpdate) {
 
197
              parentJob()->setNeedsUpdate(false);
 
198
              return;
 
199
            }
 
200
          }
 
201
        }
 
202
    }
 
203
    
 
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() );
 
206
    
 
207
    if(m_secondEnvironmentFile)
 
208
      m_secondEnvironmentFile->setIncludePaths(m_firstEnvironmentFile->includePaths());
 
209
 
 
210
    if (checkAbort() || !readContents())
 
211
        return;
 
212
 
 
213
    {
 
214
        ///Find a context that can be updated
 
215
        KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
 
216
        
 
217
        KDevelop::ReferencedTopDUContext updating;
 
218
        if(updatingEnvironmentFile)
 
219
          updating = updatingEnvironmentFile->topContext();
 
220
 
 
221
        if(m_secondEnvironmentFile)
 
222
          parentJob()->setUpdatingProxyContext( updating ); //The content-context to be updated will be searched later
 
223
        else
 
224
          parentJob()->setUpdatingContentContext( updating );
 
225
      
 
226
        if( 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()) );
 
229
        }
 
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()));
 
233
        }
 
234
    }
 
235
    
 
236
    preprocessor.setEnvironment( m_currentEnvironment );
 
237
 
 
238
    PreprocessedContents result = preprocessor.processFile(parentJob()->document().str(), m_contents);
 
239
 
 
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);
 
245
      }
 
246
    }
 
247
    
 
248
    if(!m_headerSectionEnded) {
 
249
      ifDebug( kDebug(9007) << parentJob()->document().str() << ": header-section was not ended"; )
 
250
      headerSectionEndedInternal(0);
 
251
    }
 
252
    
 
253
    m_currentEnvironment->finishEnvironment(m_currentEnvironment->environmentFile() == m_updatingEnvironmentFile);
 
254
    
 
255
    foreach (KDevelop::ProblemPointer p, preprocessor.problems()) {
 
256
      p->setLocationStack(parentJob()->includeStack());
 
257
      p->setSource(KDevelop::ProblemData::Preprocessor);
 
258
      parentJob()->addPreprocessorProblem(p);
 
259
    }
 
260
 
 
261
    parentJob()->parseSession()->setContents( result, m_currentEnvironment->takeLocationTable() );
 
262
    parentJob()->parseSession()->setUrl( parentJob()->document() );
 
263
 
 
264
    
 
265
    if(m_secondEnvironmentFile)
 
266
      parentJob()->setProxyEnvironmentFile( m_firstEnvironmentFile.data() );
 
267
    else
 
268
      parentJob()->setContentEnvironmentFile( m_firstEnvironmentFile.data() );
 
269
    
 
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());
 
275
        else
 
276
          m_secondEnvironmentFile->setHeaderGuard(m_firstEnvironmentFile->headerGuard());
 
277
    }
 
278
    
 
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());
 
284
    }
 
285
    
 
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);
 
292
    }else{
 
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;
 
296
        }*/
 
297
    }
 
298
    ifDebug( kDebug(9007) << "PreprocessJob: finished" << parentJob()->document().str(); )
 
299
 
 
300
    m_pp = 0;
 
301
    m_currentEnvironment = 0; //Was given to the pp-engine, and will be destroyed by that
 
302
}
 
303
 
 
304
void PreprocessJob::headerSectionEnded(rpp::Stream& stream)
 
305
{
 
306
  headerSectionEndedInternal(&stream);
 
307
}
 
308
 
 
309
TopDUContext* contentFromProxy(TopDUContext* ctx) {
 
310
    if( ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->isProxyContext() ) {
 
311
        {
 
312
          ReferencedTopDUContext ref(ctx);
 
313
        }
 
314
        if(ctx->importedParentContexts().isEmpty()) {
 
315
          kDebug() << "proxy-context for" << ctx->url().str() << "has no imports!" << ctx->ownIndex();
 
316
          return 0;
 
317
        }
 
318
        Q_ASSERT(!ctx->importedParentContexts().isEmpty());
 
319
        return dynamic_cast<TopDUContext*>(ctx->importedParentContexts().first().context(0));
 
320
    }else{
 
321
        return ctx;
 
322
    }
 
323
}
 
324
 
 
325
 
 
326
void PreprocessJob::headerSectionEndedInternal(rpp::Stream* stream)
 
327
{
 
328
    bool closeStream = false;
 
329
    m_headerSectionEnded = true;
 
330
 
 
331
    ifDebug( kDebug(9007) << parentJob()->document().str() << "PreprocessJob::headerSectionEnded, " << parentJob()->includedFiles().count() << " included in header-section" << "upcoming identity-offset:" << m_pp->branchingHash()*19; )
 
332
    
 
333
    if( m_secondEnvironmentFile ) {
 
334
        m_secondEnvironmentFile->setIdentityOffset(m_pp->branchingHash()*19);
 
335
 
 
336
        if( stream ) {
 
337
          m_secondEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
 
338
          m_firstEnvironmentFile->setContentStartLine(stream->originalInputPosition().line);
 
339
        }
 
340
 
 
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
 
344
 
 
345
        m_currentEnvironment->setIdentityOffsetRestriction(m_secondEnvironmentFile->identityOffset());
 
346
        
 
347
        IndexedString u = parentJob()->document();
 
348
 
 
349
        ///Find a matching content-context
 
350
        KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
 
351
 
 
352
        KDevelop::ReferencedTopDUContext content;
 
353
 
 
354
        if(m_updatingEnvironmentFile)
 
355
          content = KDevelop::ReferencedTopDUContext(contentFromProxy(m_updatingEnvironmentFile->topContext()));
 
356
        else
 
357
          content = KDevelop::DUChain::self()->chainForDocument(u, m_currentEnvironment);
 
358
 
 
359
        m_currentEnvironment->disableIdentityOffsetRestriction();
 
360
 
 
361
        if(content) {
 
362
            //We have found a content-context that we can use
 
363
            parentJob()->setUpdatingContentContext(content);
 
364
 
 
365
            Q_ASSERT(!content->parsingEnvironmentFile()->isProxyContext());
 
366
            
 
367
            Cpp::EnvironmentFilePointer contentEnvironment(dynamic_cast<Cpp::EnvironmentFile*>(content->parsingEnvironmentFile().data()));
 
368
            Q_ASSERT(m_updatingEnvironmentFile || contentEnvironment->identityOffset() == m_secondEnvironmentFile->identityOffset());            
 
369
 
 
370
            ///@todo think whether localPath is needed
 
371
            KUrl localPath(parentJob()->document().str());
 
372
            localPath.setFileName(QString());
 
373
            
 
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;
 
382
                
 
383
                //Merge the macros etc. into the current environment
 
384
                m_currentEnvironment->merge( m_secondEnvironmentFile.data() );
 
385
 
 
386
                ifDebug( kDebug(9007) << "closing data-stream, body does not need to be processed"; )
 
387
                closeStream = true;
 
388
                parentJob()->setKeepDuchain(true); //We truncate all following content, so we don't want to update the du-chain.
 
389
                Q_ASSERT(m_secondEnvironmentFile);
 
390
            } else {
 
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.
 
395
            }
 
396
        } else {
 
397
            //We need to process the content ourselves
 
398
            ifDebug( kDebug(9007) << "could not find a matching content-context"; )
 
399
        }
 
400
 
 
401
        m_currentEnvironment->finishEnvironment();
 
402
 
 
403
        m_currentEnvironment->setEnvironmentFile(m_secondEnvironmentFile);
 
404
    }
 
405
 
 
406
    if( stream ) {
 
407
      if( closeStream )
 
408
        stream->toEnd();
 
409
    }
 
410
}
 
411
 
 
412
rpp::Stream* PreprocessJob::sourceNeeded(QString& _fileName, IncludeType type, int sourceLine, bool skipCurrentPath)
 
413
{
 
414
    Q_UNUSED(type)
 
415
    if(0){
 
416
      uint currentDepth = 0;
 
417
      CPPParseJob* job = parentJob();
 
418
      while(job->parentPreprocessor()) {
 
419
        ++currentDepth;
 
420
        job = job->parentPreprocessor()->parentJob();
 
421
      }
 
422
      if(currentDepth > maxIncludeDepth) {
 
423
        kDebug(9007) << "maximum depth reached while including" << _fileName << "into" << parentJob()->document().str();
 
424
        return 0;
 
425
      }
 
426
    }
 
427
    
 
428
    KUrl fileNameUrl(_fileName);
 
429
    
 
430
    TopDUContext::Features slaveMinimumFeatures = parentJob()->slaveMinimumFeatures();
 
431
    QString fileName = fileNameUrl.pathOrUrl();
 
432
    
 
433
    if (checkAbort())
 
434
        return 0;
 
435
 
 
436
    ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": searching for include" << fileName; )
 
437
 
 
438
    KUrl localPath(parentJob()->document().str());
 
439
    localPath.setFileName(QString());
 
440
    QStack<DocumentCursor> includeStack = parentJob()->includeStack();
 
441
 
 
442
    KUrl from;
 
443
    if (skipCurrentPath)
 
444
      from = parentJob()->includedFromPath();
 
445
 
 
446
    QPair<KUrl, KUrl> included = CppUtils::findInclude( parentJob()->includePathUrls(), localPath, fileName, type, from );
 
447
    KUrl includedFile = included.first;
 
448
    if (includedFile.isValid()) {
 
449
      
 
450
        IndexedString indexedFile(includedFile);
 
451
        
 
452
        {
 
453
          //Prevent recursion that may cause a crash
 
454
          PreprocessJob* current = this;
 
455
          while(current) {
 
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);
 
463
              return 0;
 
464
            }
 
465
            current = current->parentJob()->parentPreprocessor();
 
466
          }
 
467
        }
 
468
      
 
469
        ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": found include-file" << fileName << ":" << includedFile; )
 
470
 
 
471
        KDevelop::ReferencedTopDUContext includedContext;
 
472
        bool updateNeeded = false;
 
473
        bool updateForbidden = false;
 
474
 
 
475
        {
 
476
            KDevelop::DUChainReadLocker readLock(KDevelop::DUChain::lock());
 
477
            includedContext = KDevelop::DUChain::self()->chainForDocument(includedFile, m_currentEnvironment, (bool)m_secondEnvironmentFile);
 
478
            
 
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) {
 
481
              
 
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));
 
487
                  return 0;
 
488
                }
 
489
              }
 
490
              
 
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());
 
496
//                   
 
497
//                   if(envFile && (envFile->isProxyContext() || !m_secondEnvironmentFile) && !envFile->headerGuard().isEmpty()) {
 
498
//                     if(m_currentEnvironment->macroNameSet().contains(envFile->headerGuard())) {
 
499
//                       includedContext = envFile->topContext();
 
500
//                       
 
501
//                       break;
 
502
//                     }
 
503
//                   }
 
504
//                 }
 
505
//               }
 
506
            }
 
507
            
 
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());
 
516
                
 
517
                #if 0
 
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()))
 
521
                {
 
522
                  m_currentEnvironment->removeMacro(includedEnvironment->headerGuard());
 
523
                }
 
524
                #endif
 
525
                
 
526
                if(!includedEnvironment->headerGuard().isEmpty() && m_currentEnvironment->macroNameSet().contains(includedEnvironment->headerGuard())) {
 
527
                  updateForbidden = true;
 
528
                  kDebug() << "forbidding update of" << includedFile;
 
529
                  updateNeeded = false;         
 
530
                }
 
531
              }
 
532
            }
 
533
        }
 
534
 
 
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; )
 
537
 
 
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"; )
 
545
            } else {
 
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"; )
 
547
            }
 
548
        } else {
 
549
            if(updateNeeded)
 
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;
 
553
            else
 
554
              kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << ": no fitting entry for" << includedFile << "in du-chain, parsing";
 
555
 
 
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
 
560
            }*/
 
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.
 
564
 
 
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
 
568
 
 
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);
 
572
            
 
573
            slaveJob->setMinimumFeatures(slaveMinimumFeatures);
 
574
 
 
575
            slaveJob->setIncludedFromPath(included.second);
 
576
 
 
577
            includeStack.append(DocumentCursor(HashedString(parentJob()->document().str()), KTextEditor::Cursor(sourceLine, 0)));
 
578
            slaveJob->setIncludeStack(includeStack);
 
579
 
 
580
            slaveJob->parseForeground();
 
581
 
 
582
            // Add the included file.
 
583
            if(slaveJob->duChain())
 
584
              parentJob()->addIncludedFile(slaveJob->duChain(), sourceLine);
 
585
            else
 
586
              kDebug(9007) << "parse-job for" << includedFile << "did not return a top-context";
 
587
            delete slaveJob;
 
588
        }
 
589
        ifDebug( kDebug(9007) << "PreprocessJob" << parentJob()->document().str() << "(" << m_currentEnvironment->environment().size() << "macros)" << ": file included"; )
 
590
    
 
591
        {
 
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";
 
598
          }
 
599
        }
 
600
    
 
601
    } else {
 
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);
 
611
 
 
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));
 
615
    }
 
616
    
 
617
    return 0;
 
618
}
 
619
 
 
620
bool PreprocessJob::checkAbort()
 
621
{
 
622
  if(ICore::self()->shuttingDown()) {
 
623
    kDebug(9007) << "The application is shutting down";
 
624
    return true;
 
625
  }
 
626
  
 
627
  if(!ICore::self()->languageController()->language("C++") || !ICore::self()->languageController()->language("C++")->languageSupport()) {
 
628
    kDebug(9007) << "Environment-manager disappeared" ;
 
629
    return true;
 
630
  }
 
631
    if (CPPParseJob* parent = parentJob()) {
 
632
        if (parent->abortRequested()) {
 
633
            parent->abortJob();
 
634
            m_success = false;
 
635
            setFinished(true);
 
636
            return true;
 
637
        }
 
638
 
 
639
    } else {
 
640
        // What... the parent job got deleted??
 
641
        kWarning(9007) << "Parent job disappeared!!" ;
 
642
        m_success = false;
 
643
        setFinished(true);
 
644
        return true;
 
645
    }
 
646
 
 
647
    return false;
 
648
}
 
649
 
 
650
bool PreprocessJob::readContents()
 
651
{
 
652
    bool readFromDisk = !parentJob()->contentsAvailableFromEditor();
 
653
    parentJob()->setReadFromDisk(readFromDisk);
 
654
    
 
655
    QString localFile(parentJob()->document().toUrl().toLocalFile());
 
656
  
 
657
    QFileInfo fileInfo( localFile );
 
658
    
 
659
    if ( readFromDisk )
 
660
    {
 
661
        QFile file( localFile );
 
662
        if ( !file.open( QIODevice::ReadOnly ) )
 
663
        {
 
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;
 
669
              return true;
 
670
            }
 
671
          
 
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."));
 
678
                  break;
 
679
              case QFile::OpenError:
 
680
                  p->setExplanation(i18n("File could not be opened."));
 
681
                  break;
 
682
              case QFile::PermissionsError:
 
683
                  p->setExplanation(i18n("File permissions prevent opening for read."));
 
684
                  break;
 
685
              default:
 
686
                  break;
 
687
            }
 
688
            p->setFinalLocation(DocumentRange(parentJob()->document().str(), KTextEditor::Cursor::invalid(), KTextEditor::Cursor::invalid()));
 
689
            p->setLocationStack(parentJob()->includeStack());
 
690
            parentJob()->addPreprocessorProblem(p);
 
691
 
 
692
            kWarning( 9007 ) << "Could not open file" << parentJob()->document().str() << "(path" << localFile << ")" ;
 
693
            return false;
 
694
        }
 
695
        m_contents = file.readAll(); ///@todo respect local encoding settings. Currently, the file is expected to be utf-8
 
696
        file.close();
 
697
        m_firstEnvironmentFile->setModificationRevision( KDevelop::ModificationRevision(fileInfo.lastModified()) );
 
698
    }
 
699
    else{
 
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() ) );
 
703
    }
 
704
    
 
705
    ///@todo Modify parsing foronly changed ranges on editor files
 
706
#if 0
 
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) << "...";
 
711
#endif
 
712
    
 
713
    ifDebug( kDebug( 9007 ) << "===-- PREPROCESSING --===> "
 
714
    << parentJob()->document().str()
 
715
    << "<== readFromDisk:" << readFromDisk
 
716
    << "size:" << contents.length()
 
717
    << endl; )
 
718
    
 
719
    return true;
 
720
}
 
721
 
 
722
bool PreprocessJob::success() const
 
723
{
 
724
    return m_success;
 
725
}
 
726
 
 
727
KDevelop::ParsingEnvironment * PreprocessJob::m_standardEnvironment = 0;
 
728
 
 
729
const KDevelop::ParsingEnvironment * PreprocessJob::standardEnvironment()
 
730
{
 
731
  if(!m_standardEnvironment)
 
732
    m_standardEnvironment = createStandardEnvironment();
 
733
 
 
734
  return m_standardEnvironment;
 
735
}
 
736
 
 
737
#include "preprocessjob.moc"
 
738