cppfindreferences.cpp 12.4 KB
Newer Older
Roberto Raggi's avatar
Roberto Raggi committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/

#include "cppfindreferences.h"
#include "cppmodelmanager.h"
#include "cpptoolsconstants.h"

#include <texteditor/basetexteditor.h>
#include <find/searchresultwindow.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/filesearch.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/icore.h>

#include <ASTVisitor.h>
#include <AST.h>
#include <Control.h>
#include <Literals.h>
#include <TranslationUnit.h>
#include <Symbols.h>
47
48
#include <Names.h>
#include <Scope.h>
Roberto Raggi's avatar
Roberto Raggi committed
49
50
51
52

#include <cplusplus/CppDocument.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/ResolveExpression.h>
53
#include <cplusplus/Overview.h>
Roberto Raggi's avatar
Roberto Raggi committed
54
#include <cplusplus/TypeOfExpression.h>
Roberto Raggi's avatar
Roberto Raggi committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

#include <QtCore/QTime>
#include <QtCore/QtConcurrentRun>
#include <QtCore/QDir>

#include <qtconcurrent/runextensions.h>

using namespace CppTools::Internal;
using namespace CPlusPlus;

namespace {

struct Process: protected ASTVisitor
{
public:
    Process(QFutureInterface<Core::Utils::FileSearchResult> &future,
            Document::Ptr doc, const Snapshot &snapshot)
            : ASTVisitor(doc->control()),
              _future(future),
              _doc(doc),
              _snapshot(snapshot),
76
77
78
79
80
              _source(_doc->source()),
              _sem(doc->control())
    {
        _snapshot.insert(_doc);
    }
Roberto Raggi's avatar
Roberto Raggi committed
81

82
    void operator()(Symbol *symbol, Identifier *id, AST *ast)
Roberto Raggi's avatar
Roberto Raggi committed
83
    {
84
        _declSymbol = symbol;
Roberto Raggi's avatar
Roberto Raggi committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
        _id = id;
        _exprDoc = Document::create("<references>");
        accept(ast);
    }

protected:
    using ASTVisitor::visit;

    QString matchingLine(const Token &tk) const
    {
        const char *beg = _source.constData();
        const char *cp = beg + tk.offset;
        for (; cp != beg - 1; --cp) {
            if (*cp == '\n')
                break;
        }

        ++cp;

        const char *lineEnd = cp + 1;
        for (; *lineEnd; ++lineEnd) {
            if (*lineEnd == '\n')
                break;
        }

        const QString matchingLine = QString::fromUtf8(cp, lineEnd - cp);
        return matchingLine;

    }

