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:
Christian Kandeler
2024-05-16 16:36:17 +02:00
parent f0695dacf9
commit 7fec30a2c4
8 changed files with 526 additions and 461 deletions

View File

@@ -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

View File

@@ -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",

View 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

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 registerAssignToLocalVariableQuickfix();
} // namespace CppEditor::Internal

View File

@@ -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");

View File

@@ -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();
}; };

View File

@@ -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;

View File

@@ -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