forked from qt-creator/qt-creator
		
	New implementation of CheckUndefinedSymbols.
This commit is contained in:
		@@ -37,546 +37,97 @@
 | 
			
		||||
#include <Scope.h>
 | 
			
		||||
#include <AST.h>
 | 
			
		||||
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
 | 
			
		||||
using namespace CPlusPlus;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc)
 | 
			
		||||
    : ASTVisitor(doc->translationUnit()), _doc(doc)
 | 
			
		||||
{ }
 | 
			
		||||
CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc, const Snapshot &snapshot)
 | 
			
		||||
    : ASTVisitor(doc->translationUnit()), _context(doc, snapshot)
 | 
			
		||||
{
 | 
			
		||||
    _fileName = doc->fileName();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CheckUndefinedSymbols::~CheckUndefinedSymbols()
 | 
			
		||||
{ }
 | 
			
		||||
 | 
			
		||||
void CheckUndefinedSymbols::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding)
 | 
			
		||||
QList<Document::DiagnosticMessage> CheckUndefinedSymbols::operator()(AST *ast)
 | 
			
		||||
{
 | 
			
		||||
    _globalNamespaceBinding = globalNamespaceBinding;
 | 
			
		||||
    _types.clear();
 | 
			
		||||
    _protocols.clear();
 | 
			
		||||
 | 
			
		||||
    if (_globalNamespaceBinding) {
 | 
			
		||||
        QSet<NamespaceBinding *> processed;
 | 
			
		||||
        buildTypeMap(_globalNamespaceBinding.data(), &processed);
 | 
			
		||||
    }
 | 
			
		||||
    _diagnosticMessages.clear();
 | 
			
		||||
    accept(ast);
 | 
			
		||||
    return _diagnosticMessages;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 *)
 | 
			
		||||
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)
 | 
			
		||||
bool CheckUndefinedSymbols::warning(AST *ast, const QString &text)
 | 
			
		||||
{
 | 
			
		||||
    if (const Identifier *id = identifier(ast->namespace_name_token))
 | 
			
		||||
        _types.insert(QByteArray(id->chars(), id->size()));
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast)
 | 
			
		||||
{
 | 
			
		||||
    checkNamespace(ast->name);
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
                }
 | 
			
		||||
        unsigned line, column;
 | 
			
		||||
        getTokenStartPosition(ast->firstToken(), &line, &column);
 | 
			
		||||
 | 
			
		||||
                Overview oo;
 | 
			
		||||
                translationUnit()->warning(ast->firstToken(), "`%s' is not a type name",
 | 
			
		||||
                                           qPrintable(oo(ast->name->name)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
        Scope *enclosingScope = _context.thisDocument()->scopeAt(line, column);
 | 
			
		||||
        const QList<Symbol *> candidates = _context.lookup(ast->name->name, enclosingScope);
 | 
			
		||||
 | 
			
		||||
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<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;
 | 
			
		||||
        Symbol *ty = 0;
 | 
			
		||||
        foreach (Symbol *c, candidates) {
 | 
			
		||||
            if (c->isTypedef() || c->isClass() || c->isEnum()
 | 
			
		||||
                    || c->isForwardClassDeclaration() || c->isTypenameArgument())
 | 
			
		||||
                ty = c;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 (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)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
        if (/*Name *setterName = */ iter->value->setterName()) {
 | 
			
		||||
            // FIXME: resolve the symbol for the name, and check its signature.
 | 
			
		||||
    unsigned line, column;
 | 
			
		||||
    getTokenStartPosition(name->firstToken(), &line, &column);
 | 
			
		||||
 | 
			
		||||
    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(QtEnumDeclarationAST *ast)
 | 
			
		||||
{
 | 
			
		||||
    for (NameListAST *iter = ast->enumerator_list; iter; iter = iter->next) {
 | 
			
		||||
        if (! iter->value)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    bool warning(unsigned line, unsigned column, const QString &text, unsigned length = 0);
 | 
			
		||||
    bool warning(AST *ast, const QString &text);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    void checkNamespace(NameAST *name);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    virtual bool visit(ObjCClassDeclarationAST *ast);
 | 
			
		||||
    virtual bool visit(ObjCProtocolRefsAST *ast);
 | 
			
		||||
    virtual bool visit(ObjCPropertyDeclarationAST *ast);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user