    void reportResult(unsigned tokenIndex)
    {
        const Token &tk = tokenAt(tokenIndex);
        const QString lineText = matchingLine(tk);

        unsigned line, col;
        getTokenStartPosition(tokenIndex, &line, &col);

        if (col)
            --col;  // adjust the column position.

        int len = tk.f.length;

        _future.reportResult(Core::Utils::FileSearchResult(QDir::toNativeSeparators(_doc->fileName()),
                                                           line, lineText, col, len));
    }

132
    bool checkCandidates(const QList<Symbol *> &candidates) const
Roberto Raggi's avatar
Roberto Raggi committed
133
    {
Roberto Raggi's avatar
Roberto Raggi committed
134
135
136
137
138
139
140
141
142
143
144
        if (Symbol *canonicalSymbol = LookupContext::canonicalSymbol(candidates)) {
#if 0
            qDebug() << "*** canonical symbol:" << canonicalSymbol->fileName()
                    << canonicalSymbol->line() << canonicalSymbol->column()
                    << "candidates:" << candidates.size();
#endif

            return isDeclSymbol(canonicalSymbol);
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
145
146
    }

147
    bool isDeclSymbol(Symbol *symbol) const
Roberto Raggi's avatar
Roberto Raggi committed
148
    {
149
150
        if (! symbol)
            return false;
Roberto Raggi's avatar
Roberto Raggi committed
151

152
153
154
155
156
157
158
159
160
        else if (symbol == _declSymbol)
            return true;

        else if (symbol->line() == _declSymbol->line() && symbol->column() == _declSymbol->column()) {
            if (! qstrcmp(symbol->fileName(), _declSymbol->fileName()))
                return true;
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
161
162
    }

163
    LookupContext currentContext(AST *ast) const
Roberto Raggi's avatar
Roberto Raggi committed
164
    {
165
166
167
168
        unsigned line, column;
        getTokenStartPosition(ast->firstToken(), &line, &column);
        Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);
        return LookupContext(lastVisibleSymbol, _exprDoc, _doc, _snapshot);
Roberto Raggi's avatar
Roberto Raggi committed
169
170
    }

Roberto Raggi's avatar
Roberto Raggi committed
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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
    virtual bool visit(PostfixExpressionAST *ast)
    {
        _postfixExpressionStack.append(ast);
        return true;
    }

    virtual void endVisit(PostfixExpressionAST *ast)
    {
        _postfixExpressionStack.removeLast();
    }

    virtual bool visit(MemberAccessAST *ast)
    {
        if (! ast->member_name)
            return false;

        SimpleNameAST *simple = ast->member_name->asSimpleName();
        if (! simple)
            return true; // ### TODO handle pseudo-destructors and qualified names.

        Q_ASSERT(! _postfixExpressionStack.isEmpty());

        if (identifier(simple->identifier_token) == _id) {
            unsigned startOfPostfixExpression = _postfixExpressionStack.last()->firstToken();

            unsigned begin = tokenAt(startOfPostfixExpression).begin();
            unsigned end = tokenAt(ast->member_name->lastToken() - 1).end();

            const QString expression = _source.mid(begin, end - begin);
            // qDebug() << "*** expression:" << expression;

            TypeOfExpression typeofExpression;
            typeofExpression.setSnapshot(_snapshot);

            unsigned line, column;
            getTokenStartPosition(startOfPostfixExpression, &line, &column);
            Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);

            const QList<TypeOfExpression::Result> results =
                    typeofExpression(expression, _doc, lastVisibleSymbol,
                                     TypeOfExpression::NoPreprocess);

            QList<Symbol *> candidates;

            foreach (TypeOfExpression::Result r, results) {
                FullySpecifiedType ty = r.first;
                Symbol *lastVisibleSymbol = r.second;

                candidates.append(lastVisibleSymbol);
            }

            if (checkCandidates(candidates))
                reportResult(simple->identifier_token);
        }

        return false;
    }

Roberto Raggi's avatar
Roberto Raggi committed
229
230
    virtual bool visit(QualifiedNameAST *ast)
    {
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
        if (! ast->name) {
            //qWarning() << "invalid AST at" << _doc->fileName() << line << column;
            ast->name = _sem.check(ast, /*scope */ static_cast<Scope *>(0));
        }

        Q_ASSERT(ast->name != 0);
        Identifier *id = ast->name->identifier();
        if (id == _id && ast->unqualified_name) {
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
            if (checkCandidates(candidates))
                reportResult(ast->unqualified_name->firstToken());
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
246
247
248
249
250
251
    }

    virtual bool visit(SimpleNameAST *ast)
    {
        Identifier *id = identifier(ast->identifier_token);
        if (id == _id) {
252
253
254
255
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
            if (checkCandidates(candidates))
                reportResult(ast->identifier_token);
Roberto Raggi's avatar
Roberto Raggi committed
256
257
258
259
260
261
262
263
        }

        return false;
    }

    virtual bool visit(TemplateIdAST *ast)
    {
        Identifier *id = identifier(ast->identifier_token);
264
265
266
267
268
269
        if (id == _id) {
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
            if (checkCandidates(candidates))
                reportResult(ast->identifier_token);
        }
Roberto Raggi's avatar
Roberto Raggi committed
270

271
        return false;
Roberto Raggi's avatar
Roberto Raggi committed
272
273
274
275
    }

private:
    QFutureInterface<Core::Utils::FileSearchResult> &_future;
276
277
    Identifier *_id; // ### remove me
    Symbol *_declSymbol;
Roberto Raggi's avatar
Roberto Raggi committed
278
279
280
281
    Document::Ptr _doc;
    Snapshot _snapshot;
    QByteArray _source;
    Document::Ptr _exprDoc;
282
    Semantic _sem;
Roberto Raggi's avatar
Roberto Raggi committed
283
    QList<PostfixExpressionAST *> _postfixExpressionStack;
Roberto Raggi's avatar
Roberto Raggi committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
};

} // end of anonymous namespace

CppFindReferences::CppFindReferences(CppModelManager *modelManager)
    : _modelManager(modelManager),
      _resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>())
{
    m_watcher.setPendingResultsLimit(1);
    connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(displayResult(int)));
    connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
}

