CppDocument.cpp 19.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
con's avatar
con committed
29
30

#include "CppDocument.h"
31
#include "FastPreprocessor.h"
32
33
#include "LookupContext.h"
#include "Overview.h"
hjk's avatar
hjk committed
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cplusplus/Bind.h>
#include <cplusplus/Control.h>
#include <cplusplus/TranslationUnit.h>
#include <cplusplus/DiagnosticClient.h>
#include <cplusplus/Literals.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/Names.h>
#include <cplusplus/AST.h>
#include <cplusplus/ASTPatternBuilder.h>
#include <cplusplus/ASTMatcher.h>
#include <cplusplus/Scope.h>
#include <cplusplus/SymbolVisitor.h>
#include <cplusplus/NameVisitor.h>
#include <cplusplus/TypeVisitor.h>
#include <cplusplus/CoreTypes.h>
hjk's avatar
hjk committed
50

51
52
53
#include <QByteArray>
#include <QBitArray>
#include <QDir>
hjk's avatar
hjk committed
54
#include <QDebug>
con's avatar
con committed
55

Christian Kamm's avatar
Christian Kamm committed
56
57
58
59
60
/*!
    \namespace CPlusPlus
    The namespace for C++ related tools.
*/

con's avatar
con committed
61
62
63
64
using namespace CPlusPlus;

namespace {

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class LastVisibleSymbolAt: protected SymbolVisitor
{
    Symbol *root;
    unsigned line;
    unsigned column;
    Symbol *symbol;

public:
    LastVisibleSymbolAt(Symbol *root)
        : root(root), line(0), column(0), symbol(0) {}

    Symbol *operator()(unsigned line, unsigned column)
    {
        this->line = line;
        this->column = column;
        this->symbol = 0;
        accept(root);
        if (! symbol)
            symbol = root;
        return symbol;
    }

protected:
    bool preVisit(Symbol *s)
    {
        if (s->asBlock()) {
91
            if (s->line() < line || (s->line() == line && s->column() <= column))
92
93
94
95
96
97
98
99
100
101
102
                return true;
            // skip blocks
        } if (s->line() < line || (s->line() == line && s->column() <= column)) {
            symbol = s;
            return true;
        }

        return false;
    }
};

103
104
105
106
107
108
109
110
class FindScopeAt: protected SymbolVisitor
{
    TranslationUnit *_unit;
    unsigned _line;
    unsigned _column;
    Scope *_scope;

public:
Erik Verbruggen's avatar
Erik Verbruggen committed
111
    /** line and column should be 1-based */
112
113
114
115
116
117
118
119
120
121
    FindScopeAt(TranslationUnit *unit, unsigned line, unsigned column)
        : _unit(unit), _line(line), _column(column), _scope(0) {}

    Scope *operator()(Symbol *symbol)
    {
        accept(symbol);
        return _scope;
    }

protected:
Roberto Raggi's avatar
Roberto Raggi committed
122
    bool process(Scope *symbol)
123
124
    {
        if (! _scope) {
Roberto Raggi's avatar
Roberto Raggi committed
125
            Scope *scope = symbol;
126

Roberto Raggi's avatar
Roberto Raggi committed
127
128
            for (unsigned i = 0; i < scope->memberCount(); ++i) {
                accept(scope->memberAt(i));
129
130
131
132
133
134

                if (_scope)
                    return false;
            }

            unsigned startLine, startColumn;
135
            _unit->getPosition(scope->startOffset(), &startLine, &startColumn);
136
137
138

            if (_line > startLine || (_line == startLine && _column >= startColumn)) {
                unsigned endLine, endColumn;
139
                _unit->getPosition(scope->endOffset(), &endLine, &endColumn);
140

141
                if (_line < endLine || (_line == endLine && _column < endColumn))
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
                    _scope = scope;
            }
        }

        return false;
    }

    using SymbolVisitor::visit;

    virtual bool preVisit(Symbol *)
    { return ! _scope; }

    virtual bool visit(UsingNamespaceDirective *) { return false; }
    virtual bool visit(UsingDeclaration *) { return false; }
    virtual bool visit(NamespaceAlias *) { return false; }
    virtual bool visit(Declaration *) { return false; }
    virtual bool visit(Argument *) { return false; }
    virtual bool visit(TypenameArgument *) { return false; }
    virtual bool visit(BaseClass *) { return false; }
    virtual bool visit(ForwardClassDeclaration *) { return false; }

