CppDocument.cpp 13.6 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
con's avatar
con committed
29
30

#include "CppDocument.h"
31
32
#include "CppBindings.h"
#include "FastPreprocessor.h"
hjk's avatar
hjk committed
33

con's avatar
con committed
34
35
36
37
38
39
40
41
#include <Control.h>
#include <TranslationUnit.h>
#include <DiagnosticClient.h>
#include <Semantic.h>
#include <Literals.h>
#include <Symbols.h>
#include <AST.h>
#include <Scope.h>
hjk's avatar
hjk committed
42

Roberto Raggi's avatar
Roberto Raggi committed
43
44
45
#include <QtCore/QByteArray>
#include <QtCore/QBitArray>
#include <QtCore/QtDebug>
con's avatar
con committed
46

Christian Kamm's avatar
Christian Kamm committed
47
48
49
50
51
/*!
    \namespace CPlusPlus
    The namespace for C++ related tools.
*/

con's avatar
con committed
52
53
54
55
using namespace CPlusPlus;

namespace {

hjk's avatar
hjk committed
56
57
58
59
60
61
62
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
63
64
          messages(messages),
          errorCount(0)
hjk's avatar
hjk committed
65
66
67
68
69
70
71
    { }

    virtual void report(int level,
                        StringLiteral *fileId,
                        unsigned line, unsigned column,
                        const char *format, va_list ap)
    {
Roberto Raggi's avatar
Roberto Raggi committed
72
73
74
75
76
77
        if (level == Error) {
            ++errorCount;

            if (errorCount >= MAX_MESSAGE_COUNT)
                return; // ignore the error
        }
con's avatar
con committed
78

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

hjk's avatar
hjk committed
81
82
        if (fileName != doc->fileName())
            return;
con's avatar
con committed
83

hjk's avatar
hjk committed
84
85
        QString message;
        message.vsprintf(format, ap);
con's avatar
con committed
86

hjk's avatar
hjk committed
87
88
89
90
        Document::DiagnosticMessage m(convertLevel(level), doc->fileName(),
                                      line, column, message);
        messages->append(m);
    }
con's avatar
con committed
91

hjk's avatar
hjk committed
92
93
94
95
96
97
    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
98
        }
hjk's avatar
hjk committed
99
    }
con's avatar
con committed
100

Roberto Raggi's avatar
Roberto Raggi committed
101
private:
hjk's avatar
hjk committed
102
103
    Document *doc;
    QList<Document::DiagnosticMessage> *messages;
Roberto Raggi's avatar
Roberto Raggi committed
104
    int errorCount;
hjk's avatar
hjk committed
105
};
con's avatar
con committed
106

hjk's avatar
hjk committed
107
} // anonymous namespace
con's avatar
con committed
108

Christian Kamm's avatar
Christian Kamm committed
109

con's avatar
con committed
110
111
Document::Document(const QString &fileName)
    : _fileName(fileName),
112
113
      _globalNamespace(0),
      _revision(0)
con's avatar
con committed
114
115
116
117
118
119
{
    _control = new Control();

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

    const QByteArray localFileName = fileName.toUtf8();
Roberto Raggi's avatar
Roberto Raggi committed
120
121
    StringLiteral *fileId = _control->findOrInsertStringLiteral(localFileName.constData(),
                                                                localFileName.size());
con's avatar
con committed
122
123
    _translationUnit = new TranslationUnit(_control, fileId);
    _translationUnit->setQtMocRunEnabled(true);
Roberto Raggi's avatar
Roberto Raggi committed
124
    _translationUnit->setObjCEnabled(true);
con's avatar
con committed
125
126
127
128
129
130
131
132
133
134
135
    (void) _control->switchTranslationUnit(_translationUnit);
}

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

Control *Document::control() const
hjk's avatar
hjk committed
136
137
138
{
    return _control;
}
con's avatar
con committed
139

140
141
142
143
144
145
146
147
148
149
unsigned Document::revision() const
{
    return _revision;
}

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

con's avatar
con committed
150
QString Document::fileName() const
hjk's avatar
hjk committed
151
152
153
{
    return _fileName;
}
con's avatar
con committed
154
155

QStringList Document::includedFiles() const
hjk's avatar
hjk committed
156
{
157
158
159
160
161
    QStringList files;
    foreach (const Include &i, _includes)
        files.append(i.fileName());
    files.removeDuplicates();
    return files;
hjk's avatar
hjk committed
162
}
con's avatar
con committed
163

164
void Document::addIncludeFile(const QString &fileName, unsigned line)
hjk's avatar
hjk committed
165
{
166
    _includes.append(Include(fileName, line));
hjk's avatar
hjk committed
167
}
con's avatar
con committed
168

Roberto Raggi's avatar
Roberto Raggi committed
169
void Document::appendMacro(const Macro &macro)
hjk's avatar
hjk committed
170
{
Roberto Raggi's avatar
Roberto Raggi committed
171
    _definedMacros.append(macro);
con's avatar
con committed
172
173
}

