Files
qt-creator/src/plugins/cppeditor/cppchecksymbols.cpp

989 lines
28 KiB
C++
Raw Normal View History

/**************************************************************************
**
** This file is part of Qt Creator
**
2010-03-05 11:25:49 +01:00
** Copyright (c) 2010 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
2009-08-14 09:30:56 +02:00
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
2010-07-13 15:25:05 +02:00
#include "cppchecksymbols.h"
2010-07-15 16:03:48 +02:00
#include "cpplocalsymbols.h"
#include <cplusplus/Overview.h>
#include <Names.h>
#include <Literals.h>
#include <Symbols.h>
#include <TranslationUnit.h>
#include <Scope.h>
#include <AST.h>
2010-05-25 14:53:21 +02:00
#include <SymbolVisitor.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QThreadPool>
#include <QtCore/QDebug>
#include <qtconcurrent/runextensions.h>
using namespace CPlusPlus;
2010-07-15 16:03:48 +02:00
using namespace CppEditor::Internal;
2010-05-25 14:53:21 +02:00
namespace {
class FriendlyThread: public QThread
{
public:
using QThread::msleep;
};
2010-05-25 14:53:21 +02:00
class CollectTypes: protected SymbolVisitor
{
Document::Ptr _doc;
Snapshot _snapshot;
QSet<QByteArray> _types;
2010-07-15 16:03:48 +02:00
QSet<QByteArray> _members;
2010-08-03 17:34:51 +02:00
QSet<QByteArray> _virtualMethods;
2010-05-25 14:53:21 +02:00
QList<NameAST *> _names;
bool _mainDocument;
public:
CollectTypes(Document::Ptr doc, const Snapshot &snapshot)
: _doc(doc), _snapshot(snapshot), _mainDocument(false)
{
QSet<Namespace *> processed;
process(doc, &processed);
}
const QSet<QByteArray> &types() const
{
return _types;
}
2010-07-15 16:03:48 +02:00
const QSet<QByteArray> &members() const
{
return _members;
}
2010-08-03 17:34:51 +02:00
const QSet<QByteArray> &virtualMethods() const
{
return _virtualMethods;
}
2010-05-25 14:53:21 +02:00
protected:
void process(Document::Ptr doc, QSet<Namespace *> *processed)
{
if (! doc)
return;
else if (! processed->contains(doc->globalNamespace())) {
processed->insert(doc->globalNamespace());
foreach (const Document::Include &i, doc->includes())
process(_snapshot.document(i.fileName()), processed);
_mainDocument = (doc == _doc); // ### improve
accept(doc->globalNamespace());
}
}
void addType(const Identifier *id)
{
if (id)
_types.insert(QByteArray::fromRawData(id->chars(), id->size()));
}
void addType(const Name *name)
{
if (! name) {
return;
} else if (const QualifiedNameId *q = name->asQualifiedNameId()) {
addType(q->base());
addType(q->name());
2010-05-25 14:53:21 +02:00
} else if (name->isNameId() || name->isTemplateNameId()) {
addType(name->identifier());
}
}
2010-07-15 16:03:48 +02:00
void addMember(const Name *name)
{
if (! name) {
return;
} else if (name->isNameId()) {
const Identifier *id = name->identifier();
_members.insert(QByteArray::fromRawData(id->chars(), id->size()));
}
}
2010-08-03 17:34:51 +02:00
void addVirtualMethod(const Name *name)
{
if (! name) {
return;
} else if (name->isNameId()) {
const Identifier *id = name->identifier();
_virtualMethods.insert(QByteArray::fromRawData(id->chars(), id->size()));
}
}
2010-05-25 14:53:21 +02:00
// nothing to do
virtual bool visit(UsingNamespaceDirective *) { return true; }
virtual bool visit(UsingDeclaration *) { return true; }
virtual bool visit(Argument *) { return true; }
virtual bool visit(BaseClass *) { return true; }
virtual bool visit(Function *symbol)
{
2010-08-03 17:34:51 +02:00
if (symbol->isVirtual())
addVirtualMethod(symbol->name());
for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) {
Scope *scope = p->scope();
for (unsigned i = 0; i < scope->symbolCount(); ++i)
accept(scope->symbolAt(i));
}
2010-05-25 14:53:21 +02:00
return true;
}
2010-08-05 12:19:07 +02:00
virtual bool visit(Block *)
2010-05-25 14:53:21 +02:00
{
return true;
}
virtual bool visit(NamespaceAlias *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(Declaration *symbol)
{
2010-08-03 17:34:51 +02:00
if (Function *funTy = symbol->type()->asFunctionType()) {
if (funTy->isVirtual())
addVirtualMethod(symbol->name());
}
2010-05-25 14:53:21 +02:00
if (symbol->isTypedef())
addType(symbol->name());
2010-07-15 16:03:48 +02:00
else if (! symbol->type()->isFunctionType() && symbol->enclosingSymbol()->isClass())
addMember(symbol->name());
2010-05-25 14:53:21 +02:00
return true;
}
virtual bool visit(TypenameArgument *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(Enum *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(Namespace *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(Class *symbol)
{
for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) {
Scope *scope = p->scope();
for (unsigned i = 0; i < scope->symbolCount(); ++i)
accept(scope->symbolAt(i));
}
2010-05-25 14:53:21 +02:00
addType(symbol->name());
return true;
}
virtual bool visit(ForwardClassDeclaration *symbol)
{
for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) {
Scope *scope = p->scope();
for (unsigned i = 0; i < scope->symbolCount(); ++i)
accept(scope->symbolAt(i));
}
2010-05-25 14:53:21 +02:00
addType(symbol->name());
return true;
}
// Objective-C
virtual bool visit(ObjCBaseClass *) { return true; }
virtual bool visit(ObjCBaseProtocol *) { return true; }
virtual bool visit(ObjCPropertyDeclaration *) { return true; }
2010-08-05 12:19:07 +02:00
virtual bool visit(ObjCMethod *) { return true; }
2010-05-25 14:53:21 +02:00
virtual bool visit(ObjCClass *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(ObjCForwardClassDeclaration *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(ObjCProtocol *symbol)
{
addType(symbol->name());
return true;
}
virtual bool visit(ObjCForwardProtocolDeclaration *symbol)
{
addType(symbol->name());
return true;
}
};
} // end of anonymous namespace
2010-07-13 15:25:05 +02:00
CheckSymbols::Future CheckSymbols::go(Document::Ptr doc, const LookupContext &context)
{
Q_ASSERT(doc);
2010-07-13 15:25:05 +02:00
return (new CheckSymbols(doc, context))->start();
}
2010-07-13 15:25:05 +02:00
CheckSymbols::CheckSymbols(Document::Ptr doc, const LookupContext &context)
: ASTVisitor(doc->translationUnit()), _doc(doc), _context(context)
{
CollectTypes collectTypes(doc, context.snapshot());
2010-08-05 12:19:07 +02:00
_fileName = doc->fileName();
2010-05-25 14:53:21 +02:00
_potentialTypes = collectTypes.types();
2010-07-15 16:03:48 +02:00
_potentialMembers = collectTypes.members();
2010-08-03 17:34:51 +02:00
_potentialVirtualMethods = collectTypes.virtualMethods();
2010-07-15 13:41:51 +02:00
_flushRequested = false;
_flushLine = 0;
2010-08-03 12:22:16 +02:00
typeOfExpression.init(_doc, _context.snapshot(), _context.bindings());
}
2010-07-13 15:25:05 +02:00
CheckSymbols::~CheckSymbols()
{ }
2010-07-13 15:25:05 +02:00
void CheckSymbols::run()
{
_diagnosticMessages.clear();
if (! isCanceled()) {
if (_doc->translationUnit()) {
accept(_doc->translationUnit()->ast());
flush();
}
2010-07-06 12:42:55 +02:00
}
reportFinished();
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::warning(unsigned line, unsigned column, const QString &text, unsigned length)
{
Document::DiagnosticMessage m(Document::DiagnosticMessage::Warning, _fileName, line, column, text, length);
_diagnosticMessages.append(m);
return false;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::warning(AST *ast, const QString &text)
{
const Token &firstToken = tokenAt(ast->firstToken());
const Token &lastToken = tokenAt(ast->lastToken() - 1);
const unsigned length = lastToken.end() - firstToken.begin();
unsigned line = 1, column = 1;
getTokenStartPosition(ast->firstToken(), &line, &column);
warning(line, column, text, length);
return false;
}
2010-08-05 12:19:07 +02:00
FunctionDefinitionAST *CheckSymbols::enclosingFunctionDefinition() const
{
for (int index = _astStack.size() - 1; index != -1; --index) {
AST *ast = _astStack.at(index);
if (FunctionDefinitionAST *funDef = ast->asFunctionDefinition())
return funDef;
}
return 0;
}
TemplateDeclarationAST *CheckSymbols::enclosingTemplateDeclaration() const
{
for (int index = _astStack.size() - 1; index != -1; --index) {
AST *ast = _astStack.at(index);
if (TemplateDeclarationAST *funDef = ast->asTemplateDeclaration())
return funDef;
}
return 0;
}
Scope *CheckSymbols::enclosingScope() const
{
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();
} 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();
} 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();
2010-08-05 12:19:07 +02:00
}
}
return _doc->globalSymbols();
}
bool CheckSymbols::preVisit(AST *ast)
{
2010-08-05 12:19:07 +02:00
_astStack.append(ast);
if (isCanceled())
return false;
return true;
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::postVisit(AST *)
{
_astStack.takeLast();
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(NamespaceAST *ast)
{
2010-05-25 17:49:29 +02:00
if (ast->identifier_token) {
const Token &tok = tokenAt(ast->identifier_token);
if (! tok.generated()) {
unsigned line, column;
getTokenStartPosition(ast->identifier_token, &line, &column);
Use use(line, column, tok.length());
2010-08-05 12:19:07 +02:00
addUse(use);
2010-05-25 17:49:29 +02:00
}
}
return true;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(UsingDirectiveAST *)
{
return true;
}
2010-08-03 17:34:51 +02:00
bool CheckSymbols::visit(SimpleDeclarationAST *ast)
{
if (ast->declarator_list && !ast->declarator_list->next) {
if (ast->symbols && ! ast->symbols->next && !ast->symbols->value->isGenerated()) {
Symbol *decl = ast->symbols->value;
if (NameAST *declId = declaratorId(ast->declarator_list->value)) {
if (Function *funTy = decl->type()->asFunctionType()) {
if (funTy->isVirtual()) {
2010-08-05 12:19:07 +02:00
addUse(declId, Use::VirtualMethod);
2010-08-03 17:34:51 +02:00
} else if (maybeVirtualMethod(decl->name())) {
2010-08-05 12:19:07 +02:00
addVirtualMethod(_context.lookup(decl->name(), decl->scope()), declId, funTy->argumentCount());
2010-08-03 17:34:51 +02:00
}
}
}
}
}
2010-05-25 17:49:29 +02:00
return true;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(NamedTypeSpecifierAST *)
2010-05-25 17:49:29 +02:00
{
2010-05-25 14:53:21 +02:00
return true;
}
2010-07-15 16:03:48 +02:00
bool CheckSymbols::visit(MemberAccessAST *ast)
{
accept(ast->base_expression);
2010-08-03 12:22:16 +02:00
if (! ast->member_name)
return false;
if (const Name *name = ast->member_name->name) {
if (const Identifier *ident = name->identifier()) {
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
if (_potentialMembers.contains(id)) {
const Token start = tokenAt(ast->firstToken());
const Token end = tokenAt(ast->lastToken() - 1);
const QByteArray expression = _doc->source().mid(start.begin(), end.end() - start.begin());
2010-08-05 12:19:07 +02:00
const QList<LookupItem> candidates = typeOfExpression(expression, enclosingScope(), TypeOfExpression::Preprocess);
addClassMember(candidates, ast->member_name);
2010-08-03 12:22:16 +02:00
}
}
}
2010-07-15 16:03:48 +02:00
return false;
}
2010-08-03 17:34:51 +02:00
bool CheckSymbols::visit(CallAST *ast)
{
if (ast->base_expression) {
accept(ast->base_expression);
unsigned argumentCount = 0;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next)
++argumentCount;
if (MemberAccessAST *access = ast->base_expression->asMemberAccess()) {
if (access->member_name && access->member_name->name) {
if (maybeVirtualMethod(access->member_name->name)) {
const QByteArray expression = textOf(access);
2010-08-05 12:19:07 +02:00
const QList<LookupItem> candidates = typeOfExpression(expression, enclosingScope(),
TypeOfExpression::Preprocess);
NameAST *memberName = access->member_name;
if (QualifiedNameAST *q = memberName->asQualifiedName())
memberName = q->unqualified_name;
2010-08-05 12:19:07 +02:00
addVirtualMethod(candidates, memberName, argumentCount);
2010-08-03 17:34:51 +02:00
}
}
} else if (IdExpressionAST *idExpr = ast->base_expression->asIdExpression()) {
if (const Name *name = idExpr->name->name) {
if (maybeVirtualMethod(name)) {
NameAST *exprName = idExpr->name;
if (QualifiedNameAST *q = exprName->asQualifiedName())
exprName = q->unqualified_name;
2010-08-05 12:19:07 +02:00
const QList<LookupItem> candidates = typeOfExpression(textOf(idExpr), enclosingScope(),
TypeOfExpression::Preprocess);
2010-08-03 17:34:51 +02:00
2010-08-05 12:19:07 +02:00
addVirtualMethod(candidates, exprName, argumentCount);
2010-08-03 17:34:51 +02:00
}
}
}
accept(ast->expression_list);
}
return false;
}
QByteArray CheckSymbols::textOf(AST *ast) const
{
const Token start = tokenAt(ast->firstToken());
const Token end = tokenAt(ast->lastToken() - 1);
const QByteArray text = _doc->source().mid(start.begin(), end.end() - start.begin());
return text;
}
2010-07-13 15:25:05 +02:00
void CheckSymbols::checkNamespace(NameAST *name)
{
if (! name)
return;
2010-02-06 14:32:25 +01:00
unsigned line, column;
getTokenStartPosition(name->firstToken(), &line, &column);
2010-02-06 14:32:25 +01:00
2010-08-05 12:19:07 +02:00
if (ClassOrNamespace *b = _context.lookupType(name->name, enclosingScope())) {
foreach (Symbol *s, b->symbols()) {
if (s->isNamespace())
return;
2010-02-06 14:32:25 +01:00
}
}
const unsigned length = tokenAt(name->lastToken() - 1).end() - tokenAt(name->firstToken()).begin();
warning(line, column, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a namespace-name"), length);
2010-02-06 14:32:25 +01:00
}
2010-05-25 14:53:21 +02:00
2010-08-09 17:06:00 +02:00
bool CheckSymbols::hasVirtualDestructor(Class *klass) const
{
if (! klass)
return false;
const Identifier *id = klass->identifier();
if (! id)
return false;
for (Symbol *s = klass->members()->lookat(id); s; s = s->next()) {
if (! s->name())
continue;
else if (s->name()->isDestructorNameId()) {
if (Function *funTy = s->type()->asFunctionType()) {
if (funTy->isVirtual() && id->isEqualTo(s->identifier()))
return true;
}
}
}
return false;
}
bool CheckSymbols::hasVirtualDestructor(ClassOrNamespace *binding) const
{
QSet<ClassOrNamespace *> processed;
QList<ClassOrNamespace *> todo;
todo.append(binding);
while (! todo.isEmpty()) {
ClassOrNamespace *b = todo.takeFirst();
if (b && ! processed.contains(b)) {
processed.insert(b);
foreach (Symbol *s, b->symbols()) {
if (Class *k = s->asClass()) {
if (hasVirtualDestructor(k))
return true;
}
}
todo += b->usings();
}
}
return false;
}
2010-08-03 13:01:24 +02:00
void CheckSymbols::checkName(NameAST *ast, Scope *scope)
2010-05-25 14:53:21 +02:00
{
if (ast && ast->name) {
2010-08-03 13:01:24 +02:00
if (! scope)
2010-08-05 12:19:07 +02:00
scope = enclosingScope();
2010-08-09 17:06:00 +02:00
if (ast->asDestructorName() != 0 && scope->isClassScope()) {
Class *klass = scope->owner()->asClass();
if (hasVirtualDestructor(_context.lookupType(klass)))
addUse(ast, Use::VirtualMethod);
} else if (maybeType(ast->name)) {
2010-08-05 12:19:07 +02:00
const QList<LookupItem> candidates = _context.lookup(ast->name, scope);
addType(candidates, ast);
} else if (maybeMember(ast->name)) {
const QList<LookupItem> candidates = _context.lookup(ast->name, scope);
addClassMember(candidates, ast);
2010-05-25 14:53:21 +02:00
}
}
2010-05-25 17:49:29 +02:00
}
2010-05-25 14:53:21 +02:00
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(SimpleNameAST *ast)
2010-05-25 17:49:29 +02:00
{
checkName(ast);
2010-05-25 14:53:21 +02:00
return true;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(TemplateIdAST *ast)
2010-05-25 14:53:21 +02:00
{
2010-05-25 17:49:29 +02:00
checkName(ast);
2010-05-25 14:53:21 +02:00
return true;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(DestructorNameAST *ast)
2010-05-25 14:53:21 +02:00
{
checkName(ast);
2010-05-25 14:53:21 +02:00
return true;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(QualifiedNameAST *ast)
2010-05-25 14:53:21 +02:00
{
if (ast->name) {
ClassOrNamespace *b = 0;
if (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list) {
NestedNameSpecifierAST *nested_name_specifier = it->value;
2010-06-04 10:48:24 +02:00
if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) { // ### remove shadowing
2010-05-25 14:53:21 +02:00
if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId())
accept(template_id->template_argument_list);
2010-06-04 10:48:24 +02:00
const Name *name = class_or_namespace_name->name;
2010-08-05 12:19:07 +02:00
b = _context.lookupType(name, enclosingScope());
addType(b, class_or_namespace_name);
2010-05-25 14:53:21 +02:00
2010-06-04 10:48:24 +02:00
for (it = it->next; b && it; it = it->next) {
NestedNameSpecifierAST *nested_name_specifier = it->value;
2010-05-25 14:53:21 +02:00
2010-06-04 10:48:24 +02:00
if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId())
accept(template_id->template_argument_list);
2010-06-04 10:48:24 +02:00
b = b->findType(class_or_namespace_name->name);
2010-08-05 12:19:07 +02:00
addType(b, class_or_namespace_name);
2010-06-04 10:48:24 +02:00
}
2010-05-25 14:53:21 +02:00
}
}
}
2010-08-05 12:19:07 +02:00
if (b && ast->unqualified_name) {
2010-08-09 17:06:00 +02:00
if (ast->unqualified_name->asDestructorName() != 0) {
if (hasVirtualDestructor(b))
addUse(ast->unqualified_name, Use::VirtualMethod);
} else {
addType(b->find(ast->unqualified_name->name), ast->unqualified_name);
}
2010-08-05 12:19:07 +02:00
}
2010-05-25 14:53:21 +02:00
}
return false;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
{
2010-08-05 12:19:07 +02:00
addUse(ast->name, Use::Type);
accept(ast->type_id);
return false;
}
2010-07-13 15:25:05 +02:00
bool CheckSymbols::visit(TemplateTypeParameterAST *ast)
{
2010-08-05 12:19:07 +02:00
accept(ast->template_parameter_list);
addUse(ast->name, Use::Type);
accept(ast->type_id);
return false;
}
2010-08-03 13:01:24 +02:00
bool CheckSymbols::visit(MemInitializerAST *ast)
{
2010-08-05 12:19:07 +02:00
if (FunctionDefinitionAST *enclosingFunction = enclosingFunctionDefinition()) {
if (ast->name && enclosingFunction->symbol) {
if (ClassOrNamespace *binding = _context.lookupType(enclosingFunction->symbol)) {
foreach (Symbol *s, binding->symbols()) {
if (Class *klass = s->asClass()){
checkName(ast->name, klass->members());
break;
}
2010-08-03 13:01:24 +02:00
}
}
}
2010-08-05 12:19:07 +02:00
accept(ast->expression_list);
}
2010-08-03 13:01:24 +02:00
return false;
}
2010-07-15 16:03:48 +02:00
bool CheckSymbols::visit(FunctionDefinitionAST *ast)
{
AST *thisFunction = _astStack.takeLast();
accept(ast->decl_specifier_list);
_astStack.append(thisFunction);
2010-08-03 17:34:51 +02:00
2010-08-03 18:08:13 +02:00
if (ast->declarator && ast->symbol && ! ast->symbol->isGenerated()) {
2010-08-03 17:34:51 +02:00
Function *fun = ast->symbol;
if (NameAST *declId = declaratorId(ast->declarator)) {
if (QualifiedNameAST *q = declId->asQualifiedName())
declId = q->unqualified_name;
if (fun->isVirtual()) {
2010-08-05 12:19:07 +02:00
addUse(declId, Use::VirtualMethod);
2010-08-03 17:34:51 +02:00
} else if (maybeVirtualMethod(fun->name())) {
2010-08-05 12:19:07 +02:00
addVirtualMethod(_context.lookup(fun->name(), fun->scope()), declId, fun->argumentCount());
2010-08-03 17:34:51 +02:00
}
}
}
accept(ast->declarator);
accept(ast->ctor_initializer);
accept(ast->function_body);
2010-07-15 16:03:48 +02:00
const LocalSymbols locals(_doc, ast);
QList<SemanticInfo::Use> uses;
foreach (uses, locals.uses) {
foreach (const SemanticInfo::Use &u, uses)
2010-08-05 12:19:07 +02:00
addUse(u);
2010-07-15 16:03:48 +02:00
}
flush();
2010-07-15 16:03:48 +02:00
return false;
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::addUse(NameAST *ast, Use::Kind kind)
{
if (! ast)
return;
if (QualifiedNameAST *q = ast->asQualifiedName())
ast = q->unqualified_name;
if (! ast)
return; // nothing to do
else if (ast->asOperatorFunctionId() != 0 || ast->asConversionFunctionId() != 0)
return; // nothing to do
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
startToken = dtor->identifier_token;
const Token &tok = tokenAt(startToken);
if (tok.generated())
return;
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length();
const Use use(line, column, length, kind);
addUse(use);
}
void CheckSymbols::addUse(const Use &use)
{
2010-08-05 12:19:07 +02:00
if (! enclosingFunctionDefinition()) {
if (_usages.size() >= 50) {
2010-07-15 16:03:48 +02:00
if (_flushRequested && use.line != _flushLine)
flush();
else if (! _flushRequested) {
_flushRequested = true;
_flushLine = use.line;
}
2010-07-15 13:41:51 +02:00
}
}
2010-07-06 12:42:55 +02:00
_usages.append(use);
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::addType(ClassOrNamespace *b, NameAST *ast)
2010-05-25 14:53:21 +02:00
{
if (! b)
return;
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
startToken = dtor->identifier_token;
const Token &tok = tokenAt(startToken);
2010-05-25 14:53:21 +02:00
if (tok.generated())
return;
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
2010-05-25 14:53:21 +02:00
const unsigned length = tok.length();
2010-08-05 12:19:07 +02:00
const Use use(line, column, length, Use::Type);
addUse(use);
2010-05-25 14:53:21 +02:00
//qDebug() << "added use" << oo(ast->name) << line << column << length;
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::addType(const QList<LookupItem> &candidates, NameAST *ast)
2010-05-25 14:53:21 +02:00
{
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
startToken = dtor->identifier_token;
const Token &tok = tokenAt(startToken);
2010-05-25 14:53:21 +02:00
if (tok.generated())
return;
foreach (const LookupItem &r, candidates) {
Symbol *c = r.declaration();
2010-05-25 17:49:29 +02:00
if (c->isUsingDeclaration()) // skip using declarations...
continue;
else if (c->isUsingNamespaceDirective()) // ... and using namespace directives.
continue;
else if (c->isTypedef() || c->isNamespace() ||
c->isClass() || c->isEnum() ||
c->isForwardClassDeclaration() || c->isTypenameArgument()) {
2010-08-09 15:40:14 +02:00
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length();
2010-08-05 12:19:07 +02:00
const Use use(line, column, length, Use::Type);
addUse(use);
2010-05-25 14:53:21 +02:00
//qDebug() << "added use" << oo(ast->name) << line << column << length;
break;
}
2010-07-15 16:03:48 +02:00
}
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::addClassMember(const QList<LookupItem> &candidates, NameAST *ast)
2010-07-15 16:03:48 +02:00
{
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
startToken = dtor->identifier_token;
const Token &tok = tokenAt(startToken);
if (tok.generated())
return;
foreach (const LookupItem &r, candidates) {
Symbol *c = r.declaration();
if (! c)
continue;
else if (! c->isDeclaration())
2010-08-03 18:37:04 +02:00
return;
else if (! (c->enclosingSymbol() && c->enclosingSymbol()->isClass()))
return; // shadowed
else if (c->isTypedef() || c->type()->isFunctionType())
return; // shadowed
2010-07-15 16:03:48 +02:00
2010-08-09 15:40:14 +02:00
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length();
2010-07-15 16:03:48 +02:00
const Use use(line, column, length, Use::Field);
2010-08-05 12:19:07 +02:00
addUse(use);
2010-08-03 17:34:51 +02:00
break;
}
}
2010-08-05 12:19:07 +02:00
void CheckSymbols::addVirtualMethod(const QList<LookupItem> &candidates, NameAST *ast, unsigned argumentCount)
2010-08-03 17:34:51 +02:00
{
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
startToken = dtor->identifier_token;
const Token &tok = tokenAt(startToken);
if (tok.generated())
return;
foreach (const LookupItem &r, candidates) {
Symbol *c = r.declaration();
if (! c)
continue;
Function *funTy = r.type()->asFunctionType();
if (! funTy)
continue;
if (! funTy->isVirtual())
continue;
else if (argumentCount < funTy->minimumArgumentCount())
continue;
2010-08-09 15:40:14 +02:00
else if (argumentCount > funTy->argumentCount()) {
if (! funTy->isVariadic())
continue;
}
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length();
2010-08-03 17:34:51 +02:00
const Use use(line, column, length, Use::VirtualMethod);
2010-08-05 12:19:07 +02:00
addUse(use);
2010-08-03 17:34:51 +02:00
break;
2010-05-25 14:53:21 +02:00
}
}
2010-08-05 12:19:07 +02:00
NameAST *CheckSymbols::declaratorId(DeclaratorAST *ast) const
{
2010-08-05 12:19:07 +02:00
if (ast && ast->core_declarator) {
if (NestedDeclaratorAST *nested = ast->core_declarator->asNestedDeclarator())
return declaratorId(nested->declarator);
else if (DeclaratorIdAST *declId = ast->core_declarator->asDeclaratorId()) {
return declId->name;
}
}
2010-08-05 12:19:07 +02:00
return 0;
}
2010-08-05 12:19:07 +02:00
bool CheckSymbols::maybeType(const Name *name) const
{
2010-08-05 12:19:07 +02:00
if (name) {
if (const Identifier *ident = name->identifier()) {
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
if (_potentialTypes.contains(id))
return true;
}
}
2010-08-05 12:19:07 +02:00
return false;
}
2010-07-06 12:42:55 +02:00
2010-08-05 12:19:07 +02:00
bool CheckSymbols::maybeMember(const Name *name) const
2010-08-03 17:34:51 +02:00
{
2010-08-05 12:19:07 +02:00
if (name) {
if (const Identifier *ident = name->identifier()) {
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
if (_potentialMembers.contains(id))
return true;
2010-08-03 17:34:51 +02:00
}
}
2010-08-05 12:19:07 +02:00
return false;
2010-08-03 17:34:51 +02:00
}
bool CheckSymbols::maybeVirtualMethod(const Name *name) const
{
2010-08-05 12:19:07 +02:00
if (name) {
if (const Identifier *ident = name->identifier()) {
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
if (_potentialVirtualMethods.contains(id))
return true;
}
2010-08-03 17:34:51 +02:00
}
return false;
}
2010-07-13 15:25:05 +02:00
void CheckSymbols::flush()
2010-07-06 12:42:55 +02:00
{
2010-07-15 13:41:51 +02:00
_flushRequested = false;
_flushLine = 0;
if (_usages.isEmpty())
2010-07-06 12:42:55 +02:00
return;
reportResults(_usages);
_usages.clear();
2010-07-06 12:42:55 +02:00
}