    virtual bool visit(Enum *symbol)
    { return process(symbol); }

    virtual bool visit(Function *symbol)
    { return process(symbol); }

    virtual bool visit(Namespace *symbol)
    { return process(symbol); }

    virtual bool visit(Class *symbol)
    { return process(symbol); }

    virtual bool visit(Block *symbol)
    { return process(symbol); }

    // Objective-C
    virtual bool visit(ObjCBaseClass *) { return false; }
    virtual bool visit(ObjCBaseProtocol *) { return false; }
    virtual bool visit(ObjCForwardClassDeclaration *) { return false; }
    virtual bool visit(ObjCForwardProtocolDeclaration *) { return false; }
    virtual bool visit(ObjCPropertyDeclaration *) { return false; }
Erik Verbruggen's avatar
Erik Verbruggen committed
184
185
186
187
188
189
190
191
192

    virtual bool visit(ObjCClass *symbol)
    { return process(symbol); }

    virtual bool visit(ObjCProtocol *symbol)
    { return process(symbol); }

    virtual bool visit(ObjCMethod *symbol)
    { return process(symbol); }
193
194
};

195

196
197
#define DO_NOT_DUMP_ALL_PARSER_ERRORS

hjk's avatar
hjk committed
198
199
200
201
202
203
204
class DocumentDiagnosticClient : public DiagnosticClient
{
    enum { MAX_MESSAGE_COUNT = 10 };

public:
    DocumentDiagnosticClient(Document *doc, QList<Document::DiagnosticMessage> *messages)
        : doc(doc),
Roberto Raggi's avatar
Roberto Raggi committed
205
206
          messages(messages),
          errorCount(0)
hjk's avatar
hjk committed
207
208
209
    { }

    virtual void report(int level,
Roberto Raggi's avatar
Roberto Raggi committed
210
                        const StringLiteral *fileId,
hjk's avatar
hjk committed
211
212
213
                        unsigned line, unsigned column,
                        const char *format, va_list ap)
    {
Roberto Raggi's avatar
Roberto Raggi committed
214
215
216
        if (level == Error) {
            ++errorCount;

217
#ifdef DO_NOT_DUMP_ALL_PARSER_ERRORS
Roberto Raggi's avatar
Roberto Raggi committed
218
219
            if (errorCount >= MAX_MESSAGE_COUNT)
                return; // ignore the error
220
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
Roberto Raggi's avatar
Roberto Raggi committed
221
        }
con's avatar
con committed
222

hjk's avatar
hjk committed
223
        const QString fileName = QString::fromUtf8(fileId->chars(), fileId->size());
con's avatar
con committed
224

hjk's avatar
hjk committed
225
226
        if (fileName != doc->fileName())
            return;
con's avatar
con committed
227

hjk's avatar
hjk committed
228
229
        QString message;
        message.vsprintf(format, ap);
con's avatar
con committed
230

231
232
233
234
235
236
237
238
239
240
#ifndef DO_NOT_DUMP_ALL_PARSER_ERRORS
        {
            const char *levelStr = "Unknown level";
            if (level == Document::DiagnosticMessage::Warning) levelStr = "Warning";
            if (level == Document::DiagnosticMessage::Error) levelStr = "Error";
            if (level == Document::DiagnosticMessage::Fatal) levelStr = "Fatal";
            qDebug("%s:%u:%u: %s: %s", fileId->chars(), line, column, levelStr, message.toUtf8().constData());
        }
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS

hjk's avatar
hjk committed
241
242
243
244
        Document::DiagnosticMessage m(convertLevel(level), doc->fileName(),
                                      line, column, message);
        messages->append(m);
    }
con's avatar
con committed
245

hjk's avatar
hjk committed
246
247
248
249
250
251
    static int convertLevel(int level) {
        switch (level) {
            case Warning: return Document::DiagnosticMessage::Warning;
            case Error:   return Document::DiagnosticMessage::Error;
            case Fatal:   return Document::DiagnosticMessage::Fatal;
            default:      return Document::DiagnosticMessage::Error;
con's avatar
con committed
252
        }
hjk's avatar
hjk committed
253
    }
con's avatar
con committed
254

Roberto Raggi's avatar
Roberto Raggi committed
255
private:
hjk's avatar
hjk committed
256
257
    Document *doc;
    QList<Document::DiagnosticMessage> *messages;
Roberto Raggi's avatar
Roberto Raggi committed
258
    int errorCount;
hjk's avatar
hjk committed
259
};
con's avatar
con committed
260

hjk's avatar
hjk committed
261
} // anonymous namespace
con's avatar
con committed
262

