diff --git a/src/plugins/cppeditor/cppchecksymbols.cpp b/src/plugins/cppeditor/cppchecksymbols.cpp index 27126be286da0be9ce75d66f5bc1183118435675..b7b95d8b930c7d7cce42d6635d0f319b770d73b2 100644 --- a/src/plugins/cppeditor/cppchecksymbols.cpp +++ b/src/plugins/cppeditor/cppchecksymbols.cpp @@ -63,6 +63,7 @@ class CollectTypes: protected SymbolVisitor Snapshot _snapshot; QSet<QByteArray> _types; QSet<QByteArray> _members; + QSet<QByteArray> _virtualMethods; QList<ScopedSymbol *> _scopes; QList<NameAST *> _names; bool _mainDocument; @@ -85,6 +86,11 @@ public: return _members; } + const QSet<QByteArray> &virtualMethods() const + { + return _virtualMethods; + } + const QList<ScopedSymbol *> &scopes() const { return _scopes; @@ -153,6 +159,18 @@ protected: } } + 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())); + + } + } + void addScope(ScopedSymbol *symbol) { if (_mainDocument) @@ -167,6 +185,9 @@ protected: virtual bool visit(Function *symbol) { + 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) @@ -191,6 +212,11 @@ protected: virtual bool visit(Declaration *symbol) { + if (Function *funTy = symbol->type()->asFunctionType()) { + if (funTy->isVirtual()) + addVirtualMethod(symbol->name()); + } + if (symbol->isTypedef()) addType(symbol->name()); else if (! symbol->type()->isFunctionType() && symbol->enclosingSymbol()->isClass()) @@ -298,6 +324,7 @@ CheckSymbols::CheckSymbols(Document::Ptr doc, const LookupContext &context) CollectTypes collectTypes(doc, context.snapshot()); _potentialTypes = collectTypes.types(); _potentialMembers = collectTypes.members(); + _potentialVirtualMethods = collectTypes.virtualMethods(); _scopes = collectTypes.scopes(); _flushRequested = false; _flushLine = 0; @@ -370,8 +397,23 @@ bool CheckSymbols::visit(UsingDirectiveAST *) return true; } -bool CheckSymbols::visit(SimpleDeclarationAST *) -{ +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()) { + addVirtualMethodUsage(declId); + } else if (maybeVirtualMethod(decl->name())) { + addVirtualMethodUsage(_context.lookup(decl->name(), decl->scope()), declId, funTy->argumentCount()); + } + } + } + } + } + return true; } @@ -405,6 +447,52 @@ bool CheckSymbols::visit(MemberAccessAST *ast) return false; } +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)) { + Scope *scope = findScope(access); + const QByteArray expression = textOf(access); + + const QList<LookupItem> candidates = typeOfExpression(expression, scope, TypeOfExpression::Preprocess); + addVirtualMethodUsage(candidates, access->member_name, argumentCount); + } + } + } else if (IdExpressionAST *idExpr = ast->base_expression->asIdExpression()) { + if (const Name *name = idExpr->name->name) { + if (maybeVirtualMethod(name)) { + Scope *scope = findScope(idExpr); + const QByteArray expression = textOf(idExpr); + + const QList<LookupItem> candidates = typeOfExpression(expression, scope, TypeOfExpression::Preprocess); + addVirtualMethodUsage(candidates, idExpr->name, argumentCount); + } + } + } + + 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; +} + void CheckSymbols::checkNamespace(NameAST *name) { if (! name) @@ -573,6 +661,21 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast) _functionDefinitionStack.append(ast); accept(ast->decl_specifier_list); + + if (ast->declarator && ! ast->symbol->isGenerated()) { + Function *fun = ast->symbol; + if (NameAST *declId = declaratorId(ast->declarator)) { + if (QualifiedNameAST *q = declId->asQualifiedName()) + declId = q->unqualified_name; + + if (fun->isVirtual()) { + addVirtualMethodUsage(declId); + } else if (maybeVirtualMethod(fun->name())) { + addVirtualMethodUsage(_context.lookup(fun->name(), fun->scope()), declId, fun->argumentCount()); + } + } + } + accept(ast->declarator); accept(ast->ctor_initializer); accept(ast->function_body); @@ -687,8 +790,61 @@ void CheckSymbols::addMemberUsage(const QList<LookupItem> &candidates, NameAST * const Use use(line, column, length, Use::Field); addUsage(use); - //Overview oo; - //qDebug() << "added use" << oo(ast->name) << line << column << length; + break; + } +} + +void CheckSymbols::addVirtualMethodUsage(NameAST *ast) +{ + if (! ast) + return; + + 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, Use::VirtualMethod); + addUsage(use); +} + +void CheckSymbols::addVirtualMethodUsage(const QList<LookupItem> &candidates, NameAST *ast, unsigned argumentCount) +{ + 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(); + + 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; + + const Use use(line, column, length, Use::VirtualMethod); + addUsage(use); + break; } } @@ -723,6 +879,30 @@ Scope *CheckSymbols::findScope(AST *ast) const return scope; } +NameAST *CheckSymbols::declaratorId(DeclaratorAST *ast) const +{ + 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; + } + } + + return 0; +} + +bool CheckSymbols::maybeVirtualMethod(const Name *name) const +{ + if (const Identifier *ident = name->identifier()) { + const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size()); + if (_potentialVirtualMethods.contains(id)) + return true; + } + + return false; +} + void CheckSymbols::flush() { _flushRequested = false; diff --git a/src/plugins/cppeditor/cppchecksymbols.h b/src/plugins/cppeditor/cppchecksymbols.h index 8b3418fc4584704324bd94b5f953dd660dad1eff..5047e839a986e1c33937de9afbb125c15c5f2ab3 100644 --- a/src/plugins/cppeditor/cppchecksymbols.h +++ b/src/plugins/cppeditor/cppchecksymbols.h @@ -93,6 +93,8 @@ protected: bool warning(unsigned line, unsigned column, const QString &text, unsigned length = 0); bool warning(AST *ast, const QString &text); + QByteArray textOf(AST *ast) const; + void checkName(NameAST *ast, Scope *scope = 0); void checkNamespace(NameAST *name); void addUsage(ClassOrNamespace *b, NameAST *ast); @@ -101,6 +103,10 @@ protected: void checkMemberName(NameAST *ast); void addMemberUsage(const QList<LookupItem> &candidates, NameAST *ast); + void addVirtualMethodUsage(const QList<LookupItem> &candidates, NameAST *ast, unsigned argumentCount); + void addVirtualMethodUsage(NameAST *ast); + + bool maybeVirtualMethod(const Name *name) const; virtual bool preVisit(AST *); @@ -122,9 +128,12 @@ protected: virtual bool visit(FunctionDefinitionAST *ast); virtual bool visit(MemberAccessAST *ast); + virtual bool visit(CallAST *ast); virtual bool visit(MemInitializerAST *ast); + NameAST *declaratorId(DeclaratorAST *ast) const; + unsigned startOfTemplateDeclaration(TemplateDeclarationAST *ast) const; Scope *findScope(AST *ast) const; @@ -138,6 +147,7 @@ private: QList<Document::DiagnosticMessage> _diagnosticMessages; QSet<QByteArray> _potentialTypes; QSet<QByteArray> _potentialMembers; + QSet<QByteArray> _potentialVirtualMethods; QList<ScopedSymbol *> _scopes; QList<TemplateDeclarationAST *> _templateDeclarationStack; QList<FunctionDefinitionAST *> _functionDefinitionStack; diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 319d2db63c9d26adefb5b97eb0a033519b8c6a6b..7deb214b17ce2c8933920527ff23d629f8a1f367 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -968,6 +968,9 @@ void CPPEditor::highlightTypeUsages(int from, int to) Q_ASSERT(!chunks.isEmpty()); QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber); + QTextCharFormat virtualMethodFormat; // ### hardcoded; + virtualMethodFormat.setFontItalic(true); + QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks); while (b.isValid() && it.hasNext()) { it.next(); @@ -997,6 +1000,10 @@ void CPPEditor::highlightTypeUsages(int from, int to) formatRange.format = m_localFormat; break; + case SemanticInfo::Use::VirtualMethod: + formatRange.format = virtualMethodFormat; + break; + default: continue; } diff --git a/src/plugins/cppeditor/cppsemanticinfo.h b/src/plugins/cppeditor/cppsemanticinfo.h index 3ea662bdbbe3049a054e864bc61aa4729574bf0c..59768a6f1a945911416522d87691cf1a8c1b462f 100644 --- a/src/plugins/cppeditor/cppsemanticinfo.h +++ b/src/plugins/cppeditor/cppsemanticinfo.h @@ -51,7 +51,8 @@ public: enum { Type = 0, Local, - Field + Field, + VirtualMethod }; Use(unsigned line = 0, unsigned column = 0, unsigned length = 0, unsigned kind = Type) diff --git a/src/shared/cplusplus/Symbols.cpp b/src/shared/cplusplus/Symbols.cpp index e82a60ae199e92cd9bd25bf0d388412b67f61d6c..c1dcec44a4ed16fb6003a1312e8989b21236711b 100644 --- a/src/shared/cplusplus/Symbols.cpp +++ b/src/shared/cplusplus/Symbols.cpp @@ -352,6 +352,20 @@ bool Function::hasArguments() const (argumentCount() == 1 && argumentAt(0)->type()->isVoidType())); } +unsigned Function::minimumArgumentCount() const +{ + unsigned index = 0; + + for (; index < _arguments->symbolCount(); ++index) { + if (Argument *arg = _arguments->symbolAt(index)->asArgument()) { + if (arg->hasInitializer()) + break; + } + } + + return index; +} + bool Function::isVirtual() const { return f._isVirtual; } diff --git a/src/shared/cplusplus/Symbols.h b/src/shared/cplusplus/Symbols.h index 2a90fd0cc0306b5712e4f9385fea450129a1d67b..0f5d8d83745c1b215d80c9ed925e34945de5c98f 100644 --- a/src/shared/cplusplus/Symbols.h +++ b/src/shared/cplusplus/Symbols.h @@ -360,6 +360,7 @@ public: /** Convenience function that returns whether the function receives any arguments. */ bool hasArguments() const; + unsigned minimumArgumentCount() const; bool isVirtual() const; void setVirtual(bool isVirtual);