174
void Document::addMacroUse(const Macro &macro, unsigned offset, unsigned length,
Christian Kamm's avatar
Christian Kamm committed
175
                           const QVector<MacroArgumentReference> &actuals, bool inCondition)
176
{
177
    MacroUse use(macro, offset, offset + length);
Christian Kamm's avatar
Christian Kamm committed
178
    use.setInCondition(inCondition);
179
180
181
182
183
184
185
186

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

        use.addArgument(arg);
    }

    _macroUses.append(use);
187
188
}

Christian Kamm's avatar
Christian Kamm committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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
243
TranslationUnit *Document::translationUnit() const
hjk's avatar
hjk committed
244
245
246
{
    return _translationUnit;
}
con's avatar
con committed
247
248

bool Document::skipFunctionBody() const
hjk's avatar
hjk committed
249
250
251
{
    return _translationUnit->skipFunctionBody();
}
con's avatar
con committed
252
253

void Document::setSkipFunctionBody(bool skipFunctionBody)
hjk's avatar
hjk committed
254
255
256
{
    _translationUnit->setSkipFunctionBody(skipFunctionBody);
}
con's avatar
con committed
257
258
259
260
261
262
263
264
265
266

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

    return _globalNamespace->memberCount();
}

Symbol *Document::globalSymbolAt(unsigned index) const
hjk's avatar
hjk committed
267
268
269
{
    return _globalNamespace->memberAt(index);
}
con's avatar
con committed
270
271
272
273
274
275
276
277
278
279

Scope *Document::globalSymbols() const
{
    if (! _globalNamespace)
        return 0;

    return _globalNamespace->members();
}

Namespace *Document::globalNamespace() const
hjk's avatar
hjk committed
280
281
282
{
    return _globalNamespace;
}
con's avatar
con committed
283
284

Symbol *Document::findSymbolAt(unsigned line, unsigned column) const
hjk's avatar
hjk committed
285
286
287
{
    return findSymbolAt(line, column, globalSymbols());
}
con's avatar
con committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

Symbol *Document::findSymbolAt(unsigned line, unsigned column, Scope *scope) const
{
    Symbol *previousSymbol = 0;

    for (unsigned i = 0; i < scope->symbolCount(); ++i) {
        Symbol *symbol = scope->symbolAt(i);
        if (symbol->line() > line)
            break;

        previousSymbol = symbol;
    }

    if (previousSymbol) {
        if (ScopedSymbol *scoped = previousSymbol->asScopedSymbol()) {
            if (Symbol *member = findSymbolAt(line, column, scoped->members()))
                return member;
        }
    }

    return previousSymbol;
}

Document::Ptr Document::create(const QString &fileName)
{
    Document::Ptr doc(new Document(fileName));
    return doc;
}

Roberto Raggi's avatar
Roberto Raggi committed
317
318
319
QByteArray Document::source() const
{ return _source; }

con's avatar
con committed
320
void Document::setSource(const QByteArray &source)
hjk's avatar
hjk committed
321
{
322
323
    _source = source;
    _translationUnit->setSource(_source.constBegin(), _source.size());
hjk's avatar
hjk committed
324
}
con's avatar
con committed
325
326

void Document::startSkippingBlocks(unsigned start)
hjk's avatar
hjk committed
327
328
329
{
    _skippedBlocks.append(Block(start, 0));
}
con's avatar
con committed
330
331
332

void Document::stopSkippingBlocks(unsigned stop)
{
Roberto Raggi's avatar
Roberto Raggi committed
333
334
335
    if (_skippedBlocks.isEmpty())
        return;

con's avatar
con committed
336
337
338
339
340
341
342
    unsigned start = _skippedBlocks.back().begin();
    if (start > stop)
        _skippedBlocks.removeLast(); // Ignore this block, it's invalid.
    else
        _skippedBlocks.back() = Block(start, stop);
}

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
bool Document::isTokenized() const
{
    return _translationUnit->isTokenized();
}

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

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

358
bool Document::parse(ParseMode mode)
hjk's avatar
hjk committed
359
{
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    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;

374
375
376
377
    case ParseDeclarator:
        m = TranslationUnit::ParseDeclarator;
        break;

378
379
380
381
382
383
384
385
386
    case ParseStatement:
        m = TranslationUnit::ParseStatement;
        break;

    default:
        break;
    }

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

