Commit 1932ffd1 authored by Roberto Raggi's avatar Roberto Raggi

Highlight the virtual methods.

parent 221cc387
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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)
......
......@@ -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; }
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment