forked from qt-creator/qt-creator
When built-in code model fails to follow symbol under cursor fall back to the clang result even if it only follows to the decalration. Change-Id: I22d8c5fee6ab7594b1d1b7ce8104414db28383c7 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
209 lines
7.2 KiB
C++
209 lines
7.2 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2017 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of Qt Creator.
|
|
**
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "clangfollowsymbol.h"
|
|
#include "clangfollowsymboljob.h"
|
|
#include "commandlinearguments.h"
|
|
#include "cursor.h"
|
|
#include "clangstring.h"
|
|
#include "sourcerange.h"
|
|
#include "clangsupportdebugutils.h"
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <future>
|
|
|
|
namespace ClangBackEnd {
|
|
|
|
namespace {
|
|
|
|
struct Tokens
|
|
{
|
|
Tokens(const Tokens &) = delete;
|
|
Tokens(const Cursor &cursor) {
|
|
tu = cursor.cxTranslationUnit();
|
|
clang_tokenize(tu, cursor.cxSourceRange(), &data, &tokenCount);
|
|
}
|
|
Tokens(const CXTranslationUnit &tu) {
|
|
const CXSourceRange range
|
|
= clang_getCursorExtent(clang_getTranslationUnitCursor(tu));
|
|
clang_tokenize(tu, range, &data, &tokenCount);
|
|
}
|
|
~Tokens() {
|
|
clang_disposeTokens(tu, data, tokenCount);
|
|
}
|
|
|
|
CXToken *data = nullptr;
|
|
uint tokenCount = 0;
|
|
private:
|
|
CXTranslationUnit tu;
|
|
};
|
|
|
|
class FollowSymbolData {
|
|
public:
|
|
FollowSymbolData() = delete;
|
|
FollowSymbolData(const Utf8String &usr, const Utf8String &tokenSpelling, bool isFunctionLike,
|
|
std::atomic<bool> &ready)
|
|
: m_usr(usr)
|
|
, m_spelling(tokenSpelling)
|
|
, m_isFunctionLike(isFunctionLike)
|
|
, m_ready(ready)
|
|
{}
|
|
FollowSymbolData(const FollowSymbolData &other)
|
|
: m_usr(other.m_usr)
|
|
, m_spelling(other.m_spelling)
|
|
, m_isFunctionLike(other.m_isFunctionLike)
|
|
, m_ready(other.m_ready)
|
|
{}
|
|
|
|
const Utf8String &usr() const { return m_usr; }
|
|
const Utf8String &spelling() const { return m_spelling; }
|
|
bool isFunctionLike() const { return m_isFunctionLike; }
|
|
bool ready() const { return m_ready; }
|
|
const SourceRangeContainer &result() const { return m_result; }
|
|
|
|
void setReady(bool ready = true) { m_ready = ready; }
|
|
void setResult(const SourceRangeContainer &result) { m_result = result; }
|
|
private:
|
|
const Utf8String &m_usr;
|
|
const Utf8String &m_spelling;
|
|
SourceRangeContainer m_result;
|
|
bool m_isFunctionLike;
|
|
std::atomic<bool> &m_ready;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
static SourceRange getOperatorRange(const CXTranslationUnit tu,
|
|
const Tokens &tokens,
|
|
uint operatorIndex)
|
|
{
|
|
const CXSourceLocation start = clang_getTokenLocation(tu, tokens.data[operatorIndex]);
|
|
operatorIndex += 2;
|
|
while (operatorIndex < tokens.tokenCount
|
|
&& !(ClangString(clang_getTokenSpelling(tu, tokens.data[operatorIndex])) == "(")) {
|
|
++operatorIndex;
|
|
}
|
|
const CXSourceLocation end = clang_getTokenLocation(tu, tokens.data[operatorIndex]);
|
|
return SourceRange(tu, clang_getRange(start, end));
|
|
}
|
|
|
|
static SourceRangeContainer extractMatchingTokenRange(const Cursor &cursor,
|
|
const Utf8String &tokenStr)
|
|
{
|
|
Tokens tokens(cursor);
|
|
const CXTranslationUnit tu = cursor.cxTranslationUnit();
|
|
for (uint i = 0; i < tokens.tokenCount; ++i) {
|
|
if (!(tokenStr == ClangString(clang_getTokenSpelling(tu, tokens.data[i]))))
|
|
continue;
|
|
|
|
if (cursor.isFunctionLike() || cursor.isConstructorOrDestructor()) {
|
|
if (tokenStr == "operator")
|
|
return getOperatorRange(tu, tokens, i);
|
|
|
|
if (i+1 > tokens.tokenCount
|
|
|| !(ClangString(clang_getTokenSpelling(tu, tokens.data[i+1])) == "(")) {
|
|
continue;
|
|
}
|
|
}
|
|
return SourceRange(tu, clang_getTokenExtent(tu, tokens.data[i]));
|
|
}
|
|
return SourceRangeContainer();
|
|
}
|
|
|
|
static int getTokenIndex(CXTranslationUnit tu, const Tokens &tokens, uint line, uint column)
|
|
{
|
|
int tokenIndex = -1;
|
|
for (int i = static_cast<int>(tokens.tokenCount - 1); i >= 0; --i) {
|
|
const SourceRange range(tu, clang_getTokenExtent(tu, tokens.data[i]));
|
|
if (range.contains(line, column)) {
|
|
tokenIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
return tokenIndex;
|
|
}
|
|
|
|
FollowSymbolResult FollowSymbol::followSymbol(CXTranslationUnit tu,
|
|
const Cursor &fullCursor,
|
|
uint line,
|
|
uint column)
|
|
{
|
|
std::unique_ptr<Tokens> tokens(new Tokens(fullCursor));
|
|
|
|
if (!tokens->tokenCount)
|
|
tokens.reset(new Tokens(tu));
|
|
|
|
if (!tokens->tokenCount)
|
|
return SourceRangeContainer();
|
|
|
|
QVector<CXCursor> cursors(static_cast<int>(tokens->tokenCount));
|
|
clang_annotateTokens(tu, tokens->data, tokens->tokenCount, cursors.data());
|
|
int tokenIndex = getTokenIndex(tu, *tokens, line, column);
|
|
QTC_ASSERT(tokenIndex >= 0, return SourceRangeContainer());
|
|
|
|
const Utf8String tokenSpelling = ClangString(
|
|
clang_getTokenSpelling(tu, tokens->data[tokenIndex]));
|
|
if (tokenSpelling.isEmpty())
|
|
return SourceRangeContainer();
|
|
|
|
Cursor cursor{cursors[tokenIndex]};
|
|
|
|
if (cursor.kind() == CXCursor_InclusionDirective) {
|
|
CXFile file = clang_getIncludedFile(cursors[tokenIndex]);
|
|
const ClangString filename(clang_getFileName(file));
|
|
const SourceLocation loc(tu, filename, 1, 1);
|
|
return SourceRangeContainer(SourceRange(loc, loc));
|
|
}
|
|
|
|
// For definitions we can always find a declaration in current TU
|
|
if (cursor.isDefinition())
|
|
return extractMatchingTokenRange(cursor.canonical(), tokenSpelling);
|
|
|
|
if (!cursor.isDeclaration()) {
|
|
// This is the symbol usage
|
|
// We want to return definition
|
|
FollowSymbolResult result;
|
|
cursor = cursor.referenced();
|
|
if (cursor.isNull())
|
|
return SourceRangeContainer();
|
|
if (!cursor.isDefinition()) {
|
|
// We can't find definition in this TU
|
|
result.isPureDeclarationForUsage = true;
|
|
}
|
|
result.range = extractMatchingTokenRange(cursor, tokenSpelling);
|
|
return result;
|
|
}
|
|
|
|
cursor = cursor.definition();
|
|
// If we are able to find a definition in current TU
|
|
if (!cursor.isNull())
|
|
return extractMatchingTokenRange(cursor, tokenSpelling);
|
|
|
|
return SourceRangeContainer();
|
|
}
|
|
|
|
} // namespace ClangBackEnd
|