TranslationUnit.cpp 14.8 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
con's avatar
con committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "TranslationUnit.h"
#include "Control.h"
#include "Parser.h"
#include "Lexer.h"
#include "MemoryPool.h"
#include "AST.h"
#include "Literals.h"
#include "DiagnosticClient.h"
#include <stack>
#include <cstdarg>
#include <algorithm>

Tobias Hunger's avatar
Tobias Hunger committed
64
#ifdef _MSC_VER
Tobias Hunger's avatar
Tobias Hunger committed
65
#    define va_copy(dst, src) ((dst) = (src))
66
67
#elif defined(__INTEL_COMPILER) && !defined(va_copy)
#    define va_copy __va_copy
Tobias Hunger's avatar
Tobias Hunger committed
68
69
#endif

Roberto Raggi's avatar
Roberto Raggi committed
70
using namespace CPlusPlus;
con's avatar
con committed
71

Roberto Raggi's avatar
Roberto Raggi committed
72
TranslationUnit::TranslationUnit(Control *control, const StringLiteral *fileId)
con's avatar
con committed
73
74
75
76
77
78
79
80
    : _control(control),
      _fileId(fileId),
      _firstSourceChar(0),
      _lastSourceChar(0),
      _pool(0),
      _ast(0),
      _flags(0)
{
81
    _tokens = new std::vector<Token>();
con's avatar
con committed
82
83
84
85
86
87
88
89
90
91
92
93
    _previousTranslationUnit = control->switchTranslationUnit(this);
    _pool = new MemoryPool();
}

TranslationUnit::~TranslationUnit()
{
    (void) _control->switchTranslationUnit(_previousTranslationUnit);
    delete _tokens;
    delete _pool;
}

bool TranslationUnit::qtMocRunEnabled() const
94
{ return f._qtMocRunEnabled; }
con's avatar
con committed
95
96

void TranslationUnit::setQtMocRunEnabled(bool onoff)
97
{ f._qtMocRunEnabled = onoff; }
con's avatar
con committed
98

99
100
101
102
103
104
bool TranslationUnit::cxx0xEnabled() const
{ return f._cxx0xEnabled; }

void TranslationUnit::setCxxOxEnabled(bool onoff)
{ f._cxx0xEnabled = onoff; }

Roberto Raggi's avatar
Roberto Raggi committed
105
bool TranslationUnit::objCEnabled() const
106
{ return f._objCEnabled; }
Roberto Raggi's avatar
Roberto Raggi committed
107
108

void TranslationUnit::setObjCEnabled(bool onoff)
109
{ f._objCEnabled = onoff; }
Roberto Raggi's avatar
Roberto Raggi committed
110

con's avatar
con committed
111
112
113
Control *TranslationUnit::control() const
{ return _control; }

Roberto Raggi's avatar
Roberto Raggi committed
114
const StringLiteral *TranslationUnit::fileId() const
con's avatar
con committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
{ return _fileId; }

const char *TranslationUnit::fileName() const
{ return _fileId->chars(); }

unsigned TranslationUnit::fileNameLength() const
{ return _fileId->size(); }

const char *TranslationUnit::firstSourceChar() const
{ return _firstSourceChar; }

const char *TranslationUnit::lastSourceChar() const
{ return _lastSourceChar; }

unsigned TranslationUnit::sourceLength() const
{ return _lastSourceChar - _firstSourceChar; }

void TranslationUnit::setSource(const char *source, unsigned size)
{
    _firstSourceChar = source;
    _lastSourceChar = source + size;
}

unsigned TranslationUnit::tokenCount() const
{ return _tokens->size(); }

const Token &TranslationUnit::tokenAt(unsigned index) const
{ return _tokens->at(index); }

int TranslationUnit::tokenKind(unsigned index) const
145
{ return _tokens->at(index).f.kind; }
con's avatar
con committed
146

147
148
149
150
151
152
153
154
const char *TranslationUnit::spell(unsigned index) const
{
    if (! index)
        return 0;

    return _tokens->at(index).spell();
}

Roberto Raggi's avatar
Roberto Raggi committed
155
const Identifier *TranslationUnit::identifier(unsigned index) const
con's avatar
con committed
156
157
{ return _tokens->at(index).identifier; }

Roberto Raggi's avatar
Roberto Raggi committed
158
const Literal *TranslationUnit::literal(unsigned index) const
con's avatar
con committed
159
160
{ return _tokens->at(index).literal; }