Christian Kamm's avatar
Christian Kamm committed
263

con's avatar
con committed
264
Document::Document(const QString &fileName)
265
    : _fileName(QDir::cleanPath(fileName)),
266
      _globalNamespace(0),
267
      _revision(0),
268
269
      _editorRevision(0),
      _checkMode(0)
con's avatar
con committed
270
271
272
273
274
275
{
    _control = new Control();

    _control->setDiagnosticClient(new DocumentDiagnosticClient(this, &_diagnosticMessages));

    const QByteArray localFileName = fileName.toUtf8();
276
    const StringLiteral *fileId = _control->stringLiteral(localFileName.constData(),
Roberto Raggi's avatar
Roberto Raggi committed
277
                                                                      localFileName.size());
con's avatar
con committed
278
279
    _translationUnit = new TranslationUnit(_control, fileId);
    _translationUnit->setQtMocRunEnabled(true);
Roberto Raggi's avatar
Roberto Raggi committed
280
    _translationUnit->setCxxOxEnabled(true);
Roberto Raggi's avatar
Roberto Raggi committed
281
    _translationUnit->setObjCEnabled(true);
con's avatar
con committed
282
283
284
285
286
287
288
289
290
291
292
    (void) _control->switchTranslationUnit(_translationUnit);
}

Document::~Document()
{
    delete _translationUnit;
    delete _control->diagnosticClient();
    delete _control;
}

Control *Document::control() const
hjk's avatar
hjk committed
293
294
295
{
    return _control;
}
con's avatar
con committed
296

297
298
299
300
301
302
303
304
305
306
unsigned Document::revision() const
{
    return _revision;
}

void Document::setRevision(unsigned revision)
{
    _revision = revision;
}

307
308
309
310
311
312
313
314
315
316
unsigned Document::editorRevision() const
{
    return _editorRevision;
}

void Document::setEditorRevision(unsigned editorRevision)
{
    _editorRevision = editorRevision;
}

317
318
319
320
321
322
323
324
325
326
QDateTime Document::lastModified() const
{
    return _lastModified;
}

void Document::setLastModified(const QDateTime &lastModified)
{
    _lastModified = lastModified;
}

con's avatar
con committed
327
QString Document::fileName() const
hjk's avatar
hjk committed
328
329
330
{
    return _fileName;
}
con's avatar
con committed
331
332

QStringList Document::includedFiles() const
hjk's avatar
hjk committed
333
{
334
335
336
337
338
    QStringList files;
    foreach (const Include &i, _includes)
        files.append(i.fileName());
    files.removeDuplicates();
    return files;
hjk's avatar
hjk committed
339
}
con's avatar
con committed
340

341
// This assumes to be called with a QDir::cleanPath cleaned fileName.
342
void Document::addIncludeFile(const QString &fileName, unsigned line)
hjk's avatar
hjk committed
343
{
344
    _includes.append(Include(fileName, line));
hjk's avatar
hjk committed
345
}
con's avatar
con committed
346

Roberto Raggi's avatar
Roberto Raggi committed
347
void Document::appendMacro(const Macro &macro)
hjk's avatar
hjk committed
348
{
Roberto Raggi's avatar
Roberto Raggi committed
349
    _definedMacros.append(macro);
con's avatar
con committed
350
351
}

352
void Document::addMacroUse(const Macro &macro, unsigned offset, unsigned length,
353
                           unsigned beginLine,
354
                           const QVector<MacroArgumentReference> &actuals)
355
{
356
    MacroUse use(macro, offset, offset + length, beginLine);
357
358
359
360
361
362
363
364

    foreach (const MacroArgumentReference &actual, actuals) {
        const Block arg(actual.position(), actual.position() + actual.length());

        use.addArgument(arg);
    }

    _macroUses.append(use);
365
366
}