CppFindReferences::~CppFindReferences()
{
}

static void find_helper(QFutureInterface<Core::Utils::FileSearchResult> &future,
302
303
                        Snapshot snapshot,
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
304
305
306
{
    QTime tm;
    tm.start();
307

308
309
310
    Identifier *symbolId = symbol->identifier();
    Q_ASSERT(symbolId != 0);

311
312
    const QString fileName = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength());

Roberto Raggi's avatar
Roberto Raggi committed
313
314
315
316
317
318
319
320
321
322
    QStringList files(fileName);
    files += snapshot.dependsOn(fileName);
    qDebug() << "done in:" << tm.elapsed() << "number of files to parse:" << files.size();

    future.setProgressRange(0, files.size());

    tm.start();
    for (int i = 0; i < files.size(); ++i) {
        const QString &fn = files.at(i);
        future.setProgressValueAndText(i, QFileInfo(fn).fileName());
323
324
325
326
327
328
329
330
331

        Document::Ptr previousDoc = snapshot.value(fn);
        if (previousDoc) {
            Control *control = previousDoc->control();
            Identifier *id = control->findIdentifier(symbolId->chars(), symbolId->size());
            if (! id)
                continue; // skip this document, it's not using symbolId.
        }

Roberto Raggi's avatar
Roberto Raggi committed
332
333
334
335
        QFile f(fn);
        if (! f.open(QFile::ReadOnly))
            continue;

336
        const QString source = QTextStream(&f).readAll(); // ### FIXME
Roberto Raggi's avatar
Roberto Raggi committed
337
338
        const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fn);
        Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, fn);
339
        doc->tokenize();
Roberto Raggi's avatar
Roberto Raggi committed
340
341

        Control *control = doc->control();
342
        if (Identifier *id = control->findIdentifier(symbolId->chars(), symbolId->size())) {
343
344
345
            doc->check();
            TranslationUnit *unit = doc->translationUnit();
            Process process(future, doc, snapshot);
346
            process(symbol, id, unit->ast());
347
        }
Roberto Raggi's avatar
Roberto Raggi committed
348
349
350
351
    }
    future.setProgressValue(files.size());
}

352
void CppFindReferences::findAll(const Snapshot &snapshot, Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
353
354
355
356
357
358
{
    _resultWindow->clearContents();
    _resultWindow->popup(true);

    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();

359
    QFuture<Core::Utils::FileSearchResult> result = QtConcurrent::run(&find_helper, snapshot, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
360
361
362
    m_watcher.setFuture(result);

    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."),
363
                                                              CppTools::Constants::TASK_SEARCH,
Roberto Raggi's avatar
Roberto Raggi committed
364
365
366
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
                                                              Core::ProgressManager::CloseOnSuccess);

    connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
}

void CppFindReferences::displayResult(int index)
{
    Core::Utils::FileSearchResult result = m_watcher.future().resultAt(index);
    Find::ResultWindowItem *item = _resultWindow->addResult(result.fileName,
                                                            result.lineNumber,
                                                            result.matchingLine,
                                                            result.matchStart,
                                                            result.matchLength);
    if (item)
        connect(item, SIGNAL(activated(const QString&,int,int)),
                this, SLOT(openEditor(const QString&,int,int)));
}

void CppFindReferences::searchFinished()
{
    emit changed();
}

void CppFindReferences::openEditor(const QString &fileName, int line, int column)
{
    TextEditor::BaseTextEditor::openEditorAt(fileName, line, column);
}