Roberto Raggi's avatar
Roberto Raggi committed
161
const StringLiteral *TranslationUnit::stringLiteral(unsigned index) const
con's avatar
con committed
162
163
{ return _tokens->at(index).string; }

Roberto Raggi's avatar
Roberto Raggi committed
164
const NumericLiteral *TranslationUnit::numericLiteral(unsigned index) const
con's avatar
con committed
165
166
167
168
169
170
171
172
{ return _tokens->at(index).number; }

unsigned TranslationUnit::matchingBrace(unsigned index) const
{ return _tokens->at(index).close_brace; }

MemoryPool *TranslationUnit::memoryPool() const
{ return _pool; }

173
AST *TranslationUnit::ast() const
con's avatar
con committed
174
175
176
{ return _ast; }

bool TranslationUnit::isTokenized() const
177
{ return f._tokenized; }
con's avatar
con committed
178
179

bool TranslationUnit::isParsed() const
180
{ return f._parsed; }
con's avatar
con committed
181
182
183
184
185
186

void TranslationUnit::tokenize()
{
    if (isTokenized())
        return;

187
    f._tokenized = true;
con's avatar
con committed
188
189

    Lexer lex(this);
190
    lex.setQtMocRunEnabled(f._qtMocRunEnabled);
191
    lex.setCxxOxEnabled(f._cxx0xEnabled);
192
    lex.setObjCEnabled(f._objCEnabled);
con's avatar
con committed
193
194
195
196
197
198
199

    std::stack<unsigned> braces;
    _tokens->push_back(Token()); // the first token needs to be invalid!

    pushLineOffset(0);
    pushPreprocessorLine(0, 1, fileId());

200
201
    const Identifier *lineId   = control()->identifier("line");
    const Identifier *genId    = control()->identifier("gen");
con's avatar
con committed
202

203
    bool generated = false;
con's avatar
con committed
204
205
206
207
208
    Token tk;
    do {
        lex(&tk);

      _Lrecognize:
209
        if (tk.is(T_POUND) && tk.newline()) {
con's avatar
con committed
210
211
            unsigned offset = tk.offset;
            lex(&tk);
212

213
            if (! tk.f.newline && tk.is(T_IDENTIFIER) && tk.identifier == genId) {
214
                // it's a gen directive.
con's avatar
con committed
215
                lex(&tk);
216

217
                if (! tk.f.newline && tk.is(T_TRUE)) {
218
219
220
221
222
223
                    lex(&tk);
                    generated = true;
                } else {
                    generated = false;
                }
            } else {
224
                if (! tk.f.newline && tk.is(T_IDENTIFIER) && tk.identifier == lineId)
225
                    lex(&tk);
226
                if (! tk.f.newline && tk.is(T_NUMERIC_LITERAL)) {
227
                    unsigned line = (unsigned) strtoul(tk.spell(), 0, 0);
con's avatar
con committed
228
                    lex(&tk);
229
                    if (! tk.f.newline && tk.is(T_STRING_LITERAL)) {
230
                        const StringLiteral *fileName = control()->stringLiteral(tk.string->chars(),
Roberto Raggi's avatar
Roberto Raggi committed
231
                                                                                             tk.string->size());
232
233
234
                        pushPreprocessorLine(offset, line, fileName);
                        lex(&tk);
                    }
con's avatar
con committed
235
236
                }
            }
237
            while (tk.isNot(T_EOF_SYMBOL) && ! tk.f.newline)
con's avatar
con committed
238
239
                lex(&tk);
            goto _Lrecognize;
240
        } else if (tk.f.kind == T_LBRACE) {
con's avatar
con committed
241
            braces.push(_tokens->size());
242
        } else if (tk.f.kind == T_RBRACE && ! braces.empty()) {
con's avatar
con committed
243
244
245
246
            const unsigned open_brace_index = braces.top();
            braces.pop();
            (*_tokens)[open_brace_index].close_brace = _tokens->size();
        }
247
        tk.f.generated = generated;
con's avatar
con committed
248
        _tokens->push_back(tk);
249
    } while (tk.f.kind);
con's avatar
con committed
250
251
252
253
254
255
256
257

    for (; ! braces.empty(); braces.pop()) {
        unsigned open_brace_index = braces.top();
        (*_tokens)[open_brace_index].close_brace = _tokens->size();
    }
}

bool TranslationUnit::skipFunctionBody() const
258
{ return f._skipFunctionBody; }
con's avatar
con committed
259
260

void TranslationUnit::setSkipFunctionBody(bool skipFunctionBody)
261
{ f._skipFunctionBody = skipFunctionBody; }
con's avatar
con committed
262

