forked from qt-creator/qt-creator
CppTools: Also look up definitions for variables
... not just functions. This includes global variables and static members. Fixes: QTCREATORBUG-18828 Change-Id: Iee9f83a4f955a859c6fc4038c61997b30afdaec8 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -696,6 +696,7 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
|
|||||||
// Find function declaration or definition under cursor
|
// Find function declaration or definition under cursor
|
||||||
Function *functionDefinitionSymbol = nullptr;
|
Function *functionDefinitionSymbol = nullptr;
|
||||||
Symbol *functionDeclarationSymbol = nullptr;
|
Symbol *functionDeclarationSymbol = nullptr;
|
||||||
|
Symbol *declarationSymbol = nullptr;
|
||||||
|
|
||||||
ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
|
ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
|
||||||
const QList<AST *> astPath = astPathFinder(textCursor());
|
const QList<AST *> astPath = astPathFinder(textCursor());
|
||||||
@@ -707,9 +708,12 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
|
|||||||
} else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
|
} else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
|
||||||
if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
|
if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
|
||||||
if (Symbol *symbol = symbols->value) {
|
if (Symbol *symbol = symbols->value) {
|
||||||
if (symbol->isDeclaration() && symbol->type()->isFunctionType()) {
|
if (symbol->isDeclaration()) {
|
||||||
functionDeclarationSymbol = symbol;
|
declarationSymbol = symbol;
|
||||||
break; // Function declaration found!
|
if (symbol->type()->isFunctionType()) {
|
||||||
|
functionDeclarationSymbol = symbol;
|
||||||
|
break; // Function declaration found!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,6 +727,11 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
|
|||||||
->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot());
|
->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot());
|
||||||
if (symbol)
|
if (symbol)
|
||||||
symbolLink = symbol->toLink();
|
symbolLink = symbol->toLink();
|
||||||
|
} else if (declarationSymbol) {
|
||||||
|
Symbol *symbol = d->m_modelManager->symbolFinder()
|
||||||
|
->findMatchingVarDefinition(declarationSymbol, d->m_modelManager->snapshot());
|
||||||
|
if (symbol)
|
||||||
|
symbolLink = symbol->toLink();
|
||||||
} else if (functionDefinitionSymbol) {
|
} else if (functionDefinitionSymbol) {
|
||||||
const Snapshot snapshot = d->m_modelManager->snapshot();
|
const Snapshot snapshot = d->m_modelManager->snapshot();
|
||||||
LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
|
LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
|
||||||
|
|||||||
@@ -554,6 +554,31 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
|
|||||||
"class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
|
"class Foo { void $" TEST_UNICODE_IDENTIFIER "(); };\n"
|
||||||
"void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
|
"void Foo::@" TEST_UNICODE_IDENTIFIER "() {}\n"
|
||||||
) << _();
|
) << _();
|
||||||
|
|
||||||
|
QTest::newRow("globalVar")
|
||||||
|
<< _("namespace NS { extern int @globalVar; }\n")
|
||||||
|
<< _("int globalVar;\n"
|
||||||
|
"namespace NS {\n"
|
||||||
|
"extern int globalVar;\n"
|
||||||
|
"int $globalVar;\n"
|
||||||
|
"}\n");
|
||||||
|
|
||||||
|
QTest::newRow("staticMemberVar")
|
||||||
|
<< _("namespace NS {\n"
|
||||||
|
"class Test {\n"
|
||||||
|
" static int @var;\n"
|
||||||
|
"};\n"
|
||||||
|
"class OtherClass { static int var; };\n"
|
||||||
|
"}\n"
|
||||||
|
"class OtherClass { static int var; };\n")
|
||||||
|
<< _("#include \"file.h\"\n"
|
||||||
|
"int var;\n"
|
||||||
|
"int OtherClass::var;\n"
|
||||||
|
"namespace NS {\n"
|
||||||
|
"int OtherClass::var;\n"
|
||||||
|
"float Test::var;\n"
|
||||||
|
"int Test::$var;\n"
|
||||||
|
"}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppEditorPlugin::test_SwitchMethodDeclarationDefinition()
|
void CppEditorPlugin::test_SwitchMethodDeclarationDefinition()
|
||||||
@@ -1145,6 +1170,34 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data()
|
|||||||
"bool *$fun(C *) const {}\n",
|
"bool *$fun(C *) const {}\n",
|
||||||
"foo.cpp")
|
"foo.cpp")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QTest::newRow("globalVar") << QList<TestDocumentPtr>{
|
||||||
|
TestDocument::create("namespace NS { extern int @globalVar; }\n", "file.h"),
|
||||||
|
TestDocument::create(
|
||||||
|
"int globalVar;\n"
|
||||||
|
"namespace NS {\n"
|
||||||
|
"extern int globalVar;\n"
|
||||||
|
"int $globalVar;\n"
|
||||||
|
"}\n", "file.cpp")};
|
||||||
|
|
||||||
|
QTest::newRow("staticMemberVar") << QList<TestDocumentPtr>{
|
||||||
|
TestDocument::create(
|
||||||
|
"namespace NS {\n"
|
||||||
|
"class Test {\n"
|
||||||
|
" static int @var;\n"
|
||||||
|
"};\n"
|
||||||
|
"class OtherClass { static int var; };\n"
|
||||||
|
"}\n"
|
||||||
|
"class OtherClass { static int var; };\n", "file.h"),
|
||||||
|
TestDocument::create(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"int var;\n"
|
||||||
|
"int OtherClass::var;\n"
|
||||||
|
"namespace NS {\n"
|
||||||
|
"int OtherClass::var;\n"
|
||||||
|
"float Test::var;\n"
|
||||||
|
"int Test::$var;\n"
|
||||||
|
"}\n", "file.cpp")};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments()
|
void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments()
|
||||||
|
|||||||
@@ -292,9 +292,9 @@ inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbo
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Link attemptFuncDeclDef(const QTextCursor &cursor, Snapshot snapshot,
|
Link attemptDeclDef(const QTextCursor &cursor, Snapshot snapshot,
|
||||||
const Document::Ptr &document,
|
const Document::Ptr &document,
|
||||||
SymbolFinder *symbolFinder)
|
SymbolFinder *symbolFinder)
|
||||||
{
|
{
|
||||||
Link result;
|
Link result;
|
||||||
QTC_ASSERT(document, return result);
|
QTC_ASSERT(document, return result);
|
||||||
@@ -333,11 +333,6 @@ Link attemptFuncDeclDef(const QTextCursor &cursor, Snapshot snapshot,
|
|||||||
}
|
}
|
||||||
if (!decl || !declParent)
|
if (!decl || !declParent)
|
||||||
return result;
|
return result;
|
||||||
if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value)
|
|
||||||
return result;
|
|
||||||
FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator();
|
|
||||||
if (!funcDecl)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
Symbol *target = nullptr;
|
Symbol *target = nullptr;
|
||||||
if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
|
if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
|
||||||
@@ -346,8 +341,14 @@ Link attemptFuncDeclDef(const QTextCursor &cursor, Snapshot snapshot,
|
|||||||
funDef->symbol);
|
funDef->symbol);
|
||||||
if (!candidates.isEmpty()) // TODO: improve disambiguation
|
if (!candidates.isEmpty()) // TODO: improve disambiguation
|
||||||
target = candidates.first();
|
target = candidates.first();
|
||||||
} else if (declParent->asSimpleDeclaration()) {
|
} else if (const SimpleDeclarationAST * const simpleDecl = declParent->asSimpleDeclaration()) {
|
||||||
target = symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot);
|
FunctionDeclaratorAST *funcDecl = nullptr;
|
||||||
|
if (decl->postfix_declarator_list && decl->postfix_declarator_list->value)
|
||||||
|
funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator();
|
||||||
|
if (funcDecl)
|
||||||
|
target = symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot);
|
||||||
|
else
|
||||||
|
target = symbolFinder->findMatchingVarDefinition(simpleDecl->symbols->value, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
@@ -514,9 +515,9 @@ void FollowSymbolUnderCursor::findLink(
|
|||||||
int pos = tc.position();
|
int pos = tc.position();
|
||||||
while (document->characterAt(pos).isSpace())
|
while (document->characterAt(pos).isSpace())
|
||||||
++pos;
|
++pos;
|
||||||
if (document->characterAt(pos) == QLatin1Char('(')) {
|
const QChar ch = document->characterAt(pos);
|
||||||
link = attemptFuncDeclDef(cursor, snapshot, documentFromSemanticInfo,
|
if (ch == '(' || ch == ';') {
|
||||||
symbolFinder);
|
link = attemptDeclDef(cursor, snapshot, documentFromSemanticInfo, symbolFinder);
|
||||||
if (link.hasValidLinkText())
|
if (link.hasValidLinkText())
|
||||||
return processLinkCallback(link);
|
return processLinkCallback(link);
|
||||||
}
|
}
|
||||||
@@ -585,16 +586,15 @@ void FollowSymbolUnderCursor::findLink(
|
|||||||
if (positionInBlock >= tk.utf16charsBegin() && positionInBlock <= tk.utf16charsEnd()) {
|
if (positionInBlock >= tk.utf16charsBegin() && positionInBlock <= tk.utf16charsEnd()) {
|
||||||
cursorRegionReached = true;
|
cursorRegionReached = true;
|
||||||
if (tk.is(T_OPERATOR)) {
|
if (tk.is(T_OPERATOR)) {
|
||||||
link = attemptFuncDeclDef(cursor, theSnapshot,
|
link = attemptDeclDef(cursor, theSnapshot,
|
||||||
documentFromSemanticInfo, symbolFinder);
|
documentFromSemanticInfo, symbolFinder);
|
||||||
if (link.hasValidLinkText())
|
if (link.hasValidLinkText())
|
||||||
return processLinkCallback(link);
|
return processLinkCallback(link);
|
||||||
} else if (tk.isPunctuationOrOperator() && i > 0 && tokens.at(i - 1).is(T_OPERATOR)) {
|
} else if (tk.isPunctuationOrOperator() && i > 0 && tokens.at(i - 1).is(T_OPERATOR)) {
|
||||||
QTextCursor c = cursor;
|
QTextCursor c = cursor;
|
||||||
c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor,
|
c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor,
|
||||||
positionInBlock - tokens.at(i - 1).utf16charsBegin());
|
positionInBlock - tokens.at(i - 1).utf16charsBegin());
|
||||||
link = attemptFuncDeclDef(c, theSnapshot, documentFromSemanticInfo,
|
link = attemptDeclDef(c, theSnapshot, documentFromSemanticInfo, symbolFinder);
|
||||||
symbolFinder);
|
|
||||||
if (link.hasValidLinkText())
|
if (link.hasValidLinkText())
|
||||||
return processLinkCallback(link);
|
return processLinkCallback(link);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -84,6 +85,46 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FindMatchingVarDefinition: public SymbolVisitor
|
||||||
|
{
|
||||||
|
Symbol *_declaration = nullptr;
|
||||||
|
QList<Declaration *> _result;
|
||||||
|
const Identifier *_className = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FindMatchingVarDefinition(Symbol *declaration)
|
||||||
|
: _declaration(declaration)
|
||||||
|
{
|
||||||
|
if (declaration->isStatic() && declaration->enclosingScope()->asClass()
|
||||||
|
&& declaration->enclosingClass()->asClass()->name()) {
|
||||||
|
_className = declaration->enclosingScope()->name()->identifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<Declaration *> result() const { return _result; }
|
||||||
|
|
||||||
|
using SymbolVisitor::visit;
|
||||||
|
|
||||||
|
bool visit(Declaration *decl) override
|
||||||
|
{
|
||||||
|
if (!decl->type()->match(_declaration->type().type()))
|
||||||
|
return false;
|
||||||
|
if (!_declaration->identifier()->equalTo(decl->identifier()))
|
||||||
|
return false;
|
||||||
|
if (_className) {
|
||||||
|
const QualifiedNameId * const qualName = decl->name()->asQualifiedNameId();
|
||||||
|
if (!qualName)
|
||||||
|
return false;
|
||||||
|
if (!qualName->base() || !qualName->base()->identifier()->equalTo(_className))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_result.append(decl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(Block *) override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
|
||||||
static const int kMaxCacheSize = 10;
|
static const int kMaxCacheSize = 10;
|
||||||
@@ -207,6 +248,87 @@ Function *SymbolFinder::findMatchingDefinition(Symbol *declaration,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Symbol *SymbolFinder::findMatchingVarDefinition(Symbol *declaration, const Snapshot &snapshot)
|
||||||
|
{
|
||||||
|
if (!declaration)
|
||||||
|
return nullptr;
|
||||||
|
for (const Scope *s = declaration->enclosingScope(); s; s = s->enclosingScope()) {
|
||||||
|
if (s->asBlock())
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
|
||||||
|
const Document::Ptr thisDocument = snapshot.document(declFile);
|
||||||
|
if (!thisDocument) {
|
||||||
|
qWarning() << "undefined document:" << declaration->fileName();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
using SymbolWithPriority = QPair<Symbol *, bool>;
|
||||||
|
QList<SymbolWithPriority> candidates;
|
||||||
|
QList<SymbolWithPriority> fallbacks;
|
||||||
|
foreach (const QString &fileName, fileIterationOrder(declFile, snapshot)) {
|
||||||
|
Document::Ptr doc = snapshot.document(fileName);
|
||||||
|
if (!doc) {
|
||||||
|
clearCache(declFile, fileName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Identifier *id = declaration->identifier();
|
||||||
|
if (id && !doc->control()->findIdentifier(id->chars(), id->size()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FindMatchingVarDefinition finder(declaration);
|
||||||
|
finder.accept(doc->globalNamespace());
|
||||||
|
if (finder.result().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LookupContext context(doc, snapshot);
|
||||||
|
ClassOrNamespace * const enclosingType = context.lookupType(declaration);
|
||||||
|
for (Symbol * const symbol : finder.result()) {
|
||||||
|
const QList<LookupItem> items = context.lookup(symbol->name(),
|
||||||
|
symbol->enclosingScope());
|
||||||
|
bool addFallback = true;
|
||||||
|
for (const LookupItem &item : items) {
|
||||||
|
if (item.declaration() == symbol)
|
||||||
|
addFallback = false;
|
||||||
|
candidates << qMakePair(item.declaration(),
|
||||||
|
context.lookupType(item.declaration()) == enclosingType);
|
||||||
|
}
|
||||||
|
// TODO: This is a workaround for static member definitions not being found by
|
||||||
|
// the lookup() function.
|
||||||
|
if (addFallback)
|
||||||
|
fallbacks << qMakePair(symbol, context.lookupType(symbol) == enclosingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates << fallbacks;
|
||||||
|
SymbolWithPriority best;
|
||||||
|
for (const auto &candidate : qAsConst(candidates)) {
|
||||||
|
if (candidate.first == declaration)
|
||||||
|
continue;
|
||||||
|
if (QLatin1String(candidate.first->fileName()) == declFile
|
||||||
|
&& candidate.first->sourceLocation() == declaration->sourceLocation())
|
||||||
|
continue;
|
||||||
|
if (!candidate.first->asDeclaration())
|
||||||
|
continue;
|
||||||
|
if (declaration->isExtern() && candidate.first->isStatic())
|
||||||
|
continue;
|
||||||
|
if (!best.first) {
|
||||||
|
best = candidate;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!best.second && candidate.second) {
|
||||||
|
best = candidate;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (best.first->isExtern() && !candidate.first->isExtern())
|
||||||
|
best = candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return best.first;
|
||||||
|
}
|
||||||
|
|
||||||
Class *SymbolFinder::findMatchingClassDeclaration(Symbol *declaration, const Snapshot &snapshot)
|
Class *SymbolFinder::findMatchingClassDeclaration(Symbol *declaration, const Snapshot &snapshot)
|
||||||
{
|
{
|
||||||
if (!declaration->identifier())
|
if (!declaration->identifier())
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ public:
|
|||||||
const CPlusPlus::Snapshot &snapshot,
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
bool strict = false);
|
bool strict = false);
|
||||||
|
|
||||||
|
CPlusPlus::Symbol *findMatchingVarDefinition(CPlusPlus::Symbol *declaration,
|
||||||
|
const CPlusPlus::Snapshot &snapshot);
|
||||||
|
|
||||||
CPlusPlus::Class *findMatchingClassDeclaration(CPlusPlus::Symbol *declaration,
|
CPlusPlus::Class *findMatchingClassDeclaration(CPlusPlus::Symbol *declaration,
|
||||||
const CPlusPlus::Snapshot &snapshot);
|
const CPlusPlus::Snapshot &snapshot);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user