FindUsages.cpp 15.9 KB
Newer Older
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
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
**
** 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://qt.nokia.com/contact.
**
**************************************************************************/

#include "FindUsages.h"
31
#include "Overview.h"
32 33 34 35

#include <Control.h>
#include <Literals.h>
#include <Names.h>
36
#include <Scope.h>
37 38
#include <Symbols.h>
#include <AST.h>
Roberto Raggi's avatar
Roberto Raggi committed
39
#include <TranslationUnit.h>
40 41

#include <QtCore/QDir>
Roberto Raggi's avatar
Roberto Raggi committed
42
#include <QtCore/QDebug>
43 44 45

using namespace CPlusPlus;

46
FindUsages::FindUsages(Document::Ptr doc, const Snapshot &snapshot)
Roberto Raggi's avatar
Roberto Raggi committed
47
    : ASTVisitor(doc->translationUnit()),
48 49
      _doc(doc),
      _snapshot(snapshot),
50
      _context(doc, snapshot),
51
      _source(_doc->source()),
Roberto Raggi's avatar
Roberto Raggi committed
52
      _sem(doc->translationUnit()),
53 54
      _inSimpleDeclaration(0),
      _inQProperty(false)
55 56
{
    _snapshot.insert(_doc);
Roberto Raggi's avatar
Roberto Raggi committed
57
    typeofExpression.init(_doc, _snapshot, _context.bindings());
58 59
}

60 61 62 63 64 65 66 67 68 69 70 71 72
FindUsages::FindUsages(const LookupContext &context)
    : ASTVisitor(context.thisDocument()->translationUnit()),
      _doc(context.thisDocument()),
      _snapshot(context.snapshot()),
      _context(context),
      _source(_doc->source()),
      _sem(_doc->translationUnit()),
      _inSimpleDeclaration(0),
      _inQProperty(false)
{
    typeofExpression.init(_doc, _snapshot, _context.bindings());
}

Roberto Raggi's avatar
Roberto Raggi committed
73 74 75 76 77 78
QList<Usage> FindUsages::usages() const
{ return _usages; }

QList<int> FindUsages::references() const
{ return _references; }

Roberto Raggi's avatar
Roberto Raggi committed
79
void FindUsages::operator()(Symbol *symbol)
80
{
Roberto Raggi's avatar
Roberto Raggi committed
81 82 83 84 85 86 87 88
    if (! symbol)
        return;

    _id = symbol->identifier();

    if (! _id)
        return;

89
    _processed.clear();
90
    _references.clear();
Roberto Raggi's avatar
Roberto Raggi committed
91
    _usages.clear();
Roberto Raggi's avatar
Roberto Raggi committed
92
    _declSymbolFullyQualifiedName = LookupContext::fullyQualifiedName(symbol);
93
    _inSimpleDeclaration = 0;
94
    _inQProperty = false;
Roberto Raggi's avatar
Roberto Raggi committed
95

Roberto Raggi's avatar
Roberto Raggi committed
96 97
    // get the canonical id
    _id = _doc->control()->findOrInsertIdentifier(_id->chars(), _id->size());
Roberto Raggi's avatar
Roberto Raggi committed
98

Roberto Raggi's avatar
Roberto Raggi committed
99
    accept(_doc->translationUnit()->ast());
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

QString FindUsages::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;
}

123
void FindUsages::reportResult(unsigned tokenIndex, const QList<LookupItem> &candidates)
124
{
125 126 127
    if (_processed.contains(tokenIndex))
        return;

128 129 130 131 132 133 134 135
    const bool isStrongResult = checkCandidates(candidates);

    if (isStrongResult)
        reportResult(tokenIndex);
}

void FindUsages::reportResult(unsigned tokenIndex)
{
Roberto Raggi's avatar
Roberto Raggi committed
136 137 138 139
    const Token &tk = tokenAt(tokenIndex);
    if (tk.generated())
        return;
    else if (_processed.contains(tokenIndex))
140 141 142 143
        return;

    _processed.insert(tokenIndex);

144 145 146 147 148 149 150 151 152 153
    const QString lineText = matchingLine(tk);

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

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

    const int len = tk.f.length;

154
    const Usage u(_doc->fileName(), lineText, line, col, len);
Roberto Raggi's avatar
Roberto Raggi committed
155
    _usages.append(u);
156 157 158
    _references.append(tokenIndex);
}