263
bool TranslationUnit::parse(ParseMode mode)
con's avatar
con committed
264
265
{
    if (isParsed())
266
        return false;
con's avatar
con committed
267
268
269
270

    if (! isTokenized())
        tokenize();

271
272
    f._parsed = true;

con's avatar
con committed
273
    Parser parser(this);
274
    parser.setQtMocRunEnabled(f._qtMocRunEnabled);
275
    parser.setCxxOxEnabled(f._cxx0xEnabled);
276
    parser.setObjCEnabled(f._objCEnabled);
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298

    bool parsed = false;

    switch (mode) {
    case ParseTranlationUnit: {
        TranslationUnitAST *node = 0;
        parsed = parser.parseTranslationUnit(node);
        _ast = node;
    } break;

    case ParseDeclaration: {
        DeclarationAST *node = 0;
        parsed = parser.parseDeclaration(node);
        _ast = node;
    } break;

    case ParseExpression: {
        ExpressionAST *node = 0;
        parsed = parser.parseExpression(node);
        _ast = node;
    } break;

299
300
    case ParseDeclarator: {
        DeclaratorAST *node = 0;
301
        parsed = parser.parseDeclarator(node, /*decl_specifier_list =*/ 0);
302
303
304
        _ast = node;
    } break;

305
306
307
308
309
310
311
312
313
314
315
    case ParseStatement: {
        StatementAST *node = 0;
        parsed = parser.parseStatement(node);
        _ast = node;
    } break;

    default:
        break;
    } // switch

    return parsed;
con's avatar
con committed
316
317
318
319
320
321
322
}

void TranslationUnit::pushLineOffset(unsigned offset)
{ _lineOffsets.push_back(offset); }

void TranslationUnit::pushPreprocessorLine(unsigned offset,
                                           unsigned line,
Roberto Raggi's avatar
Roberto Raggi committed
323
                                           const StringLiteral *fileName)
con's avatar
con committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
{ _ppLines.push_back(PPLine(offset, line, fileName)); }

unsigned TranslationUnit::findLineNumber(unsigned offset) const
{
    std::vector<unsigned>::const_iterator it =
        std::lower_bound(_lineOffsets.begin(), _lineOffsets.end(), offset);

    if (it != _lineOffsets.begin())
        --it;

    return it - _lineOffsets.begin();
}

TranslationUnit::PPLine TranslationUnit::findPreprocessorLine(unsigned offset) const
{
    std::vector<PPLine>::const_iterator it =
        std::lower_bound(_ppLines.begin(), _ppLines.end(), PPLine(offset));

    if (it != _ppLines.begin())
        --it;

    return *it;
}

unsigned TranslationUnit::findColumnNumber(unsigned offset, unsigned lineNumber) const
{
    if (! offset)
        return 0;

    return offset - _lineOffsets[lineNumber];
}

void TranslationUnit::getTokenPosition(unsigned index,
                                       unsigned *line,
                                       unsigned *column,
Roberto Raggi's avatar
Roberto Raggi committed
359
                                       const StringLiteral **fileName) const
con's avatar
con committed
360
361
{ return getPosition(tokenAt(index).offset, line, column, fileName); }

Roberto Raggi's avatar
Roberto Raggi committed
362
363
void TranslationUnit::getTokenStartPosition(unsigned index, unsigned *line,
                                            unsigned *column,
Roberto Raggi's avatar
Roberto Raggi committed
364
                                            const StringLiteral **fileName) const
Roberto Raggi's avatar
Roberto Raggi committed
365
366
367
368
{ return getPosition(tokenAt(index).begin(), line, column, fileName); }

void TranslationUnit::getTokenEndPosition(unsigned index, unsigned *line,
                                          unsigned *column,
Roberto Raggi's avatar
Roberto Raggi committed
369
                                          const StringLiteral **fileName) const
Roberto Raggi's avatar
Roberto Raggi committed
370
371
{ return getPosition(tokenAt(index).end(), line, column, fileName); }

con's avatar
con committed
372
373
374
void TranslationUnit::getPosition(unsigned tokenOffset,
                                  unsigned *line,
                                  unsigned *column,
Roberto Raggi's avatar
Roberto Raggi committed
375
                                  const StringLiteral **fileName) const
