forked from qt-creator/qt-creator
CppEditor: Move AssignToLocalVariable quickfix to its own files
Change-Id: Ia0072846e97e4db50c60acf19af5e695e8472a6b Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -95,6 +95,7 @@ add_qtc_plugin(CppEditor
|
|||||||
insertionpointlocator.cpp insertionpointlocator.h
|
insertionpointlocator.cpp insertionpointlocator.h
|
||||||
projectinfo.cpp projectinfo.h
|
projectinfo.cpp projectinfo.h
|
||||||
projectpart.cpp projectpart.h
|
projectpart.cpp projectpart.h
|
||||||
|
quickfixes/assigntolocalvariable.cpp quickfixes/assigntolocalvariable.h
|
||||||
quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h
|
quickfixes/bringidentifierintoscope.cpp quickfixes/bringidentifierintoscope.h
|
||||||
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
|
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
|
||||||
quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
|
quickfixes/convertstringliteral.cpp quickfixes/convertstringliteral.h
|
||||||
|
@@ -219,6 +219,8 @@ QtcPlugin {
|
|||||||
name: "Quickfixes"
|
name: "Quickfixes"
|
||||||
prefix: "quickfixes/"
|
prefix: "quickfixes/"
|
||||||
files: [
|
files: [
|
||||||
|
"assigntolocalvariable.cpp",
|
||||||
|
"assigntolocalvariable.h",
|
||||||
"bringidentifierintoscope.cpp",
|
"bringidentifierintoscope.cpp",
|
||||||
"bringidentifierintoscope.h",
|
"bringidentifierintoscope.h",
|
||||||
"convertfromandtopointer.cpp",
|
"convertfromandtopointer.cpp",
|
||||||
|
513
src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp
Normal file
513
src/plugins/cppeditor/quickfixes/assigntolocalvariable.cpp
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "assigntolocalvariable.h"
|
||||||
|
|
||||||
|
#include "../cppcodestylesettings.h"
|
||||||
|
#include "../cppeditortr.h"
|
||||||
|
#include "../cppeditorwidget.h"
|
||||||
|
#include "../cpprefactoringchanges.h"
|
||||||
|
#include "cppquickfix.h"
|
||||||
|
#include "cppquickfixprojectsettings.h"
|
||||||
|
|
||||||
|
#include <cplusplus/CppRewriter.h>
|
||||||
|
#include <cplusplus/Overview.h>
|
||||||
|
#include <cplusplus/TypeOfExpression.h>
|
||||||
|
#include <projectexplorer/projecttree.h>
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include "cppquickfix_test.h"
|
||||||
|
#include <QtTest>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace CPlusPlus;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace CppEditor::Internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class AssignToLocalVariableOperation : public CppQuickFixOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AssignToLocalVariableOperation(const CppQuickFixInterface &interface,
|
||||||
|
const int insertPos, const AST *ast, const Name *name)
|
||||||
|
: CppQuickFixOperation(interface)
|
||||||
|
, m_insertPos(insertPos)
|
||||||
|
, m_ast(ast)
|
||||||
|
, m_name(name)
|
||||||
|
, m_oo(CppCodeStyleSettings::currentProjectCodeStyleOverview())
|
||||||
|
, m_originalName(m_oo.prettyName(m_name))
|
||||||
|
, m_file(CppRefactoringChanges(snapshot()).cppFile(filePath()))
|
||||||
|
{
|
||||||
|
setDescription(Tr::tr("Assign to Local Variable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void perform() override
|
||||||
|
{
|
||||||
|
QString type = deduceType();
|
||||||
|
if (type.isEmpty())
|
||||||
|
return;
|
||||||
|
const int origNameLength = m_originalName.length();
|
||||||
|
const QString varName = constructVarName();
|
||||||
|
const QString insertString = type.replace(type.length() - origNameLength, origNameLength,
|
||||||
|
varName + QLatin1String(" = "));
|
||||||
|
ChangeSet changes;
|
||||||
|
changes.insert(m_insertPos, insertString);
|
||||||
|
m_file->setChangeSet(changes);
|
||||||
|
m_file->apply();
|
||||||
|
|
||||||
|
// move cursor to new variable name
|
||||||
|
QTextCursor c = m_file->cursor();
|
||||||
|
c.setPosition(m_insertPos + insertString.length() - varName.length() - 3);
|
||||||
|
c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
editor()->setTextCursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString deduceType() const
|
||||||
|
{
|
||||||
|
const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
|
||||||
|
ProjectExplorer::ProjectTree::currentProject());
|
||||||
|
if (m_file->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
|
||||||
|
return "auto " + m_originalName;
|
||||||
|
|
||||||
|
TypeOfExpression typeOfExpression;
|
||||||
|
typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
|
||||||
|
typeOfExpression.setExpandTemplates(true);
|
||||||
|
Scope * const scope = m_file->scopeAt(m_ast->firstToken());
|
||||||
|
const QList<LookupItem> result = typeOfExpression(m_file->textOf(m_ast).toUtf8(),
|
||||||
|
scope, TypeOfExpression::Preprocess);
|
||||||
|
if (result.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
SubstitutionEnvironment env;
|
||||||
|
env.setContext(context());
|
||||||
|
env.switchScope(result.first().scope());
|
||||||
|
ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
|
||||||
|
if (!con)
|
||||||
|
con = typeOfExpression.context().globalNamespace();
|
||||||
|
UseMinimalNames q(con);
|
||||||
|
env.enter(&q);
|
||||||
|
|
||||||
|
Control *control = context().bindings()->control().get();
|
||||||
|
FullySpecifiedType type = rewriteType(result.first().type(), &env, control);
|
||||||
|
|
||||||
|
return m_oo.prettyType(type, m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString constructVarName() const
|
||||||
|
{
|
||||||
|
QString newName = m_originalName;
|
||||||
|
if (newName.startsWith(QLatin1String("get"), Qt::CaseInsensitive)
|
||||||
|
&& newName.length() > 3
|
||||||
|
&& newName.at(3).isUpper()) {
|
||||||
|
newName.remove(0, 3);
|
||||||
|
newName.replace(0, 1, newName.at(0).toLower());
|
||||||
|
} else if (newName.startsWith(QLatin1String("to"), Qt::CaseInsensitive)
|
||||||
|
&& newName.length() > 2
|
||||||
|
&& newName.at(2).isUpper()) {
|
||||||
|
newName.remove(0, 2);
|
||||||
|
newName.replace(0, 1, newName.at(0).toLower());
|
||||||
|
} else {
|
||||||
|
newName.replace(0, 1, newName.at(0).toUpper());
|
||||||
|
newName.prepend(QLatin1String("local"));
|
||||||
|
}
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int m_insertPos;
|
||||||
|
const AST * const m_ast;
|
||||||
|
const Name * const m_name;
|
||||||
|
const Overview m_oo;
|
||||||
|
const QString m_originalName;
|
||||||
|
const CppRefactoringFilePtr m_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Assigns the return value of a function call or a new expression to a local variable
|
||||||
|
class AssignToLocalVariable : public CppQuickFixFactory
|
||||||
|
{
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
public:
|
||||||
|
static QObject *createTest();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
|
||||||
|
{
|
||||||
|
const QList<AST *> &path = interface.path();
|
||||||
|
AST *outerAST = nullptr;
|
||||||
|
SimpleNameAST *nameAST = nullptr;
|
||||||
|
|
||||||
|
for (int i = path.size() - 3; i >= 0; --i) {
|
||||||
|
if (CallAST *callAST = path.at(i)->asCall()) {
|
||||||
|
if (!interface.isCursorOn(callAST))
|
||||||
|
return;
|
||||||
|
if (i - 2 >= 0) {
|
||||||
|
const int idx = i - 2;
|
||||||
|
if (path.at(idx)->asSimpleDeclaration())
|
||||||
|
return;
|
||||||
|
if (path.at(idx)->asExpressionStatement())
|
||||||
|
return;
|
||||||
|
if (path.at(idx)->asMemInitializer())
|
||||||
|
return;
|
||||||
|
if (path.at(idx)->asCall()) { // Fallback if we have a->b()->c()...
|
||||||
|
--i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int a = i - 1; a > 0; --a) {
|
||||||
|
if (path.at(a)->asBinaryExpression())
|
||||||
|
return;
|
||||||
|
if (path.at(a)->asReturnStatement())
|
||||||
|
return;
|
||||||
|
if (path.at(a)->asCall())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MemberAccessAST *member = path.at(i + 1)->asMemberAccess()) { // member
|
||||||
|
if (NameAST *name = member->member_name)
|
||||||
|
nameAST = name->asSimpleName();
|
||||||
|
} else if (QualifiedNameAST *qname = path.at(i + 2)->asQualifiedName()) { // static or
|
||||||
|
nameAST = qname->unqualified_name->asSimpleName(); // func in ns
|
||||||
|
} else { // normal
|
||||||
|
nameAST = path.at(i + 2)->asSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameAST) {
|
||||||
|
outerAST = callAST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (NewExpressionAST *newexp = path.at(i)->asNewExpression()) {
|
||||||
|
if (!interface.isCursorOn(newexp))
|
||||||
|
return;
|
||||||
|
if (i - 2 >= 0) {
|
||||||
|
const int idx = i - 2;
|
||||||
|
if (path.at(idx)->asSimpleDeclaration())
|
||||||
|
return;
|
||||||
|
if (path.at(idx)->asExpressionStatement())
|
||||||
|
return;
|
||||||
|
if (path.at(idx)->asMemInitializer())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int a = i - 1; a > 0; --a) {
|
||||||
|
if (path.at(a)->asReturnStatement())
|
||||||
|
return;
|
||||||
|
if (path.at(a)->asCall())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NamedTypeSpecifierAST *ts = path.at(i + 2)->asNamedTypeSpecifier()) {
|
||||||
|
nameAST = ts->name->asSimpleName();
|
||||||
|
outerAST = newexp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerAST && nameAST) {
|
||||||
|
const CppRefactoringFilePtr file = interface.currentFile();
|
||||||
|
QList<LookupItem> items;
|
||||||
|
TypeOfExpression typeOfExpression;
|
||||||
|
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
|
||||||
|
interface.context().bindings());
|
||||||
|
typeOfExpression.setExpandTemplates(true);
|
||||||
|
|
||||||
|
// If items are empty, AssignToLocalVariableOperation will fail.
|
||||||
|
items = typeOfExpression(file->textOf(outerAST).toUtf8(),
|
||||||
|
file->scopeAt(outerAST->firstToken()),
|
||||||
|
TypeOfExpression::Preprocess);
|
||||||
|
if (items.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CallAST *callAST = outerAST->asCall()) {
|
||||||
|
items = typeOfExpression(file->textOf(callAST->base_expression).toUtf8(),
|
||||||
|
file->scopeAt(callAST->base_expression->firstToken()),
|
||||||
|
TypeOfExpression::Preprocess);
|
||||||
|
} else {
|
||||||
|
items = typeOfExpression(file->textOf(nameAST).toUtf8(),
|
||||||
|
file->scopeAt(nameAST->firstToken()),
|
||||||
|
TypeOfExpression::Preprocess);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const LookupItem &item : std::as_const(items)) {
|
||||||
|
if (!item.declaration())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Function *func = item.declaration()->asFunction()) {
|
||||||
|
if (func->isSignal() || func->returnType()->asVoidType())
|
||||||
|
return;
|
||||||
|
} else if (Declaration *dec = item.declaration()->asDeclaration()) {
|
||||||
|
if (Function *func = dec->type()->asFunctionType()) {
|
||||||
|
if (func->isSignal() || func->returnType()->asVoidType())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Name *name = nameAST->name;
|
||||||
|
const int insertPos = interface.currentFile()->startOf(outerAST);
|
||||||
|
result << new AssignToLocalVariableOperation(interface, insertPos, outerAST, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
using namespace Tests;
|
||||||
|
|
||||||
|
class AssignToLocalVariableTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void testTemplates()
|
||||||
|
{
|
||||||
|
QList<TestDocumentPtr> testDocuments;
|
||||||
|
QByteArray original;
|
||||||
|
QByteArray expected;
|
||||||
|
|
||||||
|
// Header File
|
||||||
|
original =
|
||||||
|
"template <typename T>\n"
|
||||||
|
"class List {\n"
|
||||||
|
"public:\n"
|
||||||
|
" T first();"
|
||||||
|
"};\n"
|
||||||
|
;
|
||||||
|
expected = original;
|
||||||
|
testDocuments << CppTestDocument::create("file.h", original, expected);
|
||||||
|
|
||||||
|
// Source File
|
||||||
|
original =
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" List<int> list;\n"
|
||||||
|
" li@st.first();\n"
|
||||||
|
"}\n";
|
||||||
|
expected =
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" List<int> list;\n"
|
||||||
|
" auto localFirst = list.first();\n"
|
||||||
|
"}\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
||||||
|
|
||||||
|
AssignToLocalVariable factory;
|
||||||
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("original");
|
||||||
|
QTest::addColumn<QByteArray>("expected");
|
||||||
|
|
||||||
|
// Check: Add local variable for a free function.
|
||||||
|
QTest::newRow("freeFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"int foo() {return 1;}\n"
|
||||||
|
"void bar() {fo@o();}\n")
|
||||||
|
<< QByteArray(
|
||||||
|
"int foo() {return 1;}\n"
|
||||||
|
"void bar() {auto localFoo = foo();}\n");
|
||||||
|
|
||||||
|
// Check: Add local variable for a member function.
|
||||||
|
QTest::newRow("memberFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: int* fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Foo *f = new Foo;\n"
|
||||||
|
" @f->fooFunc();\n"
|
||||||
|
"}\n")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: int* fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Foo *f = new Foo;\n"
|
||||||
|
" auto localFooFunc = f->fooFunc();\n"
|
||||||
|
"}\n");
|
||||||
|
|
||||||
|
// Check: Add local variable for a member function, cursor in the middle (QTCREATORBUG-10355)
|
||||||
|
QTest::newRow("memberFunction2ndGrade1")
|
||||||
|
<< QByteArray(
|
||||||
|
"struct Foo {int* func();};\n"
|
||||||
|
"struct Baz {Foo* foo();};\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Baz *b = new Baz;\n"
|
||||||
|
" b->foo@()->func();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray(
|
||||||
|
"struct Foo {int* func();};\n"
|
||||||
|
"struct Baz {Foo* foo();};\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Baz *b = new Baz;\n"
|
||||||
|
" auto localFunc = b->foo()->func();\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
// Check: Add local variable for a member function, cursor on function call (QTCREATORBUG-10355)
|
||||||
|
QTest::newRow("memberFunction2ndGrade2")
|
||||||
|
<< QByteArray(
|
||||||
|
"struct Foo {int* func();};\n"
|
||||||
|
"struct Baz {Foo* foo();};\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Baz *b = new Baz;\n"
|
||||||
|
" b->foo()->f@unc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray(
|
||||||
|
"struct Foo {int* func();};\n"
|
||||||
|
"struct Baz {Foo* foo();};\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Baz *b = new Baz;\n"
|
||||||
|
" auto localFunc = b->foo()->func();\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
// Check: Add local variable for a static member function.
|
||||||
|
QTest::newRow("staticMemberFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: static int* fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Foo::fooF@unc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: static int* fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" auto localFooFunc = Foo::fooFunc();\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
// Check: Add local variable for a new Expression.
|
||||||
|
QTest::newRow("newExpression")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" new Fo@o;\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" auto localFoo = new Foo;\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
// Check: No trigger for function inside member initialization list.
|
||||||
|
QTest::newRow("noInitializationList")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo\n"
|
||||||
|
"{\n"
|
||||||
|
" public: Foo : m_i(fooF@unc()) {}\n"
|
||||||
|
" int fooFunc() {return 2;}\n"
|
||||||
|
" int m_i;\n"
|
||||||
|
"};\n")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for void functions.
|
||||||
|
QTest::newRow("noVoidFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"void foo() {}\n"
|
||||||
|
"void bar() {fo@o();}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for void member functions.
|
||||||
|
QTest::newRow("noVoidMemberFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: void fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Foo *f = new Foo;\n"
|
||||||
|
" @f->fooFunc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for void static member functions.
|
||||||
|
QTest::newRow("noVoidStaticMemberFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: static void fooFunc();}\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" Foo::fo@oFunc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in expressions.
|
||||||
|
QTest::newRow("noFunctionInExpression")
|
||||||
|
<< QByteArray(
|
||||||
|
"int foo(int a) {return a;}\n"
|
||||||
|
"int bar() {return 1;}"
|
||||||
|
"void baz() {foo(@bar() + bar());}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in functions. (QTCREATORBUG-9510)
|
||||||
|
QTest::newRow("noFunctionInFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"int foo(int a, int b) {return a + b;}\n"
|
||||||
|
"int bar(int a) {return a;}\n"
|
||||||
|
"void baz() {\n"
|
||||||
|
" int a = foo(ba@r(), bar());\n"
|
||||||
|
"}\n")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in return statements (classes).
|
||||||
|
QTest::newRow("noReturnClass1")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: static void fooFunc();}\n"
|
||||||
|
"Foo* bar() {\n"
|
||||||
|
" return new Fo@o;\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in return statements (classes). (QTCREATORBUG-9525)
|
||||||
|
QTest::newRow("noReturnClass2")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: int fooFunc();}\n"
|
||||||
|
"int bar() {\n"
|
||||||
|
" return (new Fo@o)->fooFunc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in return statements (functions).
|
||||||
|
QTest::newRow("noReturnFunc1")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Foo {public: int fooFunc();}\n"
|
||||||
|
"int bar() {\n"
|
||||||
|
" return Foo::fooFu@nc();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions in return statements (functions). (QTCREATORBUG-9525)
|
||||||
|
QTest::newRow("noReturnFunc2")
|
||||||
|
<< QByteArray(
|
||||||
|
"int bar() {\n"
|
||||||
|
" return list.firs@t().foo;\n"
|
||||||
|
"}\n")
|
||||||
|
<< QByteArray();
|
||||||
|
|
||||||
|
// Check: No trigger for functions which does not match in signature.
|
||||||
|
QTest::newRow("noSignatureMatch")
|
||||||
|
<< QByteArray(
|
||||||
|
"int someFunc(int);\n"
|
||||||
|
"\n"
|
||||||
|
"void f()\n"
|
||||||
|
"{\n"
|
||||||
|
" some@Func();\n"
|
||||||
|
"}")
|
||||||
|
<< QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, original);
|
||||||
|
QFETCH(QByteArray, expected);
|
||||||
|
AssignToLocalVariable factory;
|
||||||
|
QuickFixOperationTest(singleDocument(original, expected), &factory);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QObject *AssignToLocalVariable::createTest() { return new AssignToLocalVariableTest; }
|
||||||
|
|
||||||
|
#endif // WITH_TESTS
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void registerAssignToLocalVariableQuickfix()
|
||||||
|
{
|
||||||
|
CppQuickFixFactory::registerFactory<AssignToLocalVariable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CppEditor::Internal
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include <assigntolocalvariable.moc>
|
||||||
|
#endif
|
8
src/plugins/cppeditor/quickfixes/assigntolocalvariable.h
Normal file
8
src/plugins/cppeditor/quickfixes/assigntolocalvariable.h
Normal 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 registerAssignToLocalVariableQuickfix();
|
||||||
|
} // namespace CppEditor::Internal
|
@@ -848,197 +848,6 @@ void QuickfixTest::testGeneric_data()
|
|||||||
<< _("char@*s;")
|
<< _("char@*s;")
|
||||||
<< _("char *s;");
|
<< _("char *s;");
|
||||||
|
|
||||||
// Check: Add local variable for a free function.
|
|
||||||
QTest::newRow("AssignToLocalVariable_freeFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"int foo() {return 1;}\n"
|
|
||||||
"void bar() {fo@o();}\n"
|
|
||||||
) << _(
|
|
||||||
"int foo() {return 1;}\n"
|
|
||||||
"void bar() {auto localFoo = foo();}\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: Add local variable for a member function.
|
|
||||||
QTest::newRow("AssignToLocalVariable_memberFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: int* fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Foo *f = new Foo;\n"
|
|
||||||
" @f->fooFunc();\n"
|
|
||||||
"}\n"
|
|
||||||
) << _(
|
|
||||||
"class Foo {public: int* fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Foo *f = new Foo;\n"
|
|
||||||
" auto localFooFunc = f->fooFunc();\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: Add local variable for a member function, cursor in the middle (QTCREATORBUG-10355)
|
|
||||||
QTest::newRow("AssignToLocalVariable_memberFunction2ndGrade1")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"struct Foo {int* func();};\n"
|
|
||||||
"struct Baz {Foo* foo();};\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Baz *b = new Baz;\n"
|
|
||||||
" b->foo@()->func();\n"
|
|
||||||
"}"
|
|
||||||
) << _(
|
|
||||||
"struct Foo {int* func();};\n"
|
|
||||||
"struct Baz {Foo* foo();};\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Baz *b = new Baz;\n"
|
|
||||||
" auto localFunc = b->foo()->func();\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: Add local variable for a member function, cursor on function call (QTCREATORBUG-10355)
|
|
||||||
QTest::newRow("AssignToLocalVariable_memberFunction2ndGrade2")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"struct Foo {int* func();};\n"
|
|
||||||
"struct Baz {Foo* foo();};\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Baz *b = new Baz;\n"
|
|
||||||
" b->foo()->f@unc();\n"
|
|
||||||
"}"
|
|
||||||
) << _(
|
|
||||||
"struct Foo {int* func();};\n"
|
|
||||||
"struct Baz {Foo* foo();};\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Baz *b = new Baz;\n"
|
|
||||||
" auto localFunc = b->foo()->func();\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: Add local variable for a static member function.
|
|
||||||
QTest::newRow("AssignToLocalVariable_staticMemberFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: static int* fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Foo::fooF@unc();\n"
|
|
||||||
"}"
|
|
||||||
) << _(
|
|
||||||
"class Foo {public: static int* fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" auto localFooFunc = Foo::fooFunc();\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: Add local variable for a new Expression.
|
|
||||||
QTest::newRow("AssignToLocalVariable_newExpression")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" new Fo@o;\n"
|
|
||||||
"}"
|
|
||||||
) << _(
|
|
||||||
"class Foo {}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" auto localFoo = new Foo;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check: No trigger for function inside member initialization list.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noInitializationList")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
" public: Foo : m_i(fooF@unc()) {}\n"
|
|
||||||
" int fooFunc() {return 2;}\n"
|
|
||||||
" int m_i;\n"
|
|
||||||
"};\n"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for void functions.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noVoidFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"void foo() {}\n"
|
|
||||||
"void bar() {fo@o();}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for void member functions.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noVoidMemberFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: void fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Foo *f = new Foo;\n"
|
|
||||||
" @f->fooFunc();\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for void static member functions.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noVoidStaticMemberFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: static void fooFunc();}\n"
|
|
||||||
"void bar() {\n"
|
|
||||||
" Foo::fo@oFunc();\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in expressions.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noFunctionInExpression")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"int foo(int a) {return a;}\n"
|
|
||||||
"int bar() {return 1;}"
|
|
||||||
"void baz() {foo(@bar() + bar());}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in functions. (QTCREATORBUG-9510)
|
|
||||||
QTest::newRow("AssignToLocalVariable_noFunctionInFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"int foo(int a, int b) {return a + b;}\n"
|
|
||||||
"int bar(int a) {return a;}\n"
|
|
||||||
"void baz() {\n"
|
|
||||||
" int a = foo(ba@r(), bar());\n"
|
|
||||||
"}\n"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in return statements (classes).
|
|
||||||
QTest::newRow("AssignToLocalVariable_noReturnClass1")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: static void fooFunc();}\n"
|
|
||||||
"Foo* bar() {\n"
|
|
||||||
" return new Fo@o;\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in return statements (classes). (QTCREATORBUG-9525)
|
|
||||||
QTest::newRow("AssignToLocalVariable_noReturnClass2")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: int fooFunc();}\n"
|
|
||||||
"int bar() {\n"
|
|
||||||
" return (new Fo@o)->fooFunc();\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in return statements (functions).
|
|
||||||
QTest::newRow("AssignToLocalVariable_noReturnFunc1")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"class Foo {public: int fooFunc();}\n"
|
|
||||||
"int bar() {\n"
|
|
||||||
" return Foo::fooFu@nc();\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions in return statements (functions). (QTCREATORBUG-9525)
|
|
||||||
QTest::newRow("AssignToLocalVariable_noReturnFunc2")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"int bar() {\n"
|
|
||||||
" return list.firs@t().foo;\n"
|
|
||||||
"}\n"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
// Check: No trigger for functions which does not match in signature.
|
|
||||||
QTest::newRow("AssignToLocalVariable_noSignatureMatch")
|
|
||||||
<< CppQuickFixFactoryPtr(new AssignToLocalVariable) << _(
|
|
||||||
"int someFunc(int);\n"
|
|
||||||
"\n"
|
|
||||||
"void f()\n"
|
|
||||||
"{\n"
|
|
||||||
" some@Func();\n"
|
|
||||||
"}"
|
|
||||||
) << _();
|
|
||||||
|
|
||||||
QTest::newRow("convert to camel case: normal")
|
QTest::newRow("convert to camel case: normal")
|
||||||
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
||||||
<< _("void @lower_case_function();\n")
|
<< _("void @lower_case_function();\n")
|
||||||
@@ -1119,43 +928,6 @@ CppCodeStyleSettings CppCodeStyleSettingsChanger::currentSettings()
|
|||||||
return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>();
|
return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickfixTest::testAssignToLocalVariableTemplates()
|
|
||||||
{
|
|
||||||
|
|
||||||
QList<TestDocumentPtr> testDocuments;
|
|
||||||
QByteArray original;
|
|
||||||
QByteArray expected;
|
|
||||||
|
|
||||||
// Header File
|
|
||||||
original =
|
|
||||||
"template <typename T>\n"
|
|
||||||
"class List {\n"
|
|
||||||
"public:\n"
|
|
||||||
" T first();"
|
|
||||||
"};\n"
|
|
||||||
;
|
|
||||||
expected = original;
|
|
||||||
testDocuments << CppTestDocument::create("file.h", original, expected);
|
|
||||||
|
|
||||||
// Source File
|
|
||||||
original =
|
|
||||||
"#include \"file.h\"\n"
|
|
||||||
"void foo() {\n"
|
|
||||||
" List<int> list;\n"
|
|
||||||
" li@st.first();\n"
|
|
||||||
"}\n";
|
|
||||||
expected =
|
|
||||||
"#include \"file.h\"\n"
|
|
||||||
"void foo() {\n"
|
|
||||||
" List<int> list;\n"
|
|
||||||
" auto localFirst = list.first();\n"
|
|
||||||
"}\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
|
||||||
|
|
||||||
AssignToLocalVariable factory;
|
|
||||||
QuickFixOperationTest(testDocuments, &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testConvertToMetaMethodInvocation_data()
|
void QuickfixTest::testConvertToMetaMethodInvocation_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QByteArray>("input");
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
@@ -97,8 +97,6 @@ private slots:
|
|||||||
void testGeneric_data();
|
void testGeneric_data();
|
||||||
void testGeneric();
|
void testGeneric();
|
||||||
|
|
||||||
void testAssignToLocalVariableTemplates();
|
|
||||||
|
|
||||||
void testConvertToMetaMethodInvocation_data();
|
void testConvertToMetaMethodInvocation_data();
|
||||||
void testConvertToMetaMethodInvocation();
|
void testConvertToMetaMethodInvocation();
|
||||||
};
|
};
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
#include "../cpptoolsreuse.h"
|
#include "../cpptoolsreuse.h"
|
||||||
#include "../insertionpointlocator.h"
|
#include "../insertionpointlocator.h"
|
||||||
#include "../symbolfinder.h"
|
#include "../symbolfinder.h"
|
||||||
|
#include "assigntolocalvariable.h"
|
||||||
#include "bringidentifierintoscope.h"
|
#include "bringidentifierintoscope.h"
|
||||||
#include "convertfromandtopointer.h"
|
#include "convertfromandtopointer.h"
|
||||||
#include "cppcodegenerationquickfixes.h"
|
#include "cppcodegenerationquickfixes.h"
|
||||||
@@ -906,226 +907,6 @@ void ApplyDeclDefLinkChanges::doMatch(const CppQuickFixInterface &interface,
|
|||||||
result << op;
|
result << op;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class AssignToLocalVariableOperation : public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit AssignToLocalVariableOperation(const CppQuickFixInterface &interface,
|
|
||||||
const int insertPos, const AST *ast, const Name *name)
|
|
||||||
: CppQuickFixOperation(interface)
|
|
||||||
, m_insertPos(insertPos)
|
|
||||||
, m_ast(ast)
|
|
||||||
, m_name(name)
|
|
||||||
, m_oo(CppCodeStyleSettings::currentProjectCodeStyleOverview())
|
|
||||||
, m_originalName(m_oo.prettyName(m_name))
|
|
||||||
, m_file(CppRefactoringChanges(snapshot()).cppFile(filePath()))
|
|
||||||
{
|
|
||||||
setDescription(Tr::tr("Assign to Local Variable"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
QString type = deduceType();
|
|
||||||
if (type.isEmpty())
|
|
||||||
return;
|
|
||||||
const int origNameLength = m_originalName.length();
|
|
||||||
const QString varName = constructVarName();
|
|
||||||
const QString insertString = type.replace(type.length() - origNameLength, origNameLength,
|
|
||||||
varName + QLatin1String(" = "));
|
|
||||||
ChangeSet changes;
|
|
||||||
changes.insert(m_insertPos, insertString);
|
|
||||||
m_file->setChangeSet(changes);
|
|
||||||
m_file->apply();
|
|
||||||
|
|
||||||
// move cursor to new variable name
|
|
||||||
QTextCursor c = m_file->cursor();
|
|
||||||
c.setPosition(m_insertPos + insertString.length() - varName.length() - 3);
|
|
||||||
c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
||||||
editor()->setTextCursor(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString deduceType() const
|
|
||||||
{
|
|
||||||
const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
|
|
||||||
ProjectExplorer::ProjectTree::currentProject());
|
|
||||||
if (m_file->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
|
|
||||||
return "auto " + m_originalName;
|
|
||||||
|
|
||||||
TypeOfExpression typeOfExpression;
|
|
||||||
typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
|
|
||||||
typeOfExpression.setExpandTemplates(true);
|
|
||||||
Scope * const scope = m_file->scopeAt(m_ast->firstToken());
|
|
||||||
const QList<LookupItem> result = typeOfExpression(m_file->textOf(m_ast).toUtf8(),
|
|
||||||
scope, TypeOfExpression::Preprocess);
|
|
||||||
if (result.isEmpty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
SubstitutionEnvironment env;
|
|
||||||
env.setContext(context());
|
|
||||||
env.switchScope(result.first().scope());
|
|
||||||
ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
|
|
||||||
if (!con)
|
|
||||||
con = typeOfExpression.context().globalNamespace();
|
|
||||||
UseMinimalNames q(con);
|
|
||||||
env.enter(&q);
|
|
||||||
|
|
||||||
Control *control = context().bindings()->control().get();
|
|
||||||
FullySpecifiedType type = rewriteType(result.first().type(), &env, control);
|
|
||||||
|
|
||||||
return m_oo.prettyType(type, m_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString constructVarName() const
|
|
||||||
{
|
|
||||||
QString newName = m_originalName;
|
|
||||||
if (newName.startsWith(QLatin1String("get"), Qt::CaseInsensitive)
|
|
||||||
&& newName.length() > 3
|
|
||||||
&& newName.at(3).isUpper()) {
|
|
||||||
newName.remove(0, 3);
|
|
||||||
newName.replace(0, 1, newName.at(0).toLower());
|
|
||||||
} else if (newName.startsWith(QLatin1String("to"), Qt::CaseInsensitive)
|
|
||||||
&& newName.length() > 2
|
|
||||||
&& newName.at(2).isUpper()) {
|
|
||||||
newName.remove(0, 2);
|
|
||||||
newName.replace(0, 1, newName.at(0).toLower());
|
|
||||||
} else {
|
|
||||||
newName.replace(0, 1, newName.at(0).toUpper());
|
|
||||||
newName.prepend(QLatin1String("local"));
|
|
||||||
}
|
|
||||||
return newName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int m_insertPos;
|
|
||||||
const AST * const m_ast;
|
|
||||||
const Name * const m_name;
|
|
||||||
const Overview m_oo;
|
|
||||||
const QString m_originalName;
|
|
||||||
const CppRefactoringFilePtr m_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
void AssignToLocalVariable::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
AST *outerAST = nullptr;
|
|
||||||
SimpleNameAST *nameAST = nullptr;
|
|
||||||
|
|
||||||
for (int i = path.size() - 3; i >= 0; --i) {
|
|
||||||
if (CallAST *callAST = path.at(i)->asCall()) {
|
|
||||||
if (!interface.isCursorOn(callAST))
|
|
||||||
return;
|
|
||||||
if (i - 2 >= 0) {
|
|
||||||
const int idx = i - 2;
|
|
||||||
if (path.at(idx)->asSimpleDeclaration())
|
|
||||||
return;
|
|
||||||
if (path.at(idx)->asExpressionStatement())
|
|
||||||
return;
|
|
||||||
if (path.at(idx)->asMemInitializer())
|
|
||||||
return;
|
|
||||||
if (path.at(idx)->asCall()) { // Fallback if we have a->b()->c()...
|
|
||||||
--i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int a = i - 1; a > 0; --a) {
|
|
||||||
if (path.at(a)->asBinaryExpression())
|
|
||||||
return;
|
|
||||||
if (path.at(a)->asReturnStatement())
|
|
||||||
return;
|
|
||||||
if (path.at(a)->asCall())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MemberAccessAST *member = path.at(i + 1)->asMemberAccess()) { // member
|
|
||||||
if (NameAST *name = member->member_name)
|
|
||||||
nameAST = name->asSimpleName();
|
|
||||||
} else if (QualifiedNameAST *qname = path.at(i + 2)->asQualifiedName()) { // static or
|
|
||||||
nameAST = qname->unqualified_name->asSimpleName(); // func in ns
|
|
||||||
} else { // normal
|
|
||||||
nameAST = path.at(i + 2)->asSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nameAST) {
|
|
||||||
outerAST = callAST;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (NewExpressionAST *newexp = path.at(i)->asNewExpression()) {
|
|
||||||
if (!interface.isCursorOn(newexp))
|
|
||||||
return;
|
|
||||||
if (i - 2 >= 0) {
|
|
||||||
const int idx = i - 2;
|
|
||||||
if (path.at(idx)->asSimpleDeclaration())
|
|
||||||
return;
|
|
||||||
if (path.at(idx)->asExpressionStatement())
|
|
||||||
return;
|
|
||||||
if (path.at(idx)->asMemInitializer())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int a = i - 1; a > 0; --a) {
|
|
||||||
if (path.at(a)->asReturnStatement())
|
|
||||||
return;
|
|
||||||
if (path.at(a)->asCall())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NamedTypeSpecifierAST *ts = path.at(i + 2)->asNamedTypeSpecifier()) {
|
|
||||||
nameAST = ts->name->asSimpleName();
|
|
||||||
outerAST = newexp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerAST && nameAST) {
|
|
||||||
const CppRefactoringFilePtr file = interface.currentFile();
|
|
||||||
QList<LookupItem> items;
|
|
||||||
TypeOfExpression typeOfExpression;
|
|
||||||
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
|
|
||||||
interface.context().bindings());
|
|
||||||
typeOfExpression.setExpandTemplates(true);
|
|
||||||
|
|
||||||
// If items are empty, AssignToLocalVariableOperation will fail.
|
|
||||||
items = typeOfExpression(file->textOf(outerAST).toUtf8(),
|
|
||||||
file->scopeAt(outerAST->firstToken()),
|
|
||||||
TypeOfExpression::Preprocess);
|
|
||||||
if (items.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (CallAST *callAST = outerAST->asCall()) {
|
|
||||||
items = typeOfExpression(file->textOf(callAST->base_expression).toUtf8(),
|
|
||||||
file->scopeAt(callAST->base_expression->firstToken()),
|
|
||||||
TypeOfExpression::Preprocess);
|
|
||||||
} else {
|
|
||||||
items = typeOfExpression(file->textOf(nameAST).toUtf8(),
|
|
||||||
file->scopeAt(nameAST->firstToken()),
|
|
||||||
TypeOfExpression::Preprocess);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const LookupItem &item : std::as_const(items)) {
|
|
||||||
if (!item.declaration())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (Function *func = item.declaration()->asFunction()) {
|
|
||||||
if (func->isSignal() || func->returnType()->asVoidType())
|
|
||||||
return;
|
|
||||||
} else if (Declaration *dec = item.declaration()->asDeclaration()) {
|
|
||||||
if (Function *func = dec->type()->asFunctionType()) {
|
|
||||||
if (func->isSignal() || func->returnType()->asVoidType())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Name *name = nameAST->name;
|
|
||||||
const int insertPos = interface.currentFile()->startOf(outerAST);
|
|
||||||
result << new AssignToLocalVariableOperation(interface, insertPos, outerAST, name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface,
|
void ExtraRefactoringOperations::doMatch(const CppQuickFixInterface &interface,
|
||||||
QuickFixOperations &result)
|
QuickFixOperations &result)
|
||||||
{
|
{
|
||||||
@@ -1283,8 +1064,6 @@ void createCppQuickFixes()
|
|||||||
|
|
||||||
new ApplyDeclDefLinkChanges;
|
new ApplyDeclDefLinkChanges;
|
||||||
|
|
||||||
new AssignToLocalVariable;
|
|
||||||
|
|
||||||
registerInsertVirtualMethodsQuickfix();
|
registerInsertVirtualMethodsQuickfix();
|
||||||
registerMoveClassToOwnFileQuickfix();
|
registerMoveClassToOwnFileQuickfix();
|
||||||
registerRemoveUsingNamespaceQuickfix();
|
registerRemoveUsingNamespaceQuickfix();
|
||||||
@@ -1301,6 +1080,7 @@ void createCppQuickFixes()
|
|||||||
registerExtractFunctionQuickfix();
|
registerExtractFunctionQuickfix();
|
||||||
registerExtractLiteralAsParameterQuickfix();
|
registerExtractLiteralAsParameterQuickfix();
|
||||||
registerConvertFromAndToPointerQuickfix();
|
registerConvertFromAndToPointerQuickfix();
|
||||||
|
registerAssignToLocalVariableQuickfix();
|
||||||
|
|
||||||
new ExtraRefactoringOperations;
|
new ExtraRefactoringOperations;
|
||||||
|
|
||||||
|
@@ -134,15 +134,6 @@ public:
|
|||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
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
|
|
||||||
*/
|
|
||||||
class AssignToLocalVariable : public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Converts a normal function call into a meta method invocation, if the functions is
|
//! Converts a normal function call into a meta method invocation, if the functions is
|
||||||
//! marked as invokable.
|
//! marked as invokable.
|
||||||
class ConvertToMetaMethodCall : public CppQuickFixFactory
|
class ConvertToMetaMethodCall : public CppQuickFixFactory
|
||||||
|
Reference in New Issue
Block a user