2017-06-09 12:19:09 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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 "clangreferencescollector.h"
|
|
|
|
|
|
|
|
|
|
#include "clangstring.h"
|
|
|
|
|
#include "cursor.h"
|
|
|
|
|
#include "sourcerange.h"
|
|
|
|
|
|
2017-08-24 12:28:01 +02:00
|
|
|
#include <clangsupport/sourcerangecontainer.h>
|
2017-06-09 12:19:09 +02:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <utf8string.h>
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
namespace ClangBackEnd {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class ReferencedCursor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2018-08-21 10:23:12 +02:00
|
|
|
static ReferencedCursor find(const Cursor &cursor, const CXToken &token)
|
2017-06-09 12:19:09 +02:00
|
|
|
{
|
2018-08-21 10:23:12 +02:00
|
|
|
if (cursor.spelling().startsWith("operator")
|
|
|
|
|
&& clang_getTokenKind(token) == CXToken_Identifier) {
|
|
|
|
|
// We are actually inside operator, use clang_getCursor to return a proper cursor instead.
|
|
|
|
|
return find(clang_getCursor(cursor.cxTranslationUnit(),
|
|
|
|
|
clang_getTokenLocation(cursor.cxTranslationUnit(), token)),
|
|
|
|
|
token);
|
|
|
|
|
}
|
2017-06-09 12:19:09 +02:00
|
|
|
// Query the referenced cursor directly instead of first testing with cursor.isReference().
|
|
|
|
|
// cursor.isReference() reports false for e.g. CXCursor_DeclRefExpr or CXCursor_CallExpr
|
|
|
|
|
// although it returns a valid cursor.
|
|
|
|
|
const Cursor referenced = cursor.referenced();
|
|
|
|
|
if (referenced.isValid())
|
|
|
|
|
return handleReferenced(referenced);
|
|
|
|
|
|
|
|
|
|
const Cursor definition = cursor.definition();
|
|
|
|
|
if (definition.isValid())
|
|
|
|
|
return definition;
|
|
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utf8String usr() const
|
|
|
|
|
{
|
|
|
|
|
return cursor.unifiedSymbolResolution() + usrSuffix;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isLocalVariable() const
|
|
|
|
|
{
|
|
|
|
|
return cursor.isLocalVariable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ReferencedCursor(const Cursor &cursor, const Utf8String &usrSuffix = Utf8String())
|
|
|
|
|
: cursor(cursor)
|
|
|
|
|
, usrSuffix(usrSuffix)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
static ReferencedCursor handleReferenced(const Cursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
if (cursor.kind() == CXCursor_OverloadedDeclRef) {
|
|
|
|
|
// e.g. Text cursor is on "App" of "using N::App;".
|
|
|
|
|
if (cursor.overloadedDeclarationsCount() >= 1)
|
|
|
|
|
return cursor.overloadedDeclaration(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cursor.isConstructorOrDestructor()) {
|
|
|
|
|
const Type type = cursor.type();
|
|
|
|
|
if (type.isValid()) {
|
|
|
|
|
const Cursor typeDeclaration = type.declaration();
|
|
|
|
|
if (typeDeclaration.isValid()) {
|
|
|
|
|
// A CXCursor_CallExpr like "new Foo" has a type of CXType_Record and its
|
|
|
|
|
// declaration is e.g. CXCursor_ClassDecl.
|
|
|
|
|
return typeDeclaration;
|
|
|
|
|
} else {
|
|
|
|
|
// A CXCursor_Constructor like "Foo();" has a type of CXType_FunctionProto
|
|
|
|
|
// and its type declaration is invalid, so use the semantic parent.
|
|
|
|
|
const Cursor parent = cursor.semanticParent();
|
|
|
|
|
if (parent.isValid())
|
|
|
|
|
return parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cursor.isFunctionLike() || cursor.isTemplateLike()) {
|
|
|
|
|
const Cursor parent = cursor.semanticParent();
|
|
|
|
|
const ClangString spelling = cursor.spelling();
|
|
|
|
|
return {parent, Utf8StringLiteral("_qtc_") + Utf8String(spelling)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Cursor cursor;
|
|
|
|
|
Utf8String usrSuffix;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ReferencesCollector
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ReferencesCollector(CXTranslationUnit cxTranslationUnit);
|
|
|
|
|
~ReferencesCollector();
|
|
|
|
|
|
2017-09-26 16:00:30 +02:00
|
|
|
ReferencesResult collect(uint line, uint column, bool localReferences = false) const;
|
2017-06-09 12:19:09 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool isWithinTokenRange(CXToken token, uint line, uint column) const;
|
|
|
|
|
bool pointsToIdentifier(uint line, uint column, unsigned *tokenIndex) const;
|
|
|
|
|
bool matchesIdentifier(const CXToken &token, const Utf8String &identifier) const;
|
|
|
|
|
bool checkToken(unsigned index, const Utf8String &identifier, const Utf8String &usr) const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CXTranslationUnit m_cxTranslationUnit = nullptr;
|
|
|
|
|
CXToken *m_cxTokens = nullptr;
|
|
|
|
|
uint m_cxTokenCount = 0;
|
|
|
|
|
|
|
|
|
|
QVector<CXCursor> m_cxCursors;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ReferencesCollector::ReferencesCollector(CXTranslationUnit cxTranslationUnit)
|
|
|
|
|
: m_cxTranslationUnit(cxTranslationUnit)
|
|
|
|
|
{
|
|
|
|
|
const CXSourceRange range
|
|
|
|
|
= clang_getCursorExtent(clang_getTranslationUnitCursor(m_cxTranslationUnit));
|
|
|
|
|
clang_tokenize(cxTranslationUnit, range, &m_cxTokens, &m_cxTokenCount);
|
|
|
|
|
|
|
|
|
|
m_cxCursors.resize(static_cast<int>(m_cxTokenCount));
|
|
|
|
|
clang_annotateTokens(cxTranslationUnit, m_cxTokens, m_cxTokenCount, m_cxCursors.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReferencesCollector::~ReferencesCollector()
|
|
|
|
|
{
|
|
|
|
|
clang_disposeTokens(m_cxTranslationUnit, m_cxTokens, m_cxTokenCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ReferencesCollector::isWithinTokenRange(CXToken token, uint line, uint column) const
|
|
|
|
|
{
|
2017-11-29 16:08:06 +01:00
|
|
|
const SourceRange range {m_cxTranslationUnit, clang_getTokenExtent(m_cxTranslationUnit, token)};
|
2017-06-09 12:19:09 +02:00
|
|
|
return range.contains(line, column);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ReferencesCollector::pointsToIdentifier(uint line, uint column, unsigned *tokenIndex) const
|
|
|
|
|
{
|
|
|
|
|
for (uint i = 0; i < m_cxTokenCount; ++i) {
|
|
|
|
|
const CXToken token = m_cxTokens[i];
|
|
|
|
|
if (clang_getTokenKind(token) == CXToken_Identifier
|
|
|
|
|
&& isWithinTokenRange(token, line, column)) {
|
|
|
|
|
*tokenIndex = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ReferencesCollector::matchesIdentifier(const CXToken &token,
|
|
|
|
|
const Utf8String &identifier) const
|
|
|
|
|
{
|
|
|
|
|
const CXTokenKind tokenKind = clang_getTokenKind(token);
|
|
|
|
|
if (tokenKind == CXToken_Identifier) {
|
|
|
|
|
const Utf8String candidateIdentifier
|
|
|
|
|
= ClangString(clang_getTokenSpelling(m_cxTranslationUnit, token));
|
|
|
|
|
return candidateIdentifier == identifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ReferencesCollector::checkToken(unsigned index, const Utf8String &identifier,
|
|
|
|
|
const Utf8String &usr) const
|
|
|
|
|
{
|
|
|
|
|
const CXToken token = m_cxTokens[index];
|
|
|
|
|
if (!matchesIdentifier(token, identifier))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
{ // For debugging only
|
2018-08-21 10:23:12 +02:00
|
|
|
// const SourceRange range{m_cxTranslationUnit, clang_getTokenExtent(m_cxTranslationUnit, token)};
|
2017-06-09 12:19:09 +02:00
|
|
|
// const uint line = range.start().line();
|
|
|
|
|
// const ClangString spellingCs = clang_getTokenSpelling(m_cxTranslationUnit, token);
|
|
|
|
|
// const Utf8String spelling = spellingCs;
|
|
|
|
|
// qWarning() << "ReferencesCollector::checkToken:" << line << spelling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Cursor currentCursor(m_cxCursors[static_cast<int>(index)]);
|
2018-08-21 10:23:12 +02:00
|
|
|
const ReferencedCursor candidate = ReferencedCursor::find(currentCursor, token);
|
2017-06-09 12:19:09 +02:00
|
|
|
|
|
|
|
|
return candidate.usr() == usr;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-26 16:00:30 +02:00
|
|
|
ReferencesResult ReferencesCollector::collect(uint line, uint column, bool localReferences) const
|
2017-06-09 12:19:09 +02:00
|
|
|
{
|
|
|
|
|
ReferencesResult result;
|
|
|
|
|
|
|
|
|
|
unsigned index = 0;
|
|
|
|
|
if (!pointsToIdentifier(line, column, &index))
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
const Cursor cursorFromUser = m_cxCursors[static_cast<int>(index)];
|
2017-09-26 16:00:30 +02:00
|
|
|
|
2018-08-21 10:23:12 +02:00
|
|
|
const ReferencedCursor refCursor = ReferencedCursor::find(cursorFromUser, m_cxTokens[index]);
|
2017-06-09 12:19:09 +02:00
|
|
|
const Utf8String usr = refCursor.usr();
|
|
|
|
|
if (usr.isEmpty())
|
|
|
|
|
return result;
|
|
|
|
|
|
2017-09-26 16:00:30 +02:00
|
|
|
if (localReferences && !refCursor.isLocalVariable())
|
|
|
|
|
return result;
|
|
|
|
|
|
2017-06-09 12:19:09 +02:00
|
|
|
const CXToken token = m_cxTokens[index];
|
|
|
|
|
const Utf8String identifier = ClangString(clang_getTokenSpelling(m_cxTranslationUnit, token));
|
|
|
|
|
for (uint i = 0; i < m_cxTokenCount; ++i) {
|
|
|
|
|
if (checkToken(i, identifier, usr)) {
|
2017-11-29 16:08:06 +01:00
|
|
|
const SourceRange range {m_cxTranslationUnit,
|
|
|
|
|
clang_getTokenExtent(m_cxTranslationUnit, m_cxTokens[i])};
|
2017-06-09 12:19:09 +02:00
|
|
|
result.references.append(range);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.isLocalVariable = refCursor.isLocalVariable();
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
ReferencesResult collectReferences(CXTranslationUnit cxTranslationUnit,
|
|
|
|
|
uint line,
|
2017-09-26 16:00:30 +02:00
|
|
|
uint column,
|
|
|
|
|
bool localReferences)
|
2017-06-09 12:19:09 +02:00
|
|
|
{
|
|
|
|
|
ReferencesCollector collector(cxTranslationUnit);
|
2017-09-26 16:00:30 +02:00
|
|
|
return collector.collect(line, column, localReferences);
|
2017-06-09 12:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace ClangBackEnd
|