Clangd: Fix autocompletion for old style SIGNAL and SLOT

Fixes: QTCREATORBUG-20737
Change-Id: If6d3c6ea5924537386eca81d90d4bb1e8f1a1466
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Artem Sokolovskii
2022-06-20 12:57:23 +02:00
parent c899793b37
commit 0087bd492f
12 changed files with 114 additions and 36 deletions

View File

@@ -52,6 +52,7 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/vcsmanager.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TypeOfExpression.h>
#include <extensionsystem/pluginmanager.h>
@@ -338,11 +339,29 @@ void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
inNextSplit);
}
bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByteArray &content,
int position) const
int argumentPositionOf(const AST *last, const CallAST *callAst)
{
if (!callAst || !callAst->expression_list)
return false;
int num = 0;
for (ExpressionListAST *it = callAst->expression_list; it; it = it->next) {
++num;
const ExpressionAST *const arg = it->value;
if (arg->firstToken() <= last->firstToken()
&& arg->lastToken() >= last->lastToken()) {
return num;
}
}
return 0;
}
SignalSlotType CppModelManager::getSignalSlotType(const QString &filePath,
const QByteArray &content,
int position) const
{
if (content.isEmpty())
return false;
return SignalSlotType::None;
// Insert a dummy prefix if we don't have a real one. Otherwise the AST path will not contain
// anything after the CallAST.
@@ -360,26 +379,17 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt
// Are we at the second argument of a function call?
const QList<AST *> path = ASTPath(document)(cursor);
if (path.isEmpty() || !path.last()->asSimpleName())
return false;
if (path.isEmpty())
return SignalSlotType::None;
const CallAST *callAst = nullptr;
for (auto it = path.crbegin(); it != path.crend(); ++it) {
if ((callAst = (*it)->asCall()))
break;
}
if (!callAst)
return false;
if (!callAst->expression_list || !callAst->expression_list->next)
return false;
const ExpressionAST * const secondArg = callAst->expression_list->next->value;
if (secondArg->firstToken() > path.last()->firstToken()
|| secondArg->lastToken() < path.last()->lastToken()) {
return false;
}
// Is the function called "connect" or "disconnect"?
if (!callAst->base_expression)
return false;
if (!callAst || !callAst->base_expression)
return SignalSlotType::None;
Scope *scope = document->globalNamespace();
for (auto it = path.crbegin(); it != path.crend(); ++it) {
if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) {
@@ -398,7 +408,7 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt
exprType.init(document, snapshot);
const QList<LookupItem> typeMatches = exprType(ast->base_expression, document, scope);
if (typeMatches.isEmpty())
return false;
return SignalSlotType::None;
const std::function<const NamedType *(const FullySpecifiedType &)> getNamedType
= [&getNamedType](const FullySpecifiedType &type ) -> const NamedType * {
Type * const t = type.type();
@@ -414,22 +424,22 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt
if (!namedType && typeMatches.first().declaration())
namedType = getNamedType(typeMatches.first().declaration()->type());
if (!namedType)
return false;
return SignalSlotType::None;
const ClassOrNamespace * const result = context.lookupType(namedType->name(), scope);
if (!result)
return false;
return SignalSlotType::None;
scope = result->rootClass();
if (!scope)
return false;
return SignalSlotType::None;
}
if (!nameAst || !nameAst->name)
return false;
return SignalSlotType::None;
const Identifier * const id = nameAst->name->identifier();
if (!id)
return false;
return SignalSlotType::None;
const QString funcName = QString::fromUtf8(id->chars(), id->size());
if (funcName != "connect" && funcName != "disconnect")
return false;
return SignalSlotType::None;
// Is the function a member function of QObject?
const QList<LookupItem> matches = context.lookup(nameAst->name, scope);
@@ -440,11 +450,29 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt
if (!klass || !klass->name())
continue;
const Identifier * const classId = klass->name()->identifier();
if (classId && QString::fromUtf8(classId->chars(), classId->size()) == "QObject")
return true;
}
if (classId && QString::fromUtf8(classId->chars(), classId->size()) == "QObject") {
QString expression;
LanguageFeatures features = LanguageFeatures::defaultFeatures();
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(features);
for (int i = cursor.position(); i > 0; --i)
if (textDocument.characterAt(i) == '(') {
cursor.setPosition(i);
break;
}
return false;
expression = expressionUnderCursor(cursor);
const int argumentPosition = argumentPositionOf(path.last(), callAst);
if ((expression.endsWith(QLatin1String("SIGNAL"))
&& (argumentPosition == 2 || argumentPosition == 4))
|| (expression.endsWith(QLatin1String("SLOT")) && argumentPosition == 4))
return SignalSlotType::OldStyleSignal;
if (argumentPosition == 2)
return SignalSlotType::NewStyleSignal;
}
}
return SignalSlotType::None;
}
FollowSymbolUnderCursor &CppModelManager::builtinFollowSymbol()