con's avatar
con committed
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
{
    unsigned lineNumber = findLineNumber(tokenOffset);
    unsigned columnNumber = findColumnNumber(tokenOffset, lineNumber);
    const PPLine ppLine = findPreprocessorLine(tokenOffset);

    lineNumber -= findLineNumber(ppLine.offset) + 1;
    lineNumber += ppLine.line;

    if (line)
        *line = lineNumber;

    if (column)
        *column = columnNumber;

    if (fileName)
       *fileName = ppLine.fileName;
}

bool TranslationUnit::blockErrors(bool block)
{
396
397
    bool previous = f._blockErrors;
    f._blockErrors = block;
con's avatar
con committed
398
399
400
    return previous;
}

401
void TranslationUnit::message(DiagnosticClient::Level level, unsigned index, const char *format, va_list args)
con's avatar
con committed
402
{
403
    if (f._blockErrors)
con's avatar
con committed
404
405
406
407
408
        return;

    index = std::min(index, tokenCount() - 1);

    unsigned line = 0, column = 0;
Roberto Raggi's avatar
Roberto Raggi committed
409
    const StringLiteral *fileName = 0;
con's avatar
con committed
410
411
412
    getTokenPosition(index, &line, &column, &fileName);

    if (DiagnosticClient *client = control()->diagnosticClient()) {
413
        client->report(level, fileName, line, column, format, args);
con's avatar
con committed
414
415
    } else {
        fprintf(stderr, "%s:%d: ", fileName->chars(), line);
416
417
418
419
420
421
        const char *l = "error";
        if (level == DiagnosticClient::Warning)
            l = "warning";
        else if (level == DiagnosticClient::Fatal)
            l = "fatal";
        fprintf(stderr, "%s: ", l);
con's avatar
con committed
422
423
424
425
426
427

        vfprintf(stderr, format, args);
        fputc('\n', stderr);

        showErrorLine(index, column, stderr);
    }
428
429
430

    if (level == DiagnosticClient::Fatal)
        exit(EXIT_FAILURE);
con's avatar
con committed
431
432
}

433
void TranslationUnit::warning(unsigned index, const char *format, ...)
con's avatar
con committed
434
{
435
    if (f._blockErrors)
con's avatar
con committed
436
437
        return;

438
439
440
    va_list args, ap;
    va_start(args, format);
    va_copy(ap, args);
441
    message(DiagnosticClient::Warning, index, format, args);
442
443
444
    va_end(ap);
    va_end(args);
}
con's avatar
con committed
445

446
447
448
449
void TranslationUnit::error(unsigned index, const char *format, ...)
{
    if (f._blockErrors)
        return;
con's avatar
con committed
450

451
452
453
454
455
456
    va_list args, ap;
    va_start(args, format);
    va_copy(ap, args);
    message(DiagnosticClient::Error, index, format, args);
    va_end(ap);
    va_end(args);
con's avatar
con committed
457
458
459
460
}

void TranslationUnit::fatal(unsigned index, const char *format, ...)
{
461
    if (f._blockErrors)
con's avatar
con committed
462
463
        return;

464
465
466
467
468
469
    va_list args, ap;
    va_start(args, format);
    va_copy(ap, args);
    message(DiagnosticClient::Fatal, index, format, args);
    va_end(ap);
    va_end(args);
con's avatar
con committed
470
471
}

472
473
474
475
476
477
unsigned TranslationUnit::findPreviousLineOffset(unsigned tokenIndex) const
{
    unsigned lineOffset = _lineOffsets[findLineNumber(_tokens->at(tokenIndex).offset)];
    return lineOffset;
}

con's avatar
con committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
void TranslationUnit::showErrorLine(unsigned index, unsigned column, FILE *out)
{
    unsigned lineOffset = _lineOffsets[findLineNumber(_tokens->at(index).offset)];
    for (const char *cp = _firstSourceChar + lineOffset + 1; *cp && *cp != '\n'; ++cp) {
        fputc(*cp, out);
    }
    fputc('\n', out);

    const char *end = _firstSourceChar + lineOffset + 1 + column - 1;
    for (const char *cp = _firstSourceChar + lineOffset + 1; cp != end; ++cp) {
        if (*cp != '\t')
            fputc(' ', out);
        else
            fputc('\t', out);
    }
    fputc('^', out);
    fputc('\n', out);
}

void TranslationUnit::resetAST()
{
    delete _pool;
    _pool = 0;
Roberto Raggi's avatar
Roberto Raggi committed
501
    _ast = 0;
con's avatar
con committed
502
503
504
505
506
507
508
509
510
}

void TranslationUnit::release()
{
    resetAST();
    delete _tokens;
    _tokens = 0;
}

Roberto Raggi's avatar
Roberto Raggi committed
511