Christian Kamm's avatar
Christian Kamm committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
void Document::addUndefinedMacroUse(const QByteArray &name, unsigned offset)
{
    QByteArray copy(name.data(), name.size());
    UndefinedMacroUse use(copy, offset);
    _undefinedMacroUses.append(use);
}

/*!
    \class Document::MacroUse
    \brief Represents the usage of a macro in a \l {Document}.
    \sa Document::UndefinedMacroUse
*/

/*!
    \class Document::UndefinedMacroUse
    \brief Represents a macro that was looked up, but not found.

    Holds data about the reference to a macro in an \tt{#ifdef} or \tt{#ifndef}
    or argument to the \tt{defined} operator inside an \tt{#if} or \tt{#elif} that does
    not exist.

    \sa Document::undefinedMacroUses(), Document::MacroUse, Macro
*/

/*!
    \fn QByteArray Document::UndefinedMacroUse::name() const

    Returns the name of the macro that was not found.
*/

/*!
    \fn QList<UndefinedMacroUse> Document::undefinedMacroUses() const

    Returns a list of referenced but undefined macros.

    \sa Document::macroUses(), Document::definedMacros(), Macro
*/

/*!
    \fn QList<MacroUse> Document::macroUses() const

    Returns a list of macros used.

    \sa Document::undefinedMacroUses(), Document::definedMacros(), Macro
*/

/*!
    \fn QList<Macro> Document::definedMacros() const

    Returns the list of macros defined.

    \sa Document::macroUses(), Document::undefinedMacroUses()
*/

con's avatar
con committed
421
TranslationUnit *Document::translationUnit() const
hjk's avatar
hjk committed
422
423
424
{
    return _translationUnit;
}
con's avatar
con committed
425
426

bool Document::skipFunctionBody() const
hjk's avatar
hjk committed
427
428
429
{
    return _translationUnit->skipFunctionBody();
}
con's avatar
con committed
430
431

void Document::setSkipFunctionBody(bool skipFunctionBody)
hjk's avatar
hjk committed
432
433
434
{
    _translationUnit->setSkipFunctionBody(skipFunctionBody);
}
con's avatar
con committed
435
436
437
438
439
440
441
442
443
444

unsigned Document::globalSymbolCount() const
{
    if (! _globalNamespace)
        return 0;

    return _globalNamespace->memberCount();
}

Symbol *Document::globalSymbolAt(unsigned index) const
hjk's avatar
hjk committed
445
446
447
{
    return _globalNamespace->memberAt(index);
}
con's avatar
con committed
448
449

Namespace *Document::globalNamespace() const
hjk's avatar
hjk committed
450
451
452
{
    return _globalNamespace;
}
con's avatar
con committed
453

454
455
456
457
458
void Document::setGlobalNamespace(Namespace *globalNamespace)
{
    _globalNamespace = globalNamespace;
}

459
460
461
Scope *Document::scopeAt(unsigned line, unsigned column)
{
    FindScopeAt findScopeAt(_translationUnit, line, column);
462
463
    if (Scope *scope = findScopeAt(_globalNamespace))
        return scope;
Roberto Raggi's avatar
Roberto Raggi committed
464
    return globalNamespace();
465
466
}

467
Symbol *Document::lastVisibleSymbolAt(unsigned line, unsigned column) const
hjk's avatar
hjk committed
468
{
469
470
    LastVisibleSymbolAt lastVisibleSymbolAt(globalNamespace());
    return lastVisibleSymbolAt(line, column);
con's avatar
con committed
471
472
}

473
474
475
476
477
478
479
480
481
482
483
484
const Macro *Document::findMacroDefinitionAt(unsigned line) const
{
    foreach (const Macro &macro, _definedMacros) {
        if (macro.line() == line)
            return &macro;
    }
    return 0;
}

const Document::MacroUse *Document::findMacroUseAt(unsigned offset) const
{
    foreach (const Document::MacroUse &use, _macroUses) {
485
        if (use.contains(offset) && (offset < use.begin() + use.macro().name().length()))
486
487
488
489
490
491
492
493
            return &use;
    }
    return 0;
}