Roberto Raggi's avatar
Roberto Raggi committed
159 160 161 162 163 164
bool FindUsages::compareFullyQualifiedName(const QList<const Name *> &path, const QList<const Name *> &other)
{
    if (path.length() != other.length())
        return false;

    for (int i = 0; i < path.length(); ++i) {
165
        if (! compareName(path.at(i), other.at(i)))
Roberto Raggi's avatar
Roberto Raggi committed
166 167 168 169 170 171
            return false;
    }

    return true;
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
bool FindUsages::compareName(const Name *name, const Name *other)
{
    if (name == other)
        return true;

    else if (name && other) {
        const Identifier *id = name->identifier();
        const Identifier *otherId = other->identifier();

        if (id == otherId || (id && id->isEqualTo(otherId)))
            return true;
    }

    return false;
}


189
bool FindUsages::checkCandidates(const QList<LookupItem> &candidates) const
190
{
Roberto Raggi's avatar
Roberto Raggi committed
191 192 193 194 195
    for (int i = candidates.size() - 1; i != -1; --i) {
        const LookupItem &r = candidates.at(i);

        if (Symbol *s = r.declaration()) {
            if (compareFullyQualifiedName(LookupContext::fullyQualifiedName(s), _declSymbolFullyQualifiedName))
Roberto Raggi's avatar
Roberto Raggi committed
196 197
                return true;
        }
198 199 200 201 202 203 204 205 206 207 208
    }

    return false;
}

void FindUsages::ensureNameIsValid(NameAST *ast)
{
    if (ast && ! ast->name)
        ast->name = _sem.check(ast, /*scope = */ 0);
}

Roberto Raggi's avatar
Roberto Raggi committed
209 210 211 212 213 214 215 216 217 218 219 220
bool FindUsages::visit(FunctionDefinitionAST *ast)
{
    AST *thisFunction = _astStack.takeLast();
    accept(ast->decl_specifier_list);
    _astStack.append(thisFunction);

    accept(ast->declarator);
    accept(ast->ctor_initializer);
    accept(ast->function_body);
    return false;
}

Roberto Raggi's avatar
Roberto Raggi committed
221 222 223 224
bool FindUsages::visit(NamespaceAST *ast)
{
    const Identifier *id = identifier(ast->identifier_token);
    if (id == _id && ast->symbol) {
Roberto Raggi's avatar
Roberto Raggi committed
225
        const QList<LookupItem> candidates = _context.lookup(ast->symbol->name(), enclosingScope());
Roberto Raggi's avatar
Roberto Raggi committed
226 227 228 229 230
        reportResult(ast->identifier_token, candidates);
    }
    return true;
}

231 232 233 234 235 236 237
bool FindUsages::visit(MemInitializerAST *ast)
{
    if (ast->name && ast->name->asSimpleName() != 0) {
        ensureNameIsValid(ast->name);

        SimpleNameAST *simple = ast->name->asSimpleName();
        if (identifier(simple->identifier_token) == _id) {
Roberto Raggi's avatar
Roberto Raggi committed
238
            const QList<LookupItem> candidates = _context.lookup(simple->name, enclosingScope());
239 240 241
            reportResult(simple->identifier_token, candidates);
        }
    }
242
    accept(ast->expression_list);
243 244 245 246 247 248 249 250
    return false;
}

bool FindUsages::visit(MemberAccessAST *ast)
{
    if (ast->member_name) {
        if (SimpleNameAST *simple = ast->member_name->asSimpleName()) {
            if (identifier(simple->identifier_token) == _id) {
251
                checkExpression(ast->firstToken(), simple->identifier_token);
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
                return false;
            }
        }
    }

    return true;
}

void FindUsages::checkExpression(unsigned startToken, unsigned endToken)
{
    const unsigned begin = tokenAt(startToken).begin();
    const unsigned end = tokenAt(endToken).end();

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

    unsigned line, column;
    getTokenStartPosition(startToken, &line, &column);
270
    Scope *scope = _doc->scopeAt(line, column);
271

272
    const QList<LookupItem> results = typeofExpression(expression, scope,
273
                                                       TypeOfExpression::Preprocess);
274

275
    reportResult(endToken, results);
276 277 278 279
}

bool FindUsages::visit(QualifiedNameAST *ast)
{
Roberto Raggi's avatar
Roberto Raggi committed
280
    for (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list; it; it = it->next) {
Roberto Raggi's avatar
Roberto Raggi committed
281
        NestedNameSpecifierAST *nested_name_specifier = it->value;
282 283 284 285 286 287 288 289 290

        if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
            SimpleNameAST *simple_name = class_or_namespace_name->asSimpleName();

            TemplateIdAST *template_id = 0;
            if (! simple_name) {
                template_id = class_or_namespace_name->asTemplateId();

                if (template_id) {
Roberto Raggi's avatar
Roberto Raggi committed
291
                    for (TemplateArgumentListAST *arg_it = template_id->template_argument_list; arg_it; arg_it = arg_it->next) {
Roberto Raggi's avatar
Roberto Raggi committed
292
                        accept(arg_it->value);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
                    }
                }
            }

            if (simple_name || template_id) {
                const unsigned identifier_token = simple_name
                           ? simple_name->identifier_token
                           : template_id->identifier_token;

                if (identifier(identifier_token) == _id)
                    checkExpression(ast->firstToken(), identifier_token);
            }
        }
    }

    if (NameAST *unqualified_name = ast->unqualified_name) {
        unsigned identifier_token = 0;

        if (SimpleNameAST *simple_name = unqualified_name->asSimpleName())
            identifier_token = simple_name->identifier_token;

        else if (DestructorNameAST *dtor_name = unqualified_name->asDestructorName())
            identifier_token = dtor_name->identifier_token;

        TemplateIdAST *template_id = 0;
        if (! identifier_token) {
            template_id = unqualified_name->asTemplateId();

            if (template_id) {
                identifier_token = template_id->identifier_token;

Roberto Raggi's avatar
Roberto Raggi committed
324
                for (TemplateArgumentListAST *template_arguments = template_id->template_argument_list;
325
                     template_arguments; template_arguments = template_arguments->next) {
326
                    accept(template_arguments->value);
327 328 329 330 331 332 333 334 335 336 337 338 339
                }
            }
        }

        if (identifier_token && identifier(identifier_token) == _id)
            checkExpression(ast->firstToken(), identifier_token);
    }

    return false;
}