389
void Document::check(CheckMode mode)
con's avatar
con committed
390
{
391
    Q_ASSERT(!_globalNamespace);
con's avatar
con committed
392

393
394
395
    if (! isParsed())
        parse();

con's avatar
con committed
396
    Semantic semantic(_control);
397
398
    if (mode == FastCheck)
        semantic.setSkipFunctionBodies(true);
con's avatar
con committed
399
400
401

    _globalNamespace = _control->newNamespace(0);
    Scope *globals = _globalNamespace->members();
402
403
404
405
    if (! _translationUnit->ast())
        return; // nothing to do.

    if (TranslationUnitAST *ast = _translationUnit->ast()->asTranslationUnit()) {
Roberto Raggi's avatar
Roberto Raggi committed
406
407
        for (DeclarationListAST *decl = ast->declarations; decl; decl = decl->next) {
            semantic.check(decl->declaration, globals);
con's avatar
con committed
408
        }
409
410
    } else if (ExpressionAST *ast = _translationUnit->ast()->asExpression()) {
        semantic.check(ast, globals);
con's avatar
con committed
411
412
413
    }
}

414
415
416
417
418
void Document::releaseSource()
{
    _source.clear();
}

con's avatar
con committed
419
void Document::releaseTranslationUnit()
hjk's avatar
hjk committed
420
421
422
{
    _translationUnit->release();
}
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437

Snapshot::Snapshot()
{
}

Snapshot::~Snapshot()
{
}

void Snapshot::insert(Document::Ptr doc)
{
    if (doc)
        insert(doc->fileName(), doc);
}

438
QByteArray Snapshot::preprocessedCode(const QString &source, const QString &fileName) const
439
440
441
442
443
444
445
446
{
    FastPreprocessor pp(*this);
    return pp.run(fileName, source);
}

Document::Ptr Snapshot::documentFromSource(const QByteArray &preprocessedCode,
                                           const QString &fileName) const
{
Roberto Raggi's avatar
Roberto Raggi committed
447
448
    FastPreprocessor pp(*this);
    Document::Ptr newDoc = Document::create(fileName);
449

Roberto Raggi's avatar
Roberto Raggi committed
450
    if (Document::Ptr thisDocument = value(fileName)) {
451
452
453
454
        newDoc->_includes = thisDocument->_includes;
        newDoc->_definedMacros = thisDocument->_definedMacros;
    }

Roberto Raggi's avatar
Roberto Raggi committed
455
456
    newDoc->setSource(preprocessedCode);
    return newDoc;
457
458
459
460
461
462
}

QSharedPointer<NamespaceBinding> Snapshot::globalNamespaceBinding(Document::Ptr doc) const
{
    return CPlusPlus::bind(doc, *this);
}
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

Snapshot Snapshot::simplified(Document::Ptr doc) const
{
    Snapshot snapshot;
    simplified_helper(doc, &snapshot);
    return snapshot;
}

void Snapshot::simplified_helper(Document::Ptr doc, Snapshot *snapshot) const
{
    if (! doc)
        return;

    if (! snapshot->contains(doc->fileName())) {
        snapshot->insert(doc);

        foreach (const Document::Include &incl, doc->includes()) {
            Document::Ptr includedDoc = value(incl.fileName());
            simplified_helper(includedDoc, snapshot);
        }
    }
}
Roberto Raggi's avatar
Roberto Raggi committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559

QStringList Snapshot::dependsOn(const QString &fileName) const
{
    const int n = size();

    QVector<QString> files(n);
    QHash<QString, int> fileIndex;
    QHash<int, QList<int> > includes;

    QMapIterator<QString, Document::Ptr> it(*this);
    for (int i = 0; it.hasNext(); ++i) {
        it.next();
        files[i] = it.key();
        fileIndex[it.key()] = i;
    }

    int index = fileIndex.value(fileName, -1);
    if (index == -1) {
        qWarning() << fileName << "not in the snapshot";
        return QStringList();
    }

    QVector<QBitArray> includeMap(files.size());

    for (int i = 0; i < files.size(); ++i) {
        if (Document::Ptr doc = value(files.at(i))) {
            QBitArray bitmap(files.size());
            QList<int> directIncludes;

            foreach (const QString &includedFile, doc->includedFiles()) {
                int index = fileIndex.value(includedFile);

                if (index == -1)
                    continue;
                else if (! directIncludes.contains(index))
                    directIncludes.append(index);

                bitmap.setBit(index, true);
            }

            includeMap[i] = bitmap;
            includes[i] = directIncludes;
        }
    }

    bool changed;

    do {
        changed = false;

        for (int i = 0; i < files.size(); ++i) {
            QBitArray bitmap = includeMap.value(i);
            QBitArray previousBitmap = bitmap;

            foreach (int includedFileIndex, includes.value(i)) {
                bitmap |= includeMap.value(includedFileIndex);
            }

            if (bitmap != previousBitmap) {
                includeMap[i] = bitmap;
                changed = true;
            }
        }
    } while (changed);

    QStringList deps;
    for (int i = 0; i < files.size(); ++i) {
        const QBitArray &bits = includeMap.at(i);

        if (bits.testBit(index))
            deps.append(files.at(i));
    }

    return deps;
}