diff -r 000000000000 -r 876b1a06bc25 tools/icheck/parser/src/libs/cplusplus/CheckUndefinedSymbols.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/icheck/parser/src/libs/cplusplus/CheckUndefinedSymbols.cpp Wed Aug 25 15:49:42 2010 +0300 @@ -0,0 +1,553 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "CheckUndefinedSymbols.h" +#include "Overview.h" + +#include +#include +#include +#include +#include +#include + +using namespace CPlusPlus; + + +CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc) + : ASTVisitor(doc->translationUnit()), _doc(doc) +{ } + +CheckUndefinedSymbols::~CheckUndefinedSymbols() +{ } + +void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding) +{ + _globalNamespaceBinding = globalNamespaceBinding; + _types.clear(); + _protocols.clear(); + + if (_globalNamespaceBinding) { + QSet processed; + buildTypeMap(_globalNamespaceBinding.data(), &processed); + } +} + +void CheckUndefinedSymbols::operator()(AST *ast) +{ accept(ast); } + +QByteArray CheckUndefinedSymbols::templateParameterName(NameAST *ast) const +{ + if (ast && ast->name) { + if (const Identifier *id = ast->name->identifier()) + return QByteArray::fromRawData(id->chars(), id->size()); + } + + return QByteArray(); +} + +QByteArray CheckUndefinedSymbols::templateParameterName(DeclarationAST *ast) const +{ + if (ast) { + if (TypenameTypeParameterAST *d = ast->asTypenameTypeParameter()) + return templateParameterName(d->name); + else if (TemplateTypeParameterAST *d = ast->asTemplateTypeParameter()) + return templateParameterName(d->name); + else if (ParameterDeclarationAST *d = ast->asParameterDeclaration()) { + if (d->symbol) { + if (const Identifier *id = d->symbol->identifier()) + return QByteArray::fromRawData(id->chars(), id->size()); + } + } + } + return QByteArray(); +} + +bool CheckUndefinedSymbols::isType(const QByteArray &name) const +{ + for (int i = _compoundStatementStack.size() - 1; i != -1; --i) { + Scope *members = _compoundStatementStack.at(i)->symbol->members(); + + for (unsigned m = 0; m < members->symbolCount(); ++m) { + Symbol *member = members->symbolAt(m); + + if (member->isTypedef() && member->isDeclaration()) { + if (const Identifier *id = member->identifier()) { + if (name == id->chars()) + return true; + } + } + } + } + + for (int i = _templateDeclarationStack.size() - 1; i != - 1; --i) { + TemplateDeclarationAST *templateDeclaration = _templateDeclarationStack.at(i); + + for (DeclarationListAST *it = templateDeclaration->template_parameter_list; it; it = it->next) { + DeclarationAST *templateParameter = it->value; + + if (templateParameterName(templateParameter) == name) + return true; + } + } + + return _types.contains(name); +} + +bool CheckUndefinedSymbols::isType(const Identifier *id) const +{ + if (! id) + return false; + + return isType(QByteArray::fromRawData(id->chars(), id->size())); +} + +void CheckUndefinedSymbols::addType(const Name *name) +{ + if (! name) + return; + + if (const Identifier *id = name->identifier()) + _types.insert(QByteArray(id->chars(), id->size())); +} + +void CheckUndefinedSymbols::addProtocol(const Name *name) +{ + if (!name) + return; + + if (const Identifier *id = name->identifier()) + _protocols.insert(QByteArray(id->chars(), id->size())); +} + +bool CheckUndefinedSymbols::isProtocol(const QByteArray &name) const +{ + return _protocols.contains(name); +} + +void CheckUndefinedSymbols::buildTypeMap(Class *klass) +{ + addType(klass->name()); + + for (unsigned i = 0; i < klass->memberCount(); ++i) { + buildMemberTypeMap(klass->memberAt(i)); + } +} + +void CheckUndefinedSymbols::buildMemberTypeMap(Symbol *member) +{ + if (member == 0) + return; + + if (Class *klass = member->asClass()) { + buildTypeMap(klass); + } else if (Enum *e = member->asEnum()) { + addType(e->name()); + } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) { + addType(fwd->name()); + } else if (Declaration *decl = member->asDeclaration()) { + if (decl->isTypedef()) + addType(decl->name()); + } +} + +void CheckUndefinedSymbols::buildTypeMap(NamespaceBinding *binding, QSet *processed) +{ + if (! processed->contains(binding)) { + processed->insert(binding); + + if (const Identifier *id = binding->identifier()) { + _namespaceNames.insert(QByteArray(id->chars(), id->size())); + } + + foreach (Namespace *ns, binding->symbols) { + for (unsigned i = 0; i < ns->memberCount(); ++i) { + Symbol *member = ns->memberAt(i); + + if (Class *klass = member->asClass()) { + buildTypeMap(klass); + } else if (Enum *e = member->asEnum()) { + addType(e->name()); + } else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) { + addType(fwd->name()); + } else if (Declaration *decl = member->asDeclaration()) { + if (decl->isTypedef()) + addType(decl->name()); + } else if (ObjCForwardClassDeclaration *fKlass = member->asObjCForwardClassDeclaration()) { + addType(fKlass->name()); + } else if (ObjCClass *klass = member->asObjCClass()) { + addType(klass->name()); + + for (unsigned i = 0; i < klass->memberCount(); ++i) + buildMemberTypeMap(klass->memberAt(i)); + } else if (ObjCForwardProtocolDeclaration *fProto = member->asObjCForwardProtocolDeclaration()) { + addProtocol(fProto->name()); + } else if (ObjCProtocol *proto = member->asObjCProtocol()) { + addProtocol(proto->name()); + + for (unsigned i = 0; i < proto->memberCount(); ++i) + buildMemberTypeMap(proto->memberAt(i)); + } + } + } + + foreach (NamespaceBinding *childBinding, binding->children) { + buildTypeMap(childBinding, processed); + } + } +} + +FunctionDeclaratorAST *CheckUndefinedSymbols::currentFunctionDeclarator() const +{ + if (_functionDeclaratorStack.isEmpty()) + return 0; + + return _functionDeclaratorStack.last(); +} + +CompoundStatementAST *CheckUndefinedSymbols::compoundStatement() const +{ + if (_compoundStatementStack.isEmpty()) + return 0; + + return _compoundStatementStack.last(); +} + +bool CheckUndefinedSymbols::visit(FunctionDeclaratorAST *ast) +{ + _functionDeclaratorStack.append(ast); + return true; +} + +void CheckUndefinedSymbols::endVisit(FunctionDeclaratorAST *) +{ + _functionDeclaratorStack.removeLast(); +} + +bool CheckUndefinedSymbols::visit(TypeofSpecifierAST *) +{ + return false; +} + +bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast) +{ + if (ast->name) { + if (! ast->name->name) { + unsigned line, col; + getTokenStartPosition(ast->firstToken(), &line, &col); + // qWarning() << _doc->fileName() << line << col; + } else if (const Identifier *id = ast->name->name->identifier()) { + if (! isType(id)) { + if (FunctionDeclaratorAST *functionDeclarator = currentFunctionDeclarator()) { + if (functionDeclarator->as_cpp_initializer) + return true; + } + + Overview oo; + translationUnit()->warning(ast->firstToken(), "`%s' is not a type name", + qPrintable(oo(ast->name->name))); + } + } + } + return true; +} + +bool CheckUndefinedSymbols::visit(TemplateDeclarationAST *ast) +{ + _templateDeclarationStack.append(ast); + return true; +} + +void CheckUndefinedSymbols::endVisit(TemplateDeclarationAST *) +{ + _templateDeclarationStack.removeLast(); +} + +bool CheckUndefinedSymbols::visit(ClassSpecifierAST *ast) +{ + bool hasQ_OBJECT_CHECK = false; + + if (ast->symbol) { + Class *klass = ast->symbol->asClass(); + + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *symbol = klass->memberAt(i); + + if (symbol->name() && symbol->name()->isNameId()) { + const NameId *nameId = symbol->name()->asNameId(); + + if (! qstrcmp(nameId->identifier()->chars(), "qt_check_for_QOBJECT_macro")) { + hasQ_OBJECT_CHECK = true; + break; + } + } + } + } + + _qobjectStack.append(hasQ_OBJECT_CHECK); + + return true; +} + +void CheckUndefinedSymbols::endVisit(ClassSpecifierAST *) +{ _qobjectStack.removeLast(); } + +bool CheckUndefinedSymbols::qobjectCheck() const +{ + if (_qobjectStack.isEmpty()) + return false; + + return _qobjectStack.last(); +} + +bool CheckUndefinedSymbols::visit(FunctionDefinitionAST *ast) +{ + if (ast->symbol) { + Function *fun = ast->symbol->asFunction(); + if ((fun->isSignal() || fun->isSlot()) && ! qobjectCheck()) { + translationUnit()->warning(ast->firstToken(), + "you forgot the Q_OBJECT macro"); + } + } + return true; +} + +void CheckUndefinedSymbols::endVisit(FunctionDefinitionAST *) +{ } + +bool CheckUndefinedSymbols::visit(CompoundStatementAST *ast) +{ + _compoundStatementStack.append(ast); + return true; +} + +void CheckUndefinedSymbols::endVisit(CompoundStatementAST *) +{ + _compoundStatementStack.removeLast(); +} + +bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast) +{ + const bool check = qobjectCheck(); + for (List *it = ast->symbols; it; it = it->next) { + Declaration *decl = it->value; + + if (Function *fun = decl->type()->asFunctionType()) { + if ((fun->isSignal() || fun->isSlot()) && ! check) { + translationUnit()->warning(ast->firstToken(), + "you forgot the Q_OBJECT macro"); + } + } + } + return true; +} + +bool CheckUndefinedSymbols::visit(BaseSpecifierAST *base) +{ + if (NameAST *nameAST = base->name) { + bool resolvedBaseClassName = false; + + if (const Name *name = nameAST->name) { + const Identifier *id = name->identifier(); + const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size()); + if (isType(spell)) + resolvedBaseClassName = true; + } + + if (! resolvedBaseClassName) + translationUnit()->warning(nameAST->firstToken(), "expected class-name"); + } + + return true; +} + +bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast) +{ + if (ast->symbol && ast->symbol->name() && _globalNamespaceBinding) { + const Location loc = Location(ast->symbol); + + NamespaceBinding *binding = _globalNamespaceBinding.data(); + + if (Scope *enclosingNamespaceScope = ast->symbol->enclosingNamespaceScope()) + binding = NamespaceBinding::find(enclosingNamespaceScope->owner()->asNamespace(), binding); + + if (! binding || ! binding->resolveNamespace(loc, ast->symbol->name())) { + translationUnit()->warning(ast->name->firstToken(), + "expected a namespace"); + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast) +{ + if (ast->name) { + const QualifiedNameId *q = ast->name->asQualifiedNameId(); + for (unsigned i = 0; i < q->nameCount() - 1; ++i) { + const Name *name = q->nameAt(i); + if (const Identifier *id = name->identifier()) { + const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size()); + if (! (_namespaceNames.contains(spell) || isType(id))) { + translationUnit()->warning(ast->firstToken(), + "`%s' is not a namespace or class name", + spell.constData()); + } + } + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(CastExpressionAST *ast) +{ + if (ast->lparen_token && ast->type_id && ast->rparen_token && ast->expression) { + if (TypeIdAST *cast_type_id = ast->type_id->asTypeId()) { + SpecifierListAST *type_specifier = cast_type_id->type_specifier_list; + if (! cast_type_id->declarator && type_specifier && ! type_specifier->next && + type_specifier->value->asNamedTypeSpecifier() && ast->expression && + ast->expression->asUnaryExpression()) { + // this ast node is ambigious, e.g. + // (a) + b + // it can be parsed as + // ((a) + b) + // or + // (a) (+b) + accept(ast->expression); + return false; + } + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(SizeofExpressionAST *ast) +{ + if (ast->lparen_token && ast->expression && ast->rparen_token) { + if (TypeIdAST *type_id = ast->expression->asTypeId()) { + SpecifierListAST *type_specifier = type_id->type_specifier_list; + if (! type_id->declarator && type_specifier && ! type_specifier->next && + type_specifier->value->asNamedTypeSpecifier()) { + // this sizeof expression is ambiguos, e.g. + // sizeof (a) + // `a' can be a typeid or a nested-expression. + return false; + } else if (type_id->declarator + && type_id->declarator->postfix_declarator_list + && ! type_id->declarator->postfix_declarator_list->next + && type_id->declarator->postfix_declarator_list->value->asArrayDeclarator() != 0) { + // this sizeof expression is ambiguos, e.g. + // sizeof(a[10]) + // `a' can be a typeid or an expression. + return false; + } + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(ObjCClassDeclarationAST *ast) +{ + if (NameAST *nameAST = ast->superclass) { + bool resolvedSuperClassName = false; + + if (const Name *name = nameAST->name) { + const Identifier *id = name->identifier(); + const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size()); + if (isType(spell)) + resolvedSuperClassName = true; + } + + if (! resolvedSuperClassName) { + translationUnit()->warning(nameAST->firstToken(), + "expected class-name after ':' token"); + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(ObjCProtocolRefsAST *ast) +{ + for (ObjCIdentifierListAST *iter = ast->identifier_list; iter; iter = iter->next) { + if (NameAST *nameAST = iter->value) { + bool resolvedProtocolName = false; + + if (const Name *name = nameAST->name) { + const Identifier *id = name->identifier(); + const QByteArray spell = QByteArray::fromRawData(id->chars(), id->size()); + if (isProtocol(spell)) + resolvedProtocolName = true; + } + + if (!resolvedProtocolName) { + char after; + + if (iter == ast->identifier_list) + after = '<'; + else + after = ','; + + translationUnit()->warning(nameAST->firstToken(), "expected protocol name after '%c' token", after); + } + } + } + + return false; +} + +bool CheckUndefinedSymbols::visit(ObjCPropertyDeclarationAST *ast) +{ + for (List *iter = ast->symbols; iter; iter = iter->next) { + if (/*Name *getterName = */ iter->value->getterName()) { + // FIXME: resolve the symbol for the name, and check its signature. + } + + if (/*Name *setterName = */ iter->value->setterName()) { + // FIXME: resolve the symbol for the name, and check its signature. + } + } + + return false; +}