const Document::UndefinedMacroUse *Document::findUndefinedMacroUseAt(unsigned offset) const
{
    foreach (const Document::UndefinedMacroUse &use, _undefinedMacroUses) {
494
        if (use.contains(offset) && (offset < use.begin() + use.name().length()))
495
496
497
498
499
            return &use;
    }
    return 0;
}

con's avatar
con committed
500
501
502
503
504
505
Document::Ptr Document::create(const QString &fileName)
{
    Document::Ptr doc(new Document(fileName));
    return doc;
}

506
QByteArray Document::utf8Source() const
Roberto Raggi's avatar
Roberto Raggi committed
507
508
{ return _source; }

509
void Document::setUtf8Source(const QByteArray &source)
hjk's avatar
hjk committed
510
{
511
512
    _source = source;
    _translationUnit->setSource(_source.constBegin(), _source.size());
hjk's avatar
hjk committed
513
}
con's avatar
con committed
514
515

void Document::startSkippingBlocks(unsigned start)
hjk's avatar
hjk committed
516
517
518
{
    _skippedBlocks.append(Block(start, 0));
}
con's avatar
con committed
519
520
521

void Document::stopSkippingBlocks(unsigned stop)
{
Roberto Raggi's avatar
Roberto Raggi committed
522
523
524
    if (_skippedBlocks.isEmpty())
        return;

con's avatar
con committed
525
526
527
528
529
530
531
    unsigned start = _skippedBlocks.back().begin();
    if (start > stop)
        _skippedBlocks.removeLast(); // Ignore this block, it's invalid.
    else
        _skippedBlocks.back() = Block(start, stop);
}

532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
bool Document::isTokenized() const
{
    return _translationUnit->isTokenized();
}

void Document::tokenize()
{
    _translationUnit->tokenize();
}

bool Document::isParsed() const
{
    return _translationUnit->isParsed();
}

547
bool Document::parse(ParseMode mode)
hjk's avatar
hjk committed
548
{
549
550
551
552
553
554
555
556
557
558
559
560
561
562
    TranslationUnit::ParseMode m = TranslationUnit::ParseTranlationUnit;
    switch (mode) {
    case ParseTranlationUnit:
        m = TranslationUnit::ParseTranlationUnit;
        break;

    case ParseDeclaration:
        m = TranslationUnit::ParseDeclaration;
        break;

    case ParseExpression:
        m = TranslationUnit::ParseExpression;
        break;

563
564
565
566
    case ParseDeclarator:
        m = TranslationUnit::ParseDeclarator;
        break;

567
568
569
570
571
572
573
574
575
    case ParseStatement:
        m = TranslationUnit::ParseStatement;
        break;

    default:
        break;
    }

    return _translationUnit->parse(m);
hjk's avatar
hjk committed
576
}
con's avatar
con committed
577

578
void Document::check(CheckMode mode)
con's avatar
con committed
579
{
580
    Q_ASSERT(!_globalNamespace);
con's avatar
con committed
581

582
583
    _checkMode = mode;

584
585
586
    if (! isParsed())
        parse();

Roberto Raggi's avatar
Roberto Raggi committed
587
588
    _globalNamespace = _control->newNamespace(0);
    Bind semantic(_translationUnit);
589
    if (mode == FastCheck)
590
        semantic.setSkipFunctionBodies(true);
con's avatar
con committed
591

592
593
594
    if (! _translationUnit->ast())
        return; // nothing to do.

595
    if (TranslationUnitAST *ast = _translationUnit->ast()->asTranslationUnit())
Roberto Raggi's avatar
Roberto Raggi committed
596
        semantic(ast, _globalNamespace);
597
598
    else if (StatementAST *ast = _translationUnit->ast()->asStatement())
        semantic(ast, _globalNamespace);
599
    else if (ExpressionAST *ast = _translationUnit->ast()->asExpression())
Roberto Raggi's avatar
Roberto Raggi committed
600
        semantic(ast, _globalNamespace);
601
    else if (DeclarationAST *ast = translationUnit()->ast()->asDeclaration())
602
        semantic(ast, _globalNamespace);
con's avatar
con committed
603
604
}

605
void Document::keepSourceAndAST()
606
{
607
    _keepSourceAndASTCount.ref();
608
609
}