bool FindUsages::visit(EnumeratorAST *ast)
{
Roberto Raggi's avatar
Roberto Raggi committed
340
    const Identifier *id = identifier(ast->identifier_token);
341
    if (id == _id) {
Roberto Raggi's avatar
Roberto Raggi committed
342
        const QList<LookupItem> candidates = _context.lookup(control()->nameId(id), enclosingScope());
343 344 345 346 347 348 349 350 351 352
        reportResult(ast->identifier_token, candidates);
    }

    accept(ast->expression);

    return false;
}

bool FindUsages::visit(SimpleNameAST *ast)
{
Roberto Raggi's avatar
Roberto Raggi committed
353
    const Identifier *id = identifier(ast->identifier_token);
354
    if (id == _id) {
Roberto Raggi's avatar
Roberto Raggi committed
355
        const QList<LookupItem> candidates = _context.lookup(ast->name, enclosingScope());
356 357 358 359 360 361 362 363
        reportResult(ast->identifier_token, candidates);
    }

    return false;
}

bool FindUsages::visit(DestructorNameAST *ast)
{
Roberto Raggi's avatar
Roberto Raggi committed
364
    const Identifier *id = identifier(ast->identifier_token);
365
    if (id == _id) {
Roberto Raggi's avatar
Roberto Raggi committed
366
        const QList<LookupItem> candidates = _context.lookup(ast->name, enclosingScope());
367 368 369 370 371 372 373 374 375
        reportResult(ast->identifier_token, candidates);
    }

    return false;
}

