CppEditor: Handle classes that do not override

...when searching the overrides for virtual functions.

In case there is no override for the static type of a function call
expression, make sure to:

1) include the last provided override (look up bases)
2) and all overrides whose classes are derived from that static type

Task-number: QTCREATORBUG-10470

Change-Id: I2c01bfdc6cb35c5a01a000ebd81a2b322ce2b795
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
This commit is contained in:
Nikolai Kosjar
2013-10-28 15:12:18 +01:00
parent 0c16757085
commit 4a06dab30a
5 changed files with 276 additions and 39 deletions

View File

@@ -35,6 +35,7 @@
#include <cplusplus/ASTPath.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/ResolveExpression.h>
#include <cplusplus/SimpleLexer.h>
#include <cplusplus/TypeOfExpression.h>
#include <cpptools/cppmodelmanagerinterface.h>
@@ -60,75 +61,85 @@ public:
VirtualFunctionHelper(const TypeOfExpression &typeOfExpression,
Scope *scope,
const Document::Ptr &document,
const Snapshot &snapshot);
const Snapshot &snapshot,
SymbolFinder *symbolFinder);
bool canLookupVirtualFunctionOverrides(const Function *function) const;
bool canLookupVirtualFunctionOverrides(Function *function);
/// Returns != 0 if canLookupVirtualFunctionOverrides() succeeded.
Class *staticClassOfFunctionCallExpression() const
{ return m_staticClassOfFunctionCallExpression; }
private:
VirtualFunctionHelper();
Q_DISABLE_COPY(VirtualFunctionHelper)
ExpressionAST *getBaseExpressionAST() const
{
if (!m_expressionAST)
return 0;
CallAST *callAST = m_expressionAST->asCall();
if (!callAST)
return 0;
if (ExpressionAST *baseExpressionAST = callAST->base_expression)
return baseExpressionAST;
return 0;
}
Class *staticClassOfFunctionCallExpression_internal() const;
private:
// Provided
const QSharedPointer<TypeOfExpression> m_typeOfExpression;
ExpressionAST *m_expressionAST;
const Document::Ptr m_expressionDocument;
Scope *m_scope;
const Document::Ptr &m_document;
const Snapshot &m_snapshot;
SymbolFinder *m_finder;
// Determined
ExpressionAST *m_baseExpressionAST;
Function *m_function;
int m_accessTokenKind;
Class *m_staticClassOfFunctionCallExpression; // Output
};
VirtualFunctionHelper::VirtualFunctionHelper(const TypeOfExpression &typeOfExpression,
Scope *scope,
const Document::Ptr &document,
const Snapshot &snapshot)
: m_expressionAST(typeOfExpression.expressionAST())
, m_expressionDocument(typeOfExpression.context().expressionDocument())
const Snapshot &snapshot,
SymbolFinder *finder)
: m_expressionDocument(typeOfExpression.context().expressionDocument())
, m_scope(scope)
, m_document(document)
, m_snapshot(snapshot)
, m_finder(finder)
, m_baseExpressionAST(0)
, m_function(0)
, m_accessTokenKind(0)
, m_staticClassOfFunctionCallExpression(0)
{
if (ExpressionAST *expressionAST = typeOfExpression.expressionAST()) {
if (CallAST *callAST = expressionAST->asCall()) {
if (ExpressionAST *baseExpressionAST = callAST->base_expression)
m_baseExpressionAST = baseExpressionAST;
}
}
}
bool VirtualFunctionHelper::canLookupVirtualFunctionOverrides(const Function *function) const
bool VirtualFunctionHelper::canLookupVirtualFunctionOverrides(Function *function)
{
if (!m_expressionDocument || !m_document || !function || !m_scope || m_scope->isClass()
|| m_snapshot.isEmpty()) {
m_function = function;
if (!m_function || !m_baseExpressionAST || !m_expressionDocument || !m_document || !m_scope
|| m_scope->isClass() || m_snapshot.isEmpty()) {
return false;
}
ExpressionAST *baseExpressionAST = getBaseExpressionAST();
if (!baseExpressionAST)
return false;
bool result = false;
if (IdExpressionAST *idExpressionAST = baseExpressionAST->asIdExpression()) {
if (IdExpressionAST *idExpressionAST = m_baseExpressionAST->asIdExpression()) {
NameAST *name = idExpressionAST->name;
const bool nameIsQualified = name && name->asQualifiedName();
result = !nameIsQualified && FunctionHelper::isVirtualFunction(function, m_snapshot);
} else if (MemberAccessAST *memberAccessAST = baseExpressionAST->asMemberAccess()) {
} else if (MemberAccessAST *memberAccessAST = m_baseExpressionAST->asMemberAccess()) {
NameAST *name = memberAccessAST->member_name;
const bool nameIsQualified = name && name->asQualifiedName();
if (!nameIsQualified && FunctionHelper::isVirtualFunction(function, m_snapshot)) {
TranslationUnit *unit = m_expressionDocument->translationUnit();
QTC_ASSERT(unit, return false);
const int accessTokenKind = unit->tokenKind(memberAccessAST->access_token);
m_accessTokenKind = unit->tokenKind(memberAccessAST->access_token);
if (accessTokenKind == T_ARROW) {
if (m_accessTokenKind == T_ARROW) {
result = true;
} else if (accessTokenKind == T_DOT) {
} else if (m_accessTokenKind == T_DOT) {
TypeOfExpression typeOfExpression;
typeOfExpression.init(m_document, m_snapshot);
typeOfExpression.setExpandTemplates(true);
@@ -143,6 +154,50 @@ bool VirtualFunctionHelper::canLookupVirtualFunctionOverrides(const Function *fu
}
}
if (!result)
return false;
return (m_staticClassOfFunctionCallExpression = staticClassOfFunctionCallExpression_internal());
}
/// For "f()" in "class C { void g() { f(); };" return class C.
/// For "c->f()" in "{ C *c; c->f(); }" return class C.
Class *VirtualFunctionHelper::staticClassOfFunctionCallExpression_internal() const
{
if (!m_finder)
return 0;
Class *result = 0;
if (m_baseExpressionAST->asIdExpression()) {
for (Scope *s = m_scope; s ; s = s->enclosingScope()) {
if (Function *function = s->asFunction()) {
result = m_finder->findMatchingClassDeclaration(function, m_snapshot);
break;
}
}
} else if (MemberAccessAST *memberAccessAST = m_baseExpressionAST->asMemberAccess()) {
QTC_ASSERT(m_accessTokenKind == T_ARROW || m_accessTokenKind == T_DOT, return result);
TypeOfExpression typeOfExpression;
typeOfExpression.init(m_document, m_snapshot);
typeOfExpression.setExpandTemplates(true);
const QList<LookupItem> items = typeOfExpression(memberAccessAST->base_expression,
m_document, m_scope);
ResolveExpression resolveExpression(typeOfExpression.context());
ClassOrNamespace *binding = resolveExpression.baseExpression(items, m_accessTokenKind);
if (binding) {
if (Class *klass = binding->rootClass()) {
result = klass;
} else {
const QList<Symbol *> symbols = binding->symbols();
if (!symbols.isEmpty()) {
Symbol * const first = symbols.first();
if (first->isForwardClassDeclaration())
result = m_finder->findMatchingClassDeclaration(first, m_snapshot);
}
}
}
}
return result;
}
@@ -590,11 +645,12 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &
// Consider to show a pop-up displaying overrides for the function
Function *function = symbol->type()->asFunctionType();
VirtualFunctionHelper helper(*typeOfExpression, scope, doc, snapshot);
VirtualFunctionHelper helper(*typeOfExpression, scope, doc, snapshot, symbolFinder);
if (helper.canLookupVirtualFunctionOverrides(function)) {
VirtualFunctionAssistProvider::Parameters params;
params.function = function;
params.staticClass = helper.staticClassOfFunctionCallExpression();
params.typeOfExpression = typeOfExpression;
params.snapshot = snapshot;
params.cursorPosition = cursor.position();