610
void Document::releaseSourceAndAST()
hjk's avatar
hjk committed
611
{
612
613
614
    if (!_keepSourceAndASTCount.deref()) {
        _source.clear();
        _translationUnit->release();
615
        _control->squeeze();
616
    }
hjk's avatar
hjk committed
617
}
618

619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
bool Document::DiagnosticMessage::operator==(const Document::DiagnosticMessage &other) const
{
    return
            _line == other._line &&
            _column == other._column &&
            _length == other._length &&
            _level == other._level &&
            _fileName == other._fileName &&
            _text == other._text;
}

bool Document::DiagnosticMessage::operator!=(const Document::DiagnosticMessage &other) const
{
    return !operator==(other);
}

635
636
637
638
639
640
641
642
Snapshot::Snapshot()
{
}

Snapshot::~Snapshot()
{
}

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
int Snapshot::size() const
{
    return _documents.size();
}

bool Snapshot::isEmpty() const
{
    return _documents.isEmpty();
}

Snapshot::const_iterator Snapshot::find(const QString &fileName) const
{
    return _documents.find(fileName);
}

void Snapshot::remove(const QString &fileName)
{
    _documents.remove(fileName);
}

bool Snapshot::contains(const QString &fileName) const
{
    return _documents.contains(fileName);
}

668
669
670
void Snapshot::insert(Document::Ptr doc)
{
    if (doc)
671
        _documents.insert(doc->fileName(), doc);
672
673
}

674
Document::Ptr Snapshot::preprocessedDocument(const QString &source, const QString &fileName) const
675
{
676
677
678
679
680
    Document::Ptr newDoc = Document::create(fileName);
    if (Document::Ptr thisDocument = document(fileName)) {
        newDoc->_revision = thisDocument->_revision;
        newDoc->_editorRevision = thisDocument->_editorRevision;
        newDoc->_lastModified = thisDocument->_lastModified;
681
        newDoc->_includes = thisDocument->_includes;
682
683
    }

684
    FastPreprocessor pp(*this);
685
686
687
    const QByteArray preprocessedCode = pp.run(newDoc, source);
    newDoc->setUtf8Source(preprocessedCode);
    return newDoc;
688
689
690
691
692
}

Document::Ptr Snapshot::documentFromSource(const QByteArray &preprocessedCode,
                                           const QString &fileName) const
{
Roberto Raggi's avatar
Roberto Raggi committed
693
    Document::Ptr newDoc = Document::create(fileName);
694

695
    if (Document::Ptr thisDocument = document(fileName)) {
696
        newDoc->_revision = thisDocument->_revision;
697
        newDoc->_editorRevision = thisDocument->_editorRevision;
698
        newDoc->_lastModified = thisDocument->_lastModified;
699
700
        newDoc->_includes = thisDocument->_includes;
        newDoc->_definedMacros = thisDocument->_definedMacros;
701
        newDoc->_macroUses = thisDocument->_macroUses;
702
703
    }

704
    newDoc->setUtf8Source(preprocessedCode);
Roberto Raggi's avatar
Roberto Raggi committed
705
    return newDoc;
706
707
}

708
QSet<QString> Snapshot::allIncludesForDocument(const QString &fileName) const
709
{
710
711
712
    QSet<QString> result;
    allIncludesForDocument_helper(fileName, result);
    return result;
713
714
}

715
void Snapshot::allIncludesForDocument_helper(const QString &fileName, QSet<QString> &result) const
716
{
717
718
719
720
721
722
723
724
    if (Document::Ptr doc = document(fileName)) {
        foreach (const QString &inc, doc->includedFiles()) {
            if (!result.contains(inc)) {
                result.insert(inc);
                allIncludesForDocument_helper(inc, result);
            }
        }
    }
725
726
}

727
Document::Ptr Snapshot::document(const QString &fileName) const
728
{
729
730
    return _documents.value(fileName);
}
731

732
733
734
Snapshot Snapshot::simplified(Document::Ptr doc) const
{
    Snapshot snapshot;
735

736
737
738
739
740
    if (doc) {
        snapshot.insert(doc);
        foreach (const QString &fileName, allIncludesForDocument(doc->fileName()))
            if (Document::Ptr inc = document(fileName))
                snapshot.insert(inc);
741
    }
742

743
744
    return snapshot;
}