bool FindUsages::visit(TemplateIdAST *ast)
{
    if (_id == identifier(ast->identifier_token)) {
Roberto Raggi's avatar
Roberto Raggi committed
376
        const QList<LookupItem> candidates = _context.lookup(ast->name, enclosingScope());
377 378 379
        reportResult(ast->identifier_token, candidates);
    }

Roberto Raggi's avatar
Roberto Raggi committed
380
    for (TemplateArgumentListAST *template_arguments = ast->template_argument_list;
381
         template_arguments; template_arguments = template_arguments->next) {
382
        accept(template_arguments->value);
383 384 385 386 387 388 389
    }

    return false;
}

bool FindUsages::visit(ParameterDeclarationAST *ast)
{
Roberto Raggi's avatar
Roberto Raggi committed
390
    for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
391
        accept(it->value);
392 393

    if (DeclaratorAST *declarator = ast->declarator) {
Roberto Raggi's avatar
Roberto Raggi committed
394
        for (SpecifierListAST *it = declarator->attribute_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
395
            accept(it->value);
396

Roberto Raggi's avatar
Roberto Raggi committed
397
        for (PtrOperatorListAST *it = declarator->ptr_operator_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
398
            accept(it->value);
399 400 401 402

        if (! _inSimpleDeclaration) // visit the core declarator only if we are not in simple-declaration.
            accept(declarator->core_declarator);

Roberto Raggi's avatar
Roberto Raggi committed
403
        for (PostfixDeclaratorListAST *it = declarator->postfix_declarator_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
404
            accept(it->value);
405

Roberto Raggi's avatar
Roberto Raggi committed
406
        for (SpecifierListAST *it = declarator->post_attribute_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
407
            accept(it->value);
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

        accept(declarator->initializer);
    }

    accept(ast->expression);
    return false;
}

bool FindUsages::visit(ExpressionOrDeclarationStatementAST *ast)
{
    accept(ast->declaration);
    return false;
}

bool FindUsages::visit(FunctionDeclaratorAST *ast)
{
    accept(ast->parameters);

Roberto Raggi's avatar
Roberto Raggi committed
426
    for (SpecifierListAST *it = ast->cv_qualifier_list; it; it = it->next)
Roberto Raggi's avatar
Roberto Raggi committed
427
        accept(it->value);
428 429 430 431 432 433

    accept(ast->exception_specification);

    return false;
}

434
bool FindUsages::visit(SimpleDeclarationAST *ast)
435
{
436 437 438
    for  (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next)
        accept(it->value);

439
    ++_inSimpleDeclaration;
440 441 442 443
    for (DeclaratorListAST *it = ast->declarator_list; it; it = it->next)
        accept(it->value);
    --_inSimpleDeclaration;
    return false;
444 445
}

446
bool FindUsages::visit(ObjCSelectorAST *ast)
447
{
Erik Verbruggen's avatar
Erik Verbruggen committed
448 449
    if (ast->name) {
        const Identifier *id = ast->name->identifier();
450
        if (id == _id) {
Roberto Raggi's avatar
Roberto Raggi committed
451
            const QList<LookupItem> candidates = _context.lookup(ast->name, enclosingScope());
Erik Verbruggen's avatar
Erik Verbruggen committed
452
            reportResult(ast->firstToken(), candidates);
453 454 455 456 457
        }
    }

    return false;
}
458 459 460 461 462 463 464 465 466

bool FindUsages::visit(QtPropertyDeclarationAST *)
{
    _inQProperty = true;
    return true;
}

void FindUsages::endVisit(QtPropertyDeclarationAST *)
{ _inQProperty = false; }
467

Roberto Raggi's avatar
Roberto Raggi committed
468
bool FindUsages::visit(TypenameTypeParameterAST *ast)
469
{
Roberto Raggi's avatar
Roberto Raggi committed
470 471 472
    accept(ast->name);
    accept(ast->type_id);
    return false;
473 474
}

Roberto Raggi's avatar
Roberto Raggi committed
475
bool FindUsages::visit(TemplateTypeParameterAST *ast)
476
{
Roberto Raggi's avatar
Roberto Raggi committed
477 478 479
    accept(ast->name);
    accept(ast->type_id);
    return false;
480 481
}

Roberto Raggi's avatar
Roberto Raggi committed
482
FunctionDefinitionAST *FindUsages::enclosingFunctionDefinition() const
483
{
Roberto Raggi's avatar
Roberto Raggi committed
484 485 486 487 488
    for (int index = _astStack.size() - 1; index != -1; --index) {
        AST *ast = _astStack.at(index);

        if (FunctionDefinitionAST *funDef = ast->asFunctionDefinition())
            return funDef;
489
    }
Roberto Raggi's avatar
Roberto Raggi committed
490 491

    return 0;
492 493
}

Roberto Raggi's avatar
Roberto Raggi committed
494
TemplateDeclarationAST *FindUsages::enclosingTemplateDeclaration() const
495
{
Roberto Raggi's avatar
Roberto Raggi committed
496 497 498 499 500
    for (int index = _astStack.size() - 1; index != -1; --index) {
        AST *ast = _astStack.at(index);

        if (TemplateDeclarationAST *funDef = ast->asTemplateDeclaration())
            return funDef;
501
    }
Roberto Raggi's avatar
Roberto Raggi committed
502 503

    return 0;
504 505
}

Roberto Raggi's avatar
Roberto Raggi committed
506
Scope *FindUsages::enclosingScope()
507
{
Roberto Raggi's avatar
Roberto Raggi committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
    for (int index = _astStack.size() - 1; index != -1; --index) {
        AST *ast = _astStack.at(index);

        if (NamespaceAST *ns = ast->asNamespace()) {
            if (ns->symbol)
                return ns->symbol->members();

        } else if (ClassSpecifierAST *classSpec = ast->asClassSpecifier()) {
            if (classSpec->symbol)
                return classSpec->symbol->members();

        } else if (FunctionDefinitionAST *funDef = ast->asFunctionDefinition()) {
            if (funDef->symbol)
                return funDef->symbol->members();

        } else if (CompoundStatementAST *blockStmt = ast->asCompoundStatement()) {
            if (blockStmt->symbol)
                return blockStmt->symbol->members();

        } else if (IfStatementAST *ifStmt = ast->asIfStatement()) {
            if (ifStmt->symbol)
                return ifStmt->symbol->members();
530

Roberto Raggi's avatar
Roberto Raggi committed
531 532 533 534 535 536 537 538 539 540 541 542
        } else if (WhileStatementAST *whileStmt = ast->asWhileStatement()) {
            if (whileStmt->symbol)
                return whileStmt->symbol->members();

        } else if (ForStatementAST *forStmt = ast->asForStatement()) {
            if (forStmt->symbol)
                return forStmt->symbol->members();

        } else if (ForeachStatementAST *foreachStmt = ast->asForeachStatement()) {
            if (foreachStmt->symbol)
                return foreachStmt->symbol->members();

543 544 545 546 547 548 549 550
        } else if (SwitchStatementAST *switchStmt = ast->asSwitchStatement()) {
            if (switchStmt->symbol)
                return switchStmt->symbol->members();

        } else if (CatchClauseAST *catchClause = ast->asCatchClause()) {
            if (catchClause->symbol)
                return catchClause->symbol->members();

Roberto Raggi's avatar
Roberto Raggi committed
551
        }
552 553
    }

Roberto Raggi's avatar
Roberto Raggi committed
554 555 556 557 558 559 560 561 562 563 564 565
    return _doc->globalSymbols();
}

bool FindUsages::preVisit(AST *ast)
{
    _astStack.append(ast);
    return true;
}

void FindUsages::postVisit(AST *)
{
    _astStack.takeLast();
566
}