CppEditor: Move "move function definition" quickfixes to dedicated files

Change-Id: I4e963bd7fef1f1c9f0b69dde56298a52c74e01e4
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Christian Kandeler
2024-05-15 13:14:06 +02:00
parent 92f3731d78
commit e7505088f5
10 changed files with 2016 additions and 1952 deletions

View File

@@ -108,6 +108,7 @@ add_qtc_plugin(CppEditor
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h
resourcepreviewhoverhandler.cpp resourcepreviewhoverhandler.h
searchsymbols.cpp searchsymbols.h

View File

@@ -245,6 +245,8 @@ QtcPlugin {
"cppquickfixsettingswidget.h",
"moveclasstoownfile.cpp",
"moveclasstoownfile.h",
"movefunctiondefinition.cpp",
"movefunctiondefinition.h",
"removeusingnamespace.cpp",
"removeusingnamespace.h",
]

File diff suppressed because it is too large Load Diff

View File

@@ -152,40 +152,6 @@ private slots:
void testAddForwardDeclForUndefinedIdentifier_data();
void testAddForwardDeclForUndefinedIdentifier();
void testMoveFuncDefOutsideMemberFuncToCpp();
void testMoveFuncDefOutsideMemberFuncToCppInsideNS();
void testMoveFuncDefOutsideMemberFuncOutside1();
void testMoveFuncDefOutsideMemberFuncOutside2();
void testMoveFuncDefOutsideMemberFuncToCppNS();
void testMoveFuncDefOutsideMemberFuncToCppNSUsing();
void testMoveFuncDefOutsideMemberFuncOutsideWithNs();
void testMoveFuncDefOutsideFreeFuncToCpp();
void testMoveFuncDefOutsideFreeFuncToCppNS();
void testMoveFuncDefOutsideCtorWithInitialization1();
void testMoveFuncDefOutsideCtorWithInitialization2();
void testMoveFuncDefOutsideAfterClass();
void testMoveFuncDefOutsideRespectWsInOperatorNames1();
void testMoveFuncDefOutsideRespectWsInOperatorNames2();
void testMoveFuncDefOutsideMacroUses();
void testMoveFuncDefOutsideTemplate();
void testMoveFuncDefOutsideMemberFunctionTemplate();
void testMoveFuncDefOutsideTemplateSpecializedClass();
void testMoveFuncDefOutsideUnnamedTemplate();
void testMoveFuncDefOutsideMemberFuncToCppStatic();
void testMoveFuncDefOutsideMemberFuncToCppWithInlinePartOfName();
void testMoveFuncDefOutsideMixedQualifiers();
void testMoveAllFuncDefOutsideMemberFuncToCpp();
void testMoveAllFuncDefOutsideMemberFuncOutside();
void testMoveAllFuncDefOutsideDoNotTriggerOnBaseClass();
void testMoveAllFuncDefOutsideClassWithBaseClass();
void testMoveAllFuncDefOutsideIgnoreMacroCode();
void testMoveFuncDefToDecl_data();
void testMoveFuncDefToDecl();
void testMoveFuncDefToDeclMacroUses();
void testAssignToLocalVariableTemplates();
void testExtractFunction_data();

View File

@@ -24,6 +24,7 @@
#include "cppquickfixprojectsettings.h"
#include "convertqt4connect.h"
#include "moveclasstoownfile.h"
#include "movefunctiondefinition.h"
#include "removeusingnamespace.h"
#include <coreplugin/icore.h>
@@ -126,15 +127,6 @@ const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
namespace Internal {
QString inlinePrefix(const FilePath &targetFile, const std::function<bool()> &extraCondition = {})
{
if (ProjectFile::isHeader(ProjectFile::classify(targetFile.path()))
&& (!extraCondition || extraCondition())) {
return "inline ";
}
return {};
}
// In the following anonymous namespace all functions are collected, which could be of interest for
// different quick fixes.
namespace {
@@ -157,84 +149,6 @@ inline bool isQtStringTranslation(const QByteArray &id)
return id == "tr" || id == "trUtf8" || id == "translate" || id == "QT_TRANSLATE_NOOP";
}
Class *isMemberFunction(const LookupContext &context, Function *function)
{
QTC_ASSERT(function, return nullptr);
Scope *enclosingScope = function->enclosingScope();
while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
enclosingScope = enclosingScope->enclosingScope();
QTC_ASSERT(enclosingScope != nullptr, return nullptr);
const Name *functionName = function->name();
if (!functionName)
return nullptr;
if (!functionName->asQualifiedNameId())
return nullptr; // trying to add a declaration for a global function
const QualifiedNameId *q = functionName->asQualifiedNameId();
if (!q->base())
return nullptr;
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
const QList<Symbol *> symbols = binding->symbols();
for (Symbol *s : symbols) {
if (Class *matchingClass = s->asClass())
return matchingClass;
}
}
return nullptr;
}
Namespace *isNamespaceFunction(const LookupContext &context, Function *function)
{
QTC_ASSERT(function, return nullptr);
if (isMemberFunction(context, function))
return nullptr;
Scope *enclosingScope = function->enclosingScope();
while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
enclosingScope = enclosingScope->enclosingScope();
QTC_ASSERT(enclosingScope != nullptr, return nullptr);
const Name *functionName = function->name();
if (!functionName)
return nullptr;
// global namespace
if (!functionName->asQualifiedNameId()) {
const QList<Symbol *> symbols = context.globalNamespace()->symbols();
for (Symbol *s : symbols) {
if (Namespace *matchingNamespace = s->asNamespace())
return matchingNamespace;
}
return nullptr;
}
const QualifiedNameId *q = functionName->asQualifiedNameId();
if (!q->base())
return nullptr;
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
const QList<Symbol *> symbols = binding->symbols();
for (Symbol *s : symbols) {
if (Namespace *matchingNamespace = s->asNamespace())
return matchingNamespace;
}
}
return nullptr;
}
bool nameIncludesOperatorName(const Name *name)
{
return name->asOperatorNameId()
|| (name->asQualifiedNameId() && name->asQualifiedNameId()->name()->asOperatorNameId());
}
QString nameString(const NameAST *name)
{
return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
@@ -4895,561 +4809,6 @@ void ApplyDeclDefLinkChanges::doMatch(const CppQuickFixInterface &interface,
result << op;
}
namespace {
QString definitionSignature(const CppQuickFixInterface *assist,
FunctionDefinitionAST *functionDefinitionAST,
CppRefactoringFilePtr &baseFile,
CppRefactoringFilePtr &targetFile,
Scope *scope)
{
QTC_ASSERT(assist, return QString());
QTC_ASSERT(functionDefinitionAST, return QString());
QTC_ASSERT(scope, return QString());
Function *func = functionDefinitionAST->symbol;
QTC_ASSERT(func, return QString());
LookupContext cppContext(targetFile->cppDocument(), assist->snapshot());
ClassOrNamespace *cppCoN = cppContext.lookupType(scope);
if (!cppCoN)
cppCoN = cppContext.globalNamespace();
SubstitutionEnvironment env;
env.setContext(assist->context());
env.switchScope(func->enclosingScope());
UseMinimalNames q(cppCoN);
env.enter(&q);
Control *control = assist->context().bindings()->control().get();
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
oo.showFunctionSignatures = true;
oo.showReturnTypes = true;
oo.showArgumentNames = true;
oo.showEnclosingTemplate = true;
oo.showTemplateParameters = true;
oo.trailingReturnType = functionDefinitionAST->declarator
&& functionDefinitionAST->declarator->postfix_declarator_list
&& functionDefinitionAST->declarator->postfix_declarator_list->value
&& functionDefinitionAST->declarator->postfix_declarator_list
->value->asFunctionDeclarator()
&& functionDefinitionAST->declarator->postfix_declarator_list
->value->asFunctionDeclarator()->trailing_return_type;
const Name *name = func->name();
if (name && nameIncludesOperatorName(name)) {
CoreDeclaratorAST *coreDeclarator = functionDefinitionAST->declarator->core_declarator;
const QString operatorNameText = baseFile->textOf(coreDeclarator);
oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
}
const QString nameText = oo.prettyName(LookupContext::minimalName(func, cppCoN, control));
oo.showTemplateParameters = false;
const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
return oo.prettyType(tn, nameText);
}
class MoveFuncDefRefactoringHelper
{
public:
enum MoveType {
MoveOutside,
MoveToCppFile,
MoveOutsideMemberToCppFile
};
MoveFuncDefRefactoringHelper(CppQuickFixOperation *operation, MoveType type,
const FilePath &fromFile, const FilePath &toFile)
: m_operation(operation), m_type(type), m_changes(m_operation->snapshot())
{
m_fromFile = m_changes.cppFile(fromFile);
m_toFile = (m_type == MoveOutside) ? m_fromFile : m_changes.cppFile(toFile);
}
void performMove(FunctionDefinitionAST *funcAST)
{
// Determine file, insert position and scope
InsertionLocation l = insertLocationForMethodDefinition(
funcAST->symbol, false, NamespaceHandling::Ignore,
m_changes, m_toFile->filePath());
const QString prefix = l.prefix();
const QString suffix = l.suffix();
const int insertPos = m_toFile->position(l.line(), l.column());
Scope *scopeAtInsertPos = m_toFile->cppDocument()->scopeAt(l.line(), l.column());
// construct definition
const QString funcDec = inlinePrefix(m_toFile->filePath(), [this] { return m_type == MoveOutside; })
+ definitionSignature(m_operation, funcAST, m_fromFile, m_toFile,
scopeAtInsertPos);
QString funcDef = prefix + funcDec;
const int startPosition = m_fromFile->endOf(funcAST->declarator);
const int endPosition = m_fromFile->endOf(funcAST);
funcDef += m_fromFile->textOf(startPosition, endPosition);
funcDef += suffix;
// insert definition at new position
m_toFileChangeSet.insert(insertPos, funcDef);
m_toFile->setOpenEditor(true, insertPos);
// remove definition from fromFile
if (m_type == MoveOutsideMemberToCppFile) {
m_fromFileChangeSet.remove(m_fromFile->range(funcAST));
} else {
QString textFuncDecl = m_fromFile->textOf(funcAST);
textFuncDecl.truncate(startPosition - m_fromFile->startOf(funcAST));
if (textFuncDecl.left(7) == QLatin1String("inline "))
textFuncDecl = textFuncDecl.mid(7);
else
textFuncDecl.replace(" inline ", QLatin1String(" "));
textFuncDecl = textFuncDecl.trimmed() + QLatin1Char(';');
m_fromFileChangeSet.replace(m_fromFile->range(funcAST), textFuncDecl);
}
}
void applyChanges()
{
if (!m_toFileChangeSet.isEmpty()) {
m_toFile->setChangeSet(m_toFileChangeSet);
m_toFile->apply();
}
if (!m_fromFileChangeSet.isEmpty()) {
m_fromFile->setChangeSet(m_fromFileChangeSet);
m_fromFile->apply();
}
}
private:
CppQuickFixOperation *m_operation;
MoveType m_type;
CppRefactoringChanges m_changes;
CppRefactoringFilePtr m_fromFile;
CppRefactoringFilePtr m_toFile;
ChangeSet m_fromFileChangeSet;
ChangeSet m_toFileChangeSet;
};
class MoveFuncDefOutsideOp : public CppQuickFixOperation
{
public:
MoveFuncDefOutsideOp(const CppQuickFixInterface &interface,
MoveFuncDefRefactoringHelper::MoveType type,
FunctionDefinitionAST *funcDef, const FilePath &cppFilePath)
: CppQuickFixOperation(interface, 0)
, m_funcDef(funcDef)
, m_type(type)
, m_cppFilePath(cppFilePath)
, m_headerFilePath(funcDef->symbol->filePath())
{
if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
setDescription(Tr::tr("Move Definition Outside Class"));
} else {
const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
}
}
void perform() override
{
MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
helper.performMove(m_funcDef);
helper.applyChanges();
}
private:
FunctionDefinitionAST *m_funcDef;
MoveFuncDefRefactoringHelper::MoveType m_type;
const FilePath m_cppFilePath;
const FilePath m_headerFilePath;
};
} // anonymous namespace
void MoveFuncDefOutside::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
SimpleDeclarationAST *classAST = nullptr;
FunctionDefinitionAST *funcAST = nullptr;
bool moveOutsideMemberDefinition = false;
const int pathSize = path.size();
for (int idx = 1; idx < pathSize; ++idx) {
if ((funcAST = path.at(idx)->asFunctionDefinition())) {
// check cursor position
if (idx != pathSize - 1 // Do not allow "void a() @ {..."
&& funcAST->function_body
&& !interface.isCursorOn(funcAST->function_body)) {
if (path.at(idx - 1)->asTranslationUnit()) { // normal function
if (idx + 3 < pathSize && path.at(idx + 3)->asQualifiedName()) // Outside member
moveOutsideMemberDefinition = true; // definition
break;
}
if (idx > 1) {
if ((classAST = path.at(idx - 2)->asSimpleDeclaration())) // member function
break;
if (path.at(idx - 2)->asNamespace()) // normal function in namespace
break;
}
if (idx > 2 && path.at(idx - 1)->asTemplateDeclaration()) {
if ((classAST = path.at(idx - 3)->asSimpleDeclaration())) // member template
break;
}
}
funcAST = nullptr;
}
}
if (!funcAST || !funcAST->symbol)
return;
bool isHeaderFile = false;
const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
if (isHeaderFile && !cppFileName.isEmpty()) {
const MoveFuncDefRefactoringHelper::MoveType type = moveOutsideMemberDefinition
? MoveFuncDefRefactoringHelper::MoveOutsideMemberToCppFile
: MoveFuncDefRefactoringHelper::MoveToCppFile;
result << new MoveFuncDefOutsideOp(interface, type, funcAST, cppFileName);
}
if (classAST)
result << new MoveFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
funcAST, FilePath());
return;
}
namespace {
class MoveAllFuncDefOutsideOp : public CppQuickFixOperation
{
public:
MoveAllFuncDefOutsideOp(const CppQuickFixInterface &interface,
MoveFuncDefRefactoringHelper::MoveType type,
ClassSpecifierAST *classDef, const FilePath &cppFileName)
: CppQuickFixOperation(interface, 0)
, m_type(type)
, m_classDef(classDef)
, m_cppFilePath(cppFileName)
, m_headerFilePath(classDef->symbol->filePath())
{
if (m_type == MoveFuncDefRefactoringHelper::MoveOutside) {
setDescription(Tr::tr("Definitions Outside Class"));
} else {
const FilePath resolved = m_cppFilePath.relativePathFrom(m_headerFilePath.parentDir());
setDescription(Tr::tr("Move All Function Definitions to %1")
.arg(resolved.displayName()));
}
}
void perform() override
{
MoveFuncDefRefactoringHelper helper(this, m_type, m_headerFilePath, m_cppFilePath);
for (DeclarationListAST *it = m_classDef->member_specifier_list; it; it = it->next) {
if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
if (funcAST->symbol && !funcAST->symbol->isGenerated())
helper.performMove(funcAST);
}
}
helper.applyChanges();
}
private:
MoveFuncDefRefactoringHelper::MoveType m_type;
ClassSpecifierAST *m_classDef;
const FilePath m_cppFilePath;
const FilePath m_headerFilePath;
};
} // anonymous namespace
void MoveAllFuncDefOutside::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
ClassSpecifierAST * const classAST = astForClassOperations(interface);
if (!classAST)
return;
// Determine if the class has at least one function definition
bool classContainsFunctions = false;
for (DeclarationListAST *it = classAST->member_specifier_list; it; it = it->next) {
if (FunctionDefinitionAST *funcAST = it->value->asFunctionDefinition()) {
if (funcAST->symbol && !funcAST->symbol->isGenerated()) {
classContainsFunctions = true;
break;
}
}
}
if (!classContainsFunctions)
return;
bool isHeaderFile = false;
const FilePath cppFileName = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
if (isHeaderFile && !cppFileName.isEmpty()) {
result << new MoveAllFuncDefOutsideOp(interface,
MoveFuncDefRefactoringHelper::MoveToCppFile,
classAST, cppFileName);
}
result << new MoveAllFuncDefOutsideOp(interface, MoveFuncDefRefactoringHelper::MoveOutside,
classAST, FilePath());
}
namespace {
class MoveFuncDefToDeclOp : public CppQuickFixOperation
{
public:
enum Type { Push, Pull };
MoveFuncDefToDeclOp(const CppQuickFixInterface &interface,
const FilePath &fromFilePath, const FilePath &toFilePath,
FunctionDefinitionAST *funcAst, Function *func, const QString &declText,
const ChangeSet::Range &fromRange,
const ChangeSet::Range &toRange,
Type type)
: CppQuickFixOperation(interface, 0)
, m_fromFilePath(fromFilePath)
, m_toFilePath(toFilePath)
, m_funcAST(funcAst)
, m_func(func)
, m_declarationText(declText)
, m_fromRange(fromRange)
, m_toRange(toRange)
{
if (type == Type::Pull) {
setDescription(Tr::tr("Move Definition Here"));
} else if (m_toFilePath == m_fromFilePath) {
setDescription(Tr::tr("Move Definition to Class"));
} else {
const FilePath resolved = m_toFilePath.relativePathFrom(m_fromFilePath.parentDir());
setDescription(Tr::tr("Move Definition to %1").arg(resolved.displayName()));
}
}
private:
void perform() override
{
CppRefactoringChanges refactoring(snapshot());
CppRefactoringFilePtr fromFile = refactoring.cppFile(m_fromFilePath);
CppRefactoringFilePtr toFile = refactoring.cppFile(m_toFilePath);
ensureFuncDefAstAndRange(*fromFile);
if (!m_funcAST)
return;
const QString wholeFunctionText = m_declarationText
+ fromFile->textOf(fromFile->endOf(m_funcAST->declarator),
fromFile->endOf(m_funcAST->function_body));
// Replace declaration with function and delete old definition
ChangeSet toTarget;
toTarget.replace(m_toRange, wholeFunctionText);
if (m_toFilePath == m_fromFilePath)
toTarget.remove(m_fromRange);
toFile->setChangeSet(toTarget);
toFile->setOpenEditor(true, m_toRange.start);
toFile->apply();
if (m_toFilePath != m_fromFilePath) {
ChangeSet fromTarget;
fromTarget.remove(m_fromRange);
fromFile->setChangeSet(fromTarget);
fromFile->apply();
}
}
void ensureFuncDefAstAndRange(CppRefactoringFile &defFile)
{
if (m_funcAST) {
QTC_CHECK(m_fromRange.end > m_fromRange.start);
return;
}
QTC_ASSERT(m_func, return);
const QList<AST *> astPath = ASTPath(defFile.cppDocument())(m_func->line(),
m_func->column());
if (astPath.isEmpty())
return;
for (auto it = std::rbegin(astPath); it != std::rend(astPath); ++it) {
m_funcAST = (*it)->asFunctionDefinition();
if (!m_funcAST)
continue;
AST *astForRange = m_funcAST;
const auto prev = std::next(it);
if (prev != std::rend(astPath)) {
if (const auto templAst = (*prev)->asTemplateDeclaration())
astForRange = templAst;
}
m_fromRange = defFile.range(astForRange);
return;
}
}
const FilePath m_fromFilePath;
const FilePath m_toFilePath;
FunctionDefinitionAST *m_funcAST;
Function *m_func;
const QString m_declarationText;
ChangeSet::Range m_fromRange;
const ChangeSet::Range m_toRange;
};
} // anonymous namespace
void MoveFuncDefToDeclPush::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
AST *completeDefAST = nullptr;
FunctionDefinitionAST *funcAST = nullptr;
const int pathSize = path.size();
for (int idx = 1; idx < pathSize; ++idx) {
if ((funcAST = path.at(idx)->asFunctionDefinition())) {
AST *enclosingAST = path.at(idx - 1);
if (enclosingAST->asClassSpecifier())
return;
// check cursor position
if (idx != pathSize - 1 // Do not allow "void a() @ {..."
&& funcAST->function_body
&& !interface.isCursorOn(funcAST->function_body)) {
completeDefAST = enclosingAST->asTemplateDeclaration() ? enclosingAST : funcAST;
break;
}
funcAST = nullptr;
}
}
if (!funcAST || !funcAST->symbol)
return;
const CppRefactoringChanges refactoring(interface.snapshot());
const CppRefactoringFilePtr defFile = refactoring.cppFile(interface.filePath());
const ChangeSet::Range defRange = defFile->range(completeDefAST);
// Determine declaration (file, range, text);
ChangeSet::Range declRange;
QString declText;
FilePath declFilePath;
Function *func = funcAST->symbol;
if (Class *matchingClass = isMemberFunction(interface.context(), func)) {
// Dealing with member functions
const QualifiedNameId *qName = func->name()->asQualifiedNameId();
for (Symbol *symbol = matchingClass->find(qName->identifier());
symbol; symbol = symbol->next()) {
Symbol *s = symbol;
if (func->enclosingScope()->asTemplate()) {
if (const Template *templ = s->type()->asTemplateType()) {
if (Symbol *decl = templ->declaration()) {
if (decl->type()->asFunctionType())
s = decl;
}
}
}
if (!s->name()
|| !qName->identifier()->match(s->identifier())
|| !s->type()->asFunctionType()
|| !s->type().match(func->type())
|| s->asFunction()) {
continue;
}
declFilePath = matchingClass->filePath();
const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
ASTPath astPath(declFile->cppDocument());
const QList<AST *> path = astPath(s->line(), s->column());
for (int idx = path.size() - 1; idx > 0; --idx) {
AST *node = path.at(idx);
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
if (simpleDecl->symbols && !simpleDecl->symbols->next) {
declRange = declFile->range(simpleDecl);
declText = declFile->textOf(simpleDecl);
declText.remove(-1, 1); // remove ';' from declaration text
break;
}
}
}
if (!declText.isEmpty())
break;
}
} else if (Namespace *matchingNamespace = isNamespaceFunction(interface.context(), func)) {
// Dealing with free functions
bool isHeaderFile = false;
declFilePath = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
if (isHeaderFile)
return;
const CppRefactoringFilePtr declFile = refactoring.cppFile(declFilePath);
const LookupContext lc(declFile->cppDocument(), interface.snapshot());
const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
for (const LookupItem &candidate : candidates) {
if (Symbol *s = candidate.declaration()) {
if (s->asDeclaration()) {
ASTPath astPath(declFile->cppDocument());
const QList<AST *> path = astPath(s->line(), s->column());
for (AST *node : path) {
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
declRange = declFile->range(simpleDecl);
declText = declFile->textOf(simpleDecl);
declText.remove(-1, 1); // remove ';' from declaration text
break;
}
}
}
}
if (!declText.isEmpty()) {
declText.prepend(inlinePrefix(declFilePath));
break;
}
}
}
if (!declFilePath.isEmpty() && !declText.isEmpty())
result << new MoveFuncDefToDeclOp(interface,
interface.filePath(),
declFilePath,
funcAST, func, declText,
defRange, declRange, MoveFuncDefToDeclOp::Push);
}
void MoveFuncDefToDeclPull::doMatch(const CppQuickFixInterface &interface,
QuickFixOperations &result)
{
const QList<AST *> &path = interface.path();
for (auto it = std::rbegin(path); it != std::rend(path); ++it) {
SimpleDeclarationAST * const simpleDecl = (*it)->asSimpleDeclaration();
if (!simpleDecl)
continue;
const auto prev = std::next(it);
if (prev != std::rend(path) && (*prev)->asStatement())
return;
if (!simpleDecl->symbols || !simpleDecl->symbols->value || simpleDecl->symbols->next)
return;
Declaration * const decl = simpleDecl->symbols->value->asDeclaration();
if (!decl)
return;
Function * const funcDecl = decl->type()->asFunctionType();
if (!funcDecl)
return;
if (funcDecl->isSignal() || funcDecl->isPureVirtual() || funcDecl->isFriend())
return;
// Is there a definition?
SymbolFinder symbolFinder;
Function * const funcDef = symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
true);
if (!funcDef)
return;
QString declText = interface.currentFile()->textOf(simpleDecl);
declText.chop(1); // semicolon
declText.prepend(inlinePrefix(interface.filePath(), [funcDecl] {
return !funcDecl->enclosingScope()->asClass();
}));
result << new MoveFuncDefToDeclOp(interface, funcDef->filePath(), decl->filePath(), nullptr,
funcDef, declText, {},
interface.currentFile()->range(simpleDecl),
MoveFuncDefToDeclOp::Pull);
return;
}
}
namespace {
class AssignToLocalVariableOperation : public CppQuickFixOperation
@@ -6639,11 +5998,6 @@ void createCppQuickFixes()
new AddDeclarationForUndeclaredIdentifier;
new InsertDefsFromDecls;
new MoveFuncDefOutside;
new MoveAllFuncDefOutside;
new MoveFuncDefToDeclPush;
new MoveFuncDefToDeclPull;
new AssignToLocalVariable;
registerInsertVirtualMethodsQuickfix();
@@ -6651,6 +6005,7 @@ void createCppQuickFixes()
registerRemoveUsingNamespaceQuickfix();
registerCodeGenerationQuickfixes();
registerConvertQt4ConnectQuickfix();
registerMoveFunctionDefinitionQuickfixes();
new OptimizeForLoop;

View File

@@ -462,43 +462,6 @@ public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*!
Moves the definition of a member function outside the class or moves the definition of a member
function or a normal function to the implementation file.
*/
class MoveFuncDefOutside: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*!
Moves all member function definitions outside the class or to the implementation file.
*/
class MoveAllFuncDefOutside: public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*!
Moves the definition of a function to its declaration, with the cursor on the definition.
*/
class MoveFuncDefToDeclPush : public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*!
Moves the definition of a function to its declaration, with the cursor on the declaration.
*/
class MoveFuncDefToDeclPull : public CppQuickFixFactory
{
public:
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
/*!
Assigns the return value of a function call or a new expression to a local variable
*/

View File

@@ -3,6 +3,7 @@
#include "cppquickfixhelpers.h"
#include "../cppprojectfile.h"
#include "../includeutils.h"
#include "cppquickfixassistant.h"
@@ -65,4 +66,91 @@ ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface)
return nullptr;
}
bool nameIncludesOperatorName(const Name *name)
{
return name->asOperatorNameId()
|| (name->asQualifiedNameId() && name->asQualifiedNameId()->name()->asOperatorNameId());
}
QString inlinePrefix(const FilePath &targetFile, const std::function<bool()> &extraCondition)
{
if (ProjectFile::isHeader(ProjectFile::classify(targetFile.path()))
&& (!extraCondition || extraCondition())) {
return "inline ";
}
return {};
}
Class *isMemberFunction(const CPlusPlus::LookupContext &context, CPlusPlus::Function *function)
{
QTC_ASSERT(function, return nullptr);
Scope *enclosingScope = function->enclosingScope();
while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
enclosingScope = enclosingScope->enclosingScope();
QTC_ASSERT(enclosingScope != nullptr, return nullptr);
const Name *functionName = function->name();
if (!functionName)
return nullptr;
if (!functionName->asQualifiedNameId())
return nullptr; // trying to add a declaration for a global function
const QualifiedNameId *q = functionName->asQualifiedNameId();
if (!q->base())
return nullptr;
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
const QList<Symbol *> symbols = binding->symbols();
for (Symbol *s : symbols) {
if (Class *matchingClass = s->asClass())
return matchingClass;
}
}
return nullptr;
}
CPlusPlus::Namespace *isNamespaceFunction(
const CPlusPlus::LookupContext &context, CPlusPlus::Function *function)
{
QTC_ASSERT(function, return nullptr);
if (isMemberFunction(context, function))
return nullptr;
Scope *enclosingScope = function->enclosingScope();
while (!(enclosingScope->asNamespace() || enclosingScope->asClass()))
enclosingScope = enclosingScope->enclosingScope();
QTC_ASSERT(enclosingScope != nullptr, return nullptr);
const Name *functionName = function->name();
if (!functionName)
return nullptr;
// global namespace
if (!functionName->asQualifiedNameId()) {
const QList<Symbol *> symbols = context.globalNamespace()->symbols();
for (Symbol *s : symbols) {
if (Namespace *matchingNamespace = s->asNamespace())
return matchingNamespace;
}
return nullptr;
}
const QualifiedNameId *q = functionName->asQualifiedNameId();
if (!q->base())
return nullptr;
if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
const QList<Symbol *> symbols = binding->symbols();
for (Symbol *s : symbols) {
if (Namespace *matchingNamespace = s->asNamespace())
return matchingNamespace;
}
}
return nullptr;
}
} // namespace CppEditor::Internal

View File

@@ -25,4 +25,15 @@ void insertNewIncludeDirective(
// correspond to an AST of its own, i.e. on "empty space".
CPlusPlus::ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface);
bool nameIncludesOperatorName(const CPlusPlus::Name *name);
QString inlinePrefix(const Utils::FilePath &targetFile,
const std::function<bool()> &extraCondition = {});
CPlusPlus::Class *isMemberFunction(
const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
CPlusPlus::Namespace *isNamespaceFunction(
const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
} // namespace CppEditor::Internal

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
namespace CppEditor::Internal {
void registerMoveFunctionDefinitionQuickfixes();
} // namespace CppEditor::Internal