Files
qt-creator/src/plugins/cpptools/builtincursorinfo.cpp
Nikolai Kosjar 12f4b1ee86 CppTools: Modernize
Change-Id: I78af9cd4ccddfa4ed744dce96b772ae5644c89d2
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
2019-02-08 08:43:32 +00:00

369 lines
12 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 "builtincursorinfo.h"
#include "cppcanonicalsymbol.h"
#include "cppcursorinfo.h"
#include "cpplocalsymbols.h"
#include "cppmodelmanager.h"
#include "cppsemanticinfo.h"
#include "cpptoolsreuse.h"
#include <cplusplus/CppDocument.h>
#include <cplusplus/Macro.h>
#include <cplusplus/TranslationUnit.h>
#include <utils/textutils.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QTextBlock>
using namespace CPlusPlus;
using SemanticUses = QList<CppTools::SemanticInfo::Use>;
namespace CppTools {
namespace {
CursorInfo::Range toRange(const SemanticInfo::Use &use)
{
return {use.line, use.column, use.length};
}
CursorInfo::Range toRange(int tokenIndex, TranslationUnit *translationUnit)
{
unsigned line, column;
translationUnit->getTokenPosition(static_cast<unsigned>(tokenIndex), &line, &column);
if (column)
--column; // adjust the column position.
return {line,
column + 1,
translationUnit->tokenAt(static_cast<unsigned>(tokenIndex)).utf16chars()};
}
CursorInfo::Range toRange(const QTextCursor &textCursor,
unsigned utf16offset,
unsigned length)
{
QTextCursor cursor(textCursor.document());
cursor.setPosition(static_cast<int>(utf16offset));
const QTextBlock textBlock = cursor.block();
return {static_cast<unsigned>(textBlock.blockNumber() + 1),
static_cast<unsigned>(cursor.position() - textBlock.position() + 1),
length};
}
CursorInfo::Ranges toRanges(const SemanticUses &uses)
{
CursorInfo::Ranges ranges;
ranges.reserve(uses.size());
for (const SemanticInfo::Use &use : uses)
ranges.append(toRange(use));
return ranges;
}
CursorInfo::Ranges toRanges(const QList<int> &tokenIndices, TranslationUnit *translationUnit)
{
CursorInfo::Ranges ranges;
ranges.reserve(tokenIndices.size());
for (int reference : tokenIndices)
ranges.append(toRange(reference, translationUnit));
return ranges;
}
class FunctionDefinitionUnderCursor: protected ASTVisitor
{
unsigned m_line = 0;
unsigned m_column = 0;
DeclarationAST *m_functionDefinition = nullptr;
public:
explicit FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
: ASTVisitor(translationUnit)
{ }
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
{
m_functionDefinition = nullptr;
m_line = line;
m_column = column;
accept(ast);
return m_functionDefinition;
}
protected:
bool preVisit(AST *ast) override
{
if (m_functionDefinition)
return false;
if (FunctionDefinitionAST *def = ast->asFunctionDefinition())
return checkDeclaration(def);
if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
if (method->function_body)
return checkDeclaration(method);
}
return true;
}
private:
bool checkDeclaration(DeclarationAST *ast)
{
unsigned startLine, startColumn;
unsigned endLine, endColumn;
getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
if (m_line > startLine || (m_line == startLine && m_column >= startColumn)) {
if (m_line < endLine || (m_line == endLine && m_column < endColumn)) {
m_functionDefinition = ast;
return false;
}
}
return true;
}
};
class FindUses
{
public:
static CursorInfo find(const Document::Ptr document, const Snapshot &snapshot,
int line, int column, Scope *scope, const QString &expression)
{
FindUses findUses(document, snapshot, line, column, scope, expression);
return findUses.doFind();
}
private:
FindUses(const Document::Ptr document, const Snapshot &snapshot, int line, int column,
Scope *scope, const QString &expression)
: m_document(document)
, m_line(line)
, m_column(column)
, m_scope(scope)
, m_expression(expression)
, m_snapshot(snapshot)
{
}
CursorInfo doFind() const
{
CursorInfo result;
// findLocalUses operates with 1-based line and 0-based column
const CppTools::SemanticInfo::LocalUseMap localUses
= BuiltinCursorInfo::findLocalUses(m_document, m_line, m_column - 1);
result.localUses = localUses;
splitLocalUses(localUses, &result.useRanges, &result.unusedVariablesRanges);
if (!result.useRanges.isEmpty()) {
result.areUseRangesForLocalVariable = true;
return result;
}
result.useRanges = findReferences();
result.areUseRangesForLocalVariable = false;
return result; // OK, result.unusedVariablesRanges will be passed on
}
void splitLocalUses(const CppTools::SemanticInfo::LocalUseMap &uses,
CursorInfo::Ranges *rangesForLocalVariableUnderCursor,
CursorInfo::Ranges *rangesForLocalUnusedVariables) const
{
QTC_ASSERT(rangesForLocalVariableUnderCursor, return);
QTC_ASSERT(rangesForLocalUnusedVariables, return);
LookupContext context(m_document, m_snapshot);
CppTools::SemanticInfo::LocalUseIterator it(uses);
while (it.hasNext()) {
it.next();
const SemanticUses &uses = it.value();
bool good = false;
foreach (const CppTools::SemanticInfo::Use &use, uses) {
const auto l = static_cast<unsigned>(m_line);
const auto c = static_cast<unsigned>(m_column);
if (l == use.line && c >= use.column && c <= (use.column + use.length)) {
good = true;
break;
}
}
if (uses.size() == 1) {
if (!CppTools::isOwnershipRAIIType(it.key(), context))
rangesForLocalUnusedVariables->append(toRanges(uses)); // unused declaration
} else if (good && rangesForLocalVariableUnderCursor->isEmpty()) {
rangesForLocalVariableUnderCursor->append(toRanges(uses));
}
}
}
CursorInfo::Ranges findReferences() const
{
CursorInfo::Ranges result;
if (!m_scope || m_expression.isEmpty())
return result;
TypeOfExpression typeOfExpression;
Snapshot theSnapshot = m_snapshot;
theSnapshot.insert(m_document);
typeOfExpression.init(m_document, theSnapshot);
typeOfExpression.setExpandTemplates(true);
if (Symbol *s = CanonicalSymbol::canonicalSymbol(m_scope, m_expression, typeOfExpression)) {
CppTools::CppModelManager *mmi = CppTools::CppModelManager::instance();
const QList<int> tokenIndices = mmi->references(s, typeOfExpression.context());
result = toRanges(tokenIndices, m_document->translationUnit());
}
return result;
}
private:
// Shared
Document::Ptr m_document;
// For local use calculation
int m_line;
int m_column;
// For references calculation
Scope *m_scope;
QString m_expression;
Snapshot m_snapshot;
};
bool isSemanticInfoValidExceptLocalUses(const SemanticInfo &semanticInfo, int revision)
{
return semanticInfo.doc
&& semanticInfo.revision == static_cast<unsigned>(revision)
&& !semanticInfo.snapshot.isEmpty();
}
bool isMacroUseOf(const Document::MacroUse &marcoUse, const Macro &macro)
{
const Macro &candidate = marcoUse.macro();
return candidate.line() == macro.line()
&& candidate.utf16CharOffset() == macro.utf16CharOffset()
&& candidate.length() == macro.length()
&& candidate.fileName() == macro.fileName();
}
bool handleMacroCase(const Document::Ptr document,
const QTextCursor &textCursor,
CursorInfo::Ranges *ranges)
{
QTC_ASSERT(ranges, return false);
const Macro *macro = CppTools::findCanonicalMacro(textCursor, document);
if (!macro)
return false;
const unsigned length = static_cast<unsigned>(macro->nameToQString().size());
// Macro definition
if (macro->fileName() == document->fileName())
ranges->append(toRange(textCursor, macro->utf16CharOffset(), length));
// Other macro uses
foreach (const Document::MacroUse &use, document->macroUses()) {
if (isMacroUseOf(use, *macro))
ranges->append(toRange(textCursor, use.utf16charsBegin(), length));
}
return true;
}
} // anonymous namespace
QFuture<CursorInfo> BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoParams)
{
QFuture<CursorInfo> nothing;
const SemanticInfo semanticInfo = cursorInfoParams.semanticInfo;
const int currentDocumentRevision = cursorInfoParams.textCursor.document()->revision();
if (!isSemanticInfoValidExceptLocalUses(semanticInfo, currentDocumentRevision))
return nothing;
const Document::Ptr document = semanticInfo.doc;
const Snapshot snapshot = semanticInfo.snapshot;
if (!document)
return nothing;
QTC_ASSERT(document->translationUnit(), return nothing);
QTC_ASSERT(document->translationUnit()->ast(), return nothing);
QTC_ASSERT(!snapshot.isEmpty(), return nothing);
CursorInfo::Ranges ranges;
if (handleMacroCase(document, cursorInfoParams.textCursor, &ranges)) {
CursorInfo result;
result.useRanges = ranges;
result.areUseRangesForLocalVariable = false;
QFutureInterface<CursorInfo> fi;
fi.reportResult(result);
fi.reportFinished();
return fi.future();
}
const QTextCursor &textCursor = cursorInfoParams.textCursor;
int line, column;
Utils::Text::convertPosition(textCursor.document(), textCursor.position(), &line, &column);
CanonicalSymbol canonicalSymbol(document, snapshot);
QString expression;
Scope *scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression);
return Utils::runAsync(&FindUses::find, document, snapshot, line, column, scope, expression);
}
CppTools::SemanticInfo::LocalUseMap
BuiltinCursorInfo::findLocalUses(const Document::Ptr &document, int line, int column)
{
if (!document || !document->translationUnit() || !document->translationUnit()->ast())
return SemanticInfo::LocalUseMap();
AST *ast = document->translationUnit()->ast();
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(document->translationUnit());
DeclarationAST *declaration = functionDefinitionUnderCursor(ast,
static_cast<unsigned>(line),
static_cast<unsigned>(column));
return CppTools::LocalSymbols(document, declaration).uses;
}
} // namespace CppTools