diff --git a/src/libs/cplusplus/CheckUndefinedSymbols.cpp b/src/libs/cplusplus/CheckUndefinedSymbols.cpp index c972e89abb6cd087e3e5eb274019d7141c2fc2bb..77d18f371bbd8c2ad7c781b82a9a1e86cdef7e65 100644 --- a/src/libs/cplusplus/CheckUndefinedSymbols.cpp +++ b/src/libs/cplusplus/CheckUndefinedSymbols.cpp @@ -37,546 +37,97 @@ #include <Scope.h> #include <AST.h> -using namespace CPlusPlus; - - -CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc) - : ASTVisitor(doc->translationUnit()), _doc(doc) -{ } - -CheckUndefinedSymbols::~CheckUndefinedSymbols() -{ } +#include <QCoreApplication> +#include <QDebug> -void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding) -{ - _globalNamespaceBinding = globalNamespaceBinding; - _types.clear(); - _protocols.clear(); - - if (_globalNamespaceBinding) { - QSet<NamespaceBinding *> 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<NamespaceBinding *> *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 (NamespaceAlias *alias = member->asNamespaceAlias()) { - addType(alias->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(); -} +using namespace CPlusPlus; -CompoundStatementAST *CheckUndefinedSymbols::compoundStatement() const +CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc, const Snapshot &snapshot) + : ASTVisitor(doc->translationUnit()), _context(doc, snapshot) { - if (_compoundStatementStack.isEmpty()) - return 0; - - return _compoundStatementStack.last(); + _fileName = doc->fileName(); } -bool CheckUndefinedSymbols::visit(FunctionDeclaratorAST *ast) -{ - _functionDeclaratorStack.append(ast); - return true; -} +CheckUndefinedSymbols::~CheckUndefinedSymbols() +{ } -void CheckUndefinedSymbols::endVisit(FunctionDeclaratorAST *) +QList<Document::DiagnosticMessage> CheckUndefinedSymbols::operator()(AST *ast) { - _functionDeclaratorStack.removeLast(); + _diagnosticMessages.clear(); + accept(ast); + return _diagnosticMessages; } -bool CheckUndefinedSymbols::visit(TypeofSpecifierAST *) +bool CheckUndefinedSymbols::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; } -bool CheckUndefinedSymbols::visit(NamespaceAliasDefinitionAST *ast) -{ - if (const Identifier *id = identifier(ast->namespace_name_token)) - _types.insert(QByteArray(id->chars(), id->size())); - - return true; -} - -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 CheckUndefinedSymbols::warning(AST *ast, const QString &text) { - 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; -} + const Token &firstToken = tokenAt(ast->firstToken()); + const Token &lastToken = tokenAt(ast->lastToken() - 1); -void CheckUndefinedSymbols::endVisit(ClassSpecifierAST *) -{ _qobjectStack.removeLast(); } + const unsigned length = lastToken.end() - firstToken.begin(); + unsigned line = 1, column = 1; + getTokenStartPosition(ast->firstToken(), &line, &column); -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; + warning(line, column, text, length); + return false; } -void CheckUndefinedSymbols::endVisit(CompoundStatementAST *) +bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast) { - _compoundStatementStack.removeLast(); + checkNamespace(ast->name); + return false; } bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast) { - const bool check = qobjectCheck(); - for (List<Declaration *> *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) +bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *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; -} + unsigned line, column; + getTokenStartPosition(ast->firstToken(), &line, &column); -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; -} + Scope *enclosingScope = _context.thisDocument()->scopeAt(line, column); + const QList<Symbol *> candidates = _context.lookup(ast->name->name, enclosingScope); -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"); + Symbol *ty = 0; + foreach (Symbol *c, candidates) { + if (c->isTypedef() || c->isClass() || c->isEnum() + || c->isForwardClassDeclaration() || c->isTypenameArgument()) + ty = c; } - } - - return true; -} - -bool CheckUndefinedSymbols::visit(ObjCProtocolRefsAST *ast) -{ - for (NameListAST *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); - } - } + if (! ty) + warning(ast->name, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a type-name")); } return false; } -bool CheckUndefinedSymbols::visit(ObjCPropertyDeclarationAST *ast) +void CheckUndefinedSymbols::checkNamespace(NameAST *name) { - for (List<ObjCPropertyDeclaration *> *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; -} + if (! name) + return; -bool CheckUndefinedSymbols::visit(QtEnumDeclarationAST *ast) -{ - for (NameListAST *iter = ast->enumerator_list; iter; iter = iter->next) { - if (! iter->value) - continue; + unsigned line, column; + getTokenStartPosition(name->firstToken(), &line, &column); - if (SimpleNameAST *enumName = iter->value->asSimpleName()) { - if (enumName->name) { - const Identifier *enumId = enumName->name->identifier(); - if (!isType(enumId))// ### we're only checking if the enum name is known as a type name, not as an *enum*. - translationUnit()->warning(enumName->firstToken(), - "unknown enum '%s'", - enumId->chars()); - } + Scope *enclosingScope = _context.thisDocument()->scopeAt(line, column); + if (ClassOrNamespace *b = _context.lookupType(name->name, enclosingScope)) { + foreach (Symbol *s, b->symbols()) { + if (s->isNamespace()) + return; } } - return false; -} -bool CheckUndefinedSymbols::visit(QtFlagsDeclarationAST *) -{ - // ### TODO - return false; -} - -bool CheckUndefinedSymbols::visit(QtPropertyDeclarationAST *) -{ - // ### TODO - return false; + const unsigned length = tokenAt(name->lastToken() - 1).end() - tokenAt(name->firstToken()).begin(); + warning(line, column, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a namespace-name"), length); } diff --git a/src/libs/cplusplus/CheckUndefinedSymbols.h b/src/libs/cplusplus/CheckUndefinedSymbols.h index 00b8d4d1d7b929db086ee81d450010f855b735af..4d5172fa748c548392e04868db8d67b8d0822bbd 100644 --- a/src/libs/cplusplus/CheckUndefinedSymbols.h +++ b/src/libs/cplusplus/CheckUndefinedSymbols.h @@ -31,88 +31,35 @@ #define CHECKUNDEFINEDSYMBOLS_H #include "CppDocument.h" -#include "CppBindings.h" - +#include "LookupContext.h" #include <ASTVisitor.h> -#include <QtCore/QSet> -#include <QtCore/QByteArray> namespace CPlusPlus { class CPLUSPLUS_EXPORT CheckUndefinedSymbols: protected ASTVisitor { public: - CheckUndefinedSymbols(Document::Ptr doc); + CheckUndefinedSymbols(Document::Ptr doc, const Snapshot &snapshot); virtual ~CheckUndefinedSymbols(); - void setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding); - - void operator()(AST *ast); + QList<Document::DiagnosticMessage> operator()(AST *ast); protected: using ASTVisitor::visit; - bool isType(const Identifier *id) const; - bool isType(const QByteArray &name) const; - - void addType(const Name *name); - void buildTypeMap(Class *klass); - void buildMemberTypeMap(Symbol *member); - void buildTypeMap(NamespaceBinding *binding, QSet<NamespaceBinding *> *processed); - void addProtocol(const Name *name); - bool isProtocol(const QByteArray &name) const; - - FunctionDeclaratorAST *currentFunctionDeclarator() const; - CompoundStatementAST *compoundStatement() const; - bool qobjectCheck() const; - - QByteArray templateParameterName(NameAST *ast) const; - QByteArray templateParameterName(DeclarationAST *ast) const; - - virtual bool visit(FunctionDeclaratorAST *ast); - virtual void endVisit(FunctionDeclaratorAST *ast); - - virtual bool visit(TypeofSpecifierAST *ast); - virtual bool visit(NamedTypeSpecifierAST *ast); - - virtual bool visit(TemplateDeclarationAST *ast); - virtual void endVisit(TemplateDeclarationAST *); - - virtual bool visit(ClassSpecifierAST *ast); - virtual void endVisit(ClassSpecifierAST *); - - virtual bool visit(FunctionDefinitionAST *ast); - virtual void endVisit(FunctionDefinitionAST *ast); - - virtual bool visit(CompoundStatementAST *ast); - virtual void endVisit(CompoundStatementAST *ast); - - virtual bool visit(SimpleDeclarationAST *ast); - virtual bool visit(BaseSpecifierAST *base); - virtual bool visit(UsingDirectiveAST *ast); - virtual bool visit(QualifiedNameAST *ast); - virtual bool visit(CastExpressionAST *ast); - virtual bool visit(SizeofExpressionAST *ast); - virtual bool visit(NamespaceAliasDefinitionAST *ast); + bool warning(unsigned line, unsigned column, const QString &text, unsigned length = 0); + bool warning(AST *ast, const QString &text); - virtual bool visit(ObjCClassDeclarationAST *ast); - virtual bool visit(ObjCProtocolRefsAST *ast); - virtual bool visit(ObjCPropertyDeclarationAST *ast); + void checkNamespace(NameAST *name); - virtual bool visit(QtEnumDeclarationAST *ast); - virtual bool visit(QtFlagsDeclarationAST *ast); - virtual bool visit(QtPropertyDeclarationAST *ast); + virtual bool visit(UsingDirectiveAST *); + virtual bool visit(SimpleDeclarationAST *); + virtual bool visit(NamedTypeSpecifierAST *); private: - Document::Ptr _doc; - NamespaceBindingPtr _globalNamespaceBinding; - QList<bool> _qobjectStack; - QList<FunctionDeclaratorAST *> _functionDeclaratorStack; - QList<TemplateDeclarationAST *> _templateDeclarationStack; - QList<CompoundStatementAST *> _compoundStatementStack; - QSet<QByteArray> _types; - QSet<QByteArray> _protocols; - QSet<QByteArray> _namespaceNames; + LookupContext _context; + QString _fileName; + QList<Document::DiagnosticMessage> _diagnosticMessages; }; } // end of namespace CPlusPlus