forked from qt-creator/qt-creator
CppEditor: Move "create decl from use" quickfixes into dedicated files
Change-Id: Id93f4a5cdf7f2a36397458f3b00bb2a0cdefd69f Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -109,6 +109,7 @@ add_qtc_plugin(CppEditor
|
|||||||
quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h
|
quickfixes/cppquickfixsettings.cpp quickfixes/cppquickfixsettings.h
|
||||||
quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
|
quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
|
||||||
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
||||||
|
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
|
||||||
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
||||||
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
||||||
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
|
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
|
||||||
|
@@ -247,6 +247,8 @@ QtcPlugin {
|
|||||||
"cppquickfixsettingspage.h",
|
"cppquickfixsettingspage.h",
|
||||||
"cppquickfixsettingswidget.cpp",
|
"cppquickfixsettingswidget.cpp",
|
||||||
"cppquickfixsettingswidget.h",
|
"cppquickfixsettingswidget.h",
|
||||||
|
"createdeclarationfromuse.cpp",
|
||||||
|
"createdeclarationfromuse.h",
|
||||||
"insertfunctiondefinition.cpp",
|
"insertfunctiondefinition.cpp",
|
||||||
"insertfunctiondefinition.h",
|
"insertfunctiondefinition.h",
|
||||||
"moveclasstoownfile.cpp",
|
"moveclasstoownfile.cpp",
|
||||||
|
@@ -1566,16 +1566,6 @@ void QuickfixTest::testGeneric_data()
|
|||||||
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
||||||
<< _("void @WhAt_TODO_hErE();\n")
|
<< _("void @WhAt_TODO_hErE();\n")
|
||||||
<< _("void WhAtTODOHErE();\n");
|
<< _("void WhAtTODOHErE();\n");
|
||||||
QTest::newRow("AddLocalDeclaration_QTCREATORBUG-26004")
|
|
||||||
<< CppQuickFixFactoryPtr(new AddDeclarationForUndeclaredIdentifier)
|
|
||||||
<< _("void func() {\n"
|
|
||||||
" QStringList list;\n"
|
|
||||||
" @it = list.cbegin();\n"
|
|
||||||
"}\n")
|
|
||||||
<< _("void func() {\n"
|
|
||||||
" QStringList list;\n"
|
|
||||||
" auto it = list.cbegin();\n"
|
|
||||||
"}\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickfixTest::testGeneric()
|
void QuickfixTest::testGeneric()
|
||||||
@@ -1624,478 +1614,6 @@ CppCodeStyleSettings CppCodeStyleSettingsChanger::currentSettings()
|
|||||||
return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>();
|
return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickfixTest::testInsertMemberFromUse_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QByteArray>("original");
|
|
||||||
QTest::addColumn<QByteArray>("expected");
|
|
||||||
|
|
||||||
QByteArray original;
|
|
||||||
QByteArray expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C(int x) : @m_x(x) {}\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_y;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C(int x) : m_x(x) {}\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_y;\n"
|
|
||||||
" int m_x;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("inline constructor") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C(int x, double d);\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_x;\n"
|
|
||||||
"};\n"
|
|
||||||
"C::C(int x, double d) : m_x(x), @m_d(d)\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C(int x, double d);\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_x;\n"
|
|
||||||
" double m_d;\n"
|
|
||||||
"};\n"
|
|
||||||
"C::C(int x, double d) : m_x(x), m_d(d)\n";
|
|
||||||
QTest::addRow("out-of-line constructor") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C(int x) : @m_x(x) {}\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_x;\n"
|
|
||||||
"};\n";
|
|
||||||
expected = "";
|
|
||||||
QTest::addRow("member already present") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"int func() { return 0; }\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C() : @m_x(func()) {}\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_y;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"int func() { return 0; }\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" C() : m_x(func()) {}\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_y;\n"
|
|
||||||
" int m_x;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("initialization via function call") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.@value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" int value;\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add member to other struct") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { S::@value = v; }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" static int value;\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { S::value = v; }\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member to other struct (explicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.@value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" static int value;\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member to other struct (implicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v);\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { this->@m_value = v; }\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v);\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_value;\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { this->@m_value = v; }\n";
|
|
||||||
QTest::addRow("add member to this (explicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { @m_value = v; }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" int m_value;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add member to this (implicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static void setValue(int v) { @m_value = v; }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static void setValue(int v) { m_value = v; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static int m_value;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member to this (inline)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static void setValue(int v);\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { @m_value = v; }\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static void setValue(int v);\n"
|
|
||||||
"private:\n"
|
|
||||||
" static int m_value;\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { @m_value = v; }\n";
|
|
||||||
QTest::addRow("add static member to this (non-inline)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.@setValue(v); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" void setValue(int);\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.setValue(v); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add member function to other struct") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { S::@setValue(v); }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" static void setValue(int);\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { S::setValue(v); }\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member function to other struct (explicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"struct S {\n\n};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.@setValue(v); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"struct S {\n\n"
|
|
||||||
" static void setValue(int);\n"
|
|
||||||
"};\n"
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { m_s.setValue(v); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static S m_s;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member function to other struct (implicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v);\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { this->@setValueInternal(v); }\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v);\n"
|
|
||||||
"private:\n"
|
|
||||||
" void setValueInternal(int);\n"
|
|
||||||
"};\n"
|
|
||||||
"void C::setValue(int v) { this->setValueInternal(v); }\n";
|
|
||||||
QTest::addRow("add member function to this (explicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { @setValueInternal(v); }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" void setValue(int v) { setValueInternal(v); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" void setValueInternal(int);\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add member function to this (implicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int value() const { return @valueInternal(); }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int value() const { return valueInternal(); }\n"
|
|
||||||
"private:\n"
|
|
||||||
" int valueInternal() const;\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add const member function to this (implicit)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static int value() { int i = @valueInternal(); return i; }\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static int value() { int i = @valueInternal(); return i; }\n"
|
|
||||||
"private:\n"
|
|
||||||
" static int valueInternal();\n"
|
|
||||||
"};\n";
|
|
||||||
QTest::addRow("add static member function to this (inline)") << original << expected;
|
|
||||||
|
|
||||||
original =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static int value();\n"
|
|
||||||
"};\n"
|
|
||||||
"int C::value() { return @valueInternal(); }\n";
|
|
||||||
expected =
|
|
||||||
"class C {\n"
|
|
||||||
"public:\n"
|
|
||||||
" static int value();\n"
|
|
||||||
"private:\n"
|
|
||||||
" static int valueInternal();\n"
|
|
||||||
"};\n"
|
|
||||||
"int C::value() { return valueInternal(); }\n";
|
|
||||||
QTest::addRow("add static member function to this (non-inline)") << original << expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testInsertMemberFromUse()
|
|
||||||
{
|
|
||||||
QFETCH(QByteArray, original);
|
|
||||||
QFETCH(QByteArray, expected);
|
|
||||||
|
|
||||||
QList<TestDocumentPtr> testDocuments({
|
|
||||||
CppTestDocument::create("file.h", original, expected)
|
|
||||||
});
|
|
||||||
|
|
||||||
AddDeclarationForUndeclaredIdentifier factory;
|
|
||||||
factory.setMembersOnly();
|
|
||||||
QuickFixOperationTest(testDocuments, &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function for one of InsertDeclDef section cases
|
|
||||||
void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex)
|
|
||||||
{
|
|
||||||
QList<TestDocumentPtr> testDocuments;
|
|
||||||
|
|
||||||
QByteArray original;
|
|
||||||
QByteArray expected;
|
|
||||||
QByteArray sectionString = section + ":\n";
|
|
||||||
if (sectionIndex == 4)
|
|
||||||
sectionString.clear();
|
|
||||||
|
|
||||||
// Header File
|
|
||||||
original =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
+ sectionString +
|
|
||||||
" Foo();\n"
|
|
||||||
"@};\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.h", original, expected);
|
|
||||||
|
|
||||||
// Source File
|
|
||||||
original =
|
|
||||||
"#include \"file.h\"\n"
|
|
||||||
"\n"
|
|
||||||
"Foo::Foo@()\n"
|
|
||||||
"{\n"
|
|
||||||
"}\n"
|
|
||||||
;
|
|
||||||
expected = original;
|
|
||||||
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
|
||||||
|
|
||||||
InsertDeclFromDef factory;
|
|
||||||
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), sectionIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check from source file: Insert in header file.
|
|
||||||
void QuickfixTest::testInsertDeclFromDef()
|
|
||||||
{
|
|
||||||
insertToSectionDeclFromDef("public", 0);
|
|
||||||
insertToSectionDeclFromDef("public slots", 1);
|
|
||||||
insertToSectionDeclFromDef("protected", 2);
|
|
||||||
insertToSectionDeclFromDef("protected slots", 3);
|
|
||||||
insertToSectionDeclFromDef("private", 4);
|
|
||||||
insertToSectionDeclFromDef("private slots", 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testInsertDeclFromDefTemplateFuncTypename()
|
|
||||||
{
|
|
||||||
QByteArray original =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"template<class T>\n"
|
|
||||||
"void Foo::fu@nc() {}\n";
|
|
||||||
|
|
||||||
QByteArray expected =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"public:\n"
|
|
||||||
" template<class T>\n"
|
|
||||||
" void func();\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"template<class T>\n"
|
|
||||||
"void Foo::fu@nc() {}\n";
|
|
||||||
|
|
||||||
InsertDeclFromDef factory;
|
|
||||||
QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testInsertDeclFromDefTemplateFuncInt()
|
|
||||||
{
|
|
||||||
QByteArray original =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"template<int N>\n"
|
|
||||||
"void Foo::fu@nc() {}\n";
|
|
||||||
|
|
||||||
QByteArray expected =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"public:\n"
|
|
||||||
" template<int N>\n"
|
|
||||||
" void func();\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"template<int N>\n"
|
|
||||||
"void Foo::fu@nc() {}\n";
|
|
||||||
|
|
||||||
InsertDeclFromDef factory;
|
|
||||||
QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testInsertDeclFromDefTemplateReturnType()
|
|
||||||
{
|
|
||||||
QByteArray original =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"std::vector<int> Foo::fu@nc() const {}\n";
|
|
||||||
|
|
||||||
QByteArray expected =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
"public:\n"
|
|
||||||
" std::vector<int> func() const;\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"std::vector<int> Foo::func() const {}\n";
|
|
||||||
|
|
||||||
InsertDeclFromDef factory;
|
|
||||||
QuickFixOperationTest(singleDocument(original, expected), &factory, {}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testInsertDeclFromDefNotTriggeredForTemplateFunc()
|
|
||||||
{
|
|
||||||
QByteArray contents =
|
|
||||||
"class Foo\n"
|
|
||||||
"{\n"
|
|
||||||
" template<class T>\n"
|
|
||||||
" void func();\n"
|
|
||||||
"};\n"
|
|
||||||
"\n"
|
|
||||||
"template<class T>\n"
|
|
||||||
"void Foo::fu@nc() {}\n";
|
|
||||||
|
|
||||||
InsertDeclFromDef factory;
|
|
||||||
QuickFixOperationTest(singleDocument(contents, ""), &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testAssignToLocalVariableTemplates()
|
void QuickfixTest::testAssignToLocalVariableTemplates()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@@ -97,15 +97,6 @@ private slots:
|
|||||||
void testGeneric_data();
|
void testGeneric_data();
|
||||||
void testGeneric();
|
void testGeneric();
|
||||||
|
|
||||||
void testInsertMemberFromUse_data();
|
|
||||||
void testInsertMemberFromUse();
|
|
||||||
|
|
||||||
void testInsertDeclFromDef();
|
|
||||||
void testInsertDeclFromDefTemplateFuncTypename();
|
|
||||||
void testInsertDeclFromDefTemplateFuncInt();
|
|
||||||
void testInsertDeclFromDefTemplateReturnType();
|
|
||||||
void testInsertDeclFromDefNotTriggeredForTemplateFunc();
|
|
||||||
|
|
||||||
void testAssignToLocalVariableTemplates();
|
void testAssignToLocalVariableTemplates();
|
||||||
|
|
||||||
void testExtractFunction_data();
|
void testExtractFunction_data();
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include "cppquickfixprojectsettings.h"
|
#include "cppquickfixprojectsettings.h"
|
||||||
#include "convertqt4connect.h"
|
#include "convertqt4connect.h"
|
||||||
#include "convertstringliteral.h"
|
#include "convertstringliteral.h"
|
||||||
|
#include "createdeclarationfromuse.h"
|
||||||
#include "insertfunctiondefinition.h"
|
#include "insertfunctiondefinition.h"
|
||||||
#include "moveclasstoownfile.h"
|
#include "moveclasstoownfile.h"
|
||||||
#include "movefunctiondefinition.h"
|
#include "movefunctiondefinition.h"
|
||||||
@@ -128,79 +129,6 @@ const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
|
|||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// In the following anonymous namespace all functions are collected, which could be of interest for
|
|
||||||
// different quick fixes.
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
QString nameString(const NameAST *name)
|
|
||||||
{
|
|
||||||
return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FullySpecifiedType typeOfExpr(const ExpressionAST *expr,
|
|
||||||
const CppRefactoringFilePtr &file,
|
|
||||||
const Snapshot &snapshot,
|
|
||||||
const LookupContext &context)
|
|
||||||
{
|
|
||||||
TypeOfExpression typeOfExpression;
|
|
||||||
typeOfExpression.init(file->cppDocument(), snapshot, context.bindings());
|
|
||||||
Scope *scope = file->scopeAt(expr->firstToken());
|
|
||||||
const QList<LookupItem> result = typeOfExpression(
|
|
||||||
file->textOf(expr).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();
|
|
||||||
return rewriteType(result.first().type(), &env, control);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Needs to consider the scope at the insertion site.
|
|
||||||
QString declFromExpr(const TypeOrExpr &typeOrExpr, const CallAST *call, const NameAST *varName,
|
|
||||||
const Snapshot &snapshot, const LookupContext &context,
|
|
||||||
const CppRefactoringFilePtr &file, bool makeConst)
|
|
||||||
{
|
|
||||||
const auto getTypeFromUser = [varName, call]() -> QString {
|
|
||||||
if (call)
|
|
||||||
return {};
|
|
||||||
const QString typeFromUser = QInputDialog::getText(Core::ICore::dialogParent(),
|
|
||||||
Tr::tr("Provide the type"),
|
|
||||||
Tr::tr("Data type:"), QLineEdit::Normal);
|
|
||||||
if (!typeFromUser.isEmpty())
|
|
||||||
return typeFromUser + ' ' + nameString(varName);
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
const auto getTypeOfExpr = [&](const ExpressionAST *expr) -> FullySpecifiedType {
|
|
||||||
return typeOfExpr(expr, file, snapshot, context);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
const FullySpecifiedType type = std::holds_alternative<FullySpecifiedType>(typeOrExpr)
|
|
||||||
? std::get<FullySpecifiedType>(typeOrExpr)
|
|
||||||
: getTypeOfExpr(std::get<const ExpressionAST *>(typeOrExpr));
|
|
||||||
if (!call)
|
|
||||||
return type.isValid() ? oo.prettyType(type, varName->name) : getTypeFromUser();
|
|
||||||
|
|
||||||
Function func(file->cppDocument()->translationUnit(), 0, varName->name);
|
|
||||||
func.setConst(makeConst);
|
|
||||||
for (ExpressionListAST *it = call->expression_list; it; it = it->next) {
|
|
||||||
Argument * const arg = new Argument(nullptr, 0, nullptr);
|
|
||||||
arg->setType(getTypeOfExpr(it->value));
|
|
||||||
func.addMember(arg);
|
|
||||||
}
|
|
||||||
return oo.prettyType(type) + ' ' + oo.prettyType(func.type(), varName->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class InverseLogicalComparisonOp: public CppQuickFixOperation
|
class InverseLogicalComparisonOp: public CppQuickFixOperation
|
||||||
@@ -1160,54 +1088,6 @@ void ConvertNumericLiteral::doMatch(const CppQuickFixInterface &interface, Quick
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class AddLocalDeclarationOp: public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AddLocalDeclarationOp(const CppQuickFixInterface &interface,
|
|
||||||
int priority,
|
|
||||||
const BinaryExpressionAST *binaryAST,
|
|
||||||
const SimpleNameAST *simpleNameAST)
|
|
||||||
: CppQuickFixOperation(interface, priority)
|
|
||||||
, binaryAST(binaryAST)
|
|
||||||
, simpleNameAST(simpleNameAST)
|
|
||||||
{
|
|
||||||
setDescription(Tr::tr("Add Local Declaration"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
CppRefactoringChanges refactoring(snapshot());
|
|
||||||
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
|
|
||||||
QString declaration = getDeclaration();
|
|
||||||
|
|
||||||
if (!declaration.isEmpty()) {
|
|
||||||
ChangeSet changes;
|
|
||||||
changes.replace(currentFile->startOf(binaryAST),
|
|
||||||
currentFile->endOf(simpleNameAST),
|
|
||||||
declaration);
|
|
||||||
currentFile->setChangeSet(changes);
|
|
||||||
currentFile->apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString getDeclaration()
|
|
||||||
{
|
|
||||||
CppRefactoringChanges refactoring(snapshot());
|
|
||||||
CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
|
|
||||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
const auto settings = CppQuickFixProjectsSettings::getQuickFixSettings(
|
|
||||||
ProjectExplorer::ProjectTree::currentProject());
|
|
||||||
|
|
||||||
if (currentFile->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
|
|
||||||
return "auto " + oo.prettyName(simpleNameAST->name);
|
|
||||||
return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(),
|
|
||||||
context(), currentFile, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const BinaryExpressionAST *binaryAST;
|
|
||||||
const SimpleNameAST *simpleNameAST;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
@@ -1678,545 +1558,7 @@ void CompleteSwitchCaseStatement::doMatch(const CppQuickFixInterface &interface,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class InsertDeclOperation: public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InsertDeclOperation(const CppQuickFixInterface &interface,
|
|
||||||
const FilePath &targetFilePath, const Class *targetSymbol,
|
|
||||||
InsertionPointLocator::AccessSpec xsSpec, const QString &decl, int priority)
|
|
||||||
: CppQuickFixOperation(interface, priority)
|
|
||||||
, m_targetFilePath(targetFilePath)
|
|
||||||
, m_targetSymbol(targetSymbol)
|
|
||||||
, m_xsSpec(xsSpec)
|
|
||||||
, m_decl(decl)
|
|
||||||
{
|
|
||||||
setDescription(Tr::tr("Add %1 Declaration")
|
|
||||||
.arg(InsertionPointLocator::accessSpecToString(xsSpec)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
CppRefactoringChanges refactoring(snapshot());
|
|
||||||
|
|
||||||
InsertionPointLocator locator(refactoring);
|
|
||||||
const InsertionLocation loc = locator.methodDeclarationInClass(
|
|
||||||
m_targetFilePath, m_targetSymbol, m_xsSpec);
|
|
||||||
QTC_ASSERT(loc.isValid(), return);
|
|
||||||
|
|
||||||
CppRefactoringFilePtr targetFile = refactoring.cppFile(m_targetFilePath);
|
|
||||||
int targetPosition = targetFile->position(loc.line(), loc.column());
|
|
||||||
|
|
||||||
ChangeSet target;
|
|
||||||
target.insert(targetPosition, loc.prefix() + m_decl);
|
|
||||||
targetFile->setChangeSet(target);
|
|
||||||
targetFile->setOpenEditor(true, targetPosition);
|
|
||||||
targetFile->apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString generateDeclaration(const Function *function);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FilePath m_targetFilePath;
|
|
||||||
const Class *m_targetSymbol;
|
|
||||||
InsertionPointLocator::AccessSpec m_xsSpec;
|
|
||||||
QString m_decl;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeclOperationFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeclOperationFactory(const CppQuickFixInterface &interface, const FilePath &filePath,
|
|
||||||
const Class *matchingClass, const QString &decl)
|
|
||||||
: m_interface(interface)
|
|
||||||
, m_filePath(filePath)
|
|
||||||
, m_matchingClass(matchingClass)
|
|
||||||
, m_decl(decl)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QuickFixOperation *operator()(InsertionPointLocator::AccessSpec xsSpec, int priority)
|
|
||||||
{
|
|
||||||
return new InsertDeclOperation(m_interface, m_filePath, m_matchingClass, xsSpec, m_decl, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const CppQuickFixInterface &m_interface;
|
|
||||||
const FilePath &m_filePath;
|
|
||||||
const Class *m_matchingClass;
|
|
||||||
const QString &m_decl;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
void InsertDeclFromDef::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
CppRefactoringFilePtr file = interface.currentFile();
|
|
||||||
|
|
||||||
FunctionDefinitionAST *funDef = nullptr;
|
|
||||||
int idx = 0;
|
|
||||||
for (; idx < path.size(); ++idx) {
|
|
||||||
AST *node = path.at(idx);
|
|
||||||
if (idx > 1) {
|
|
||||||
if (DeclaratorIdAST *declId = node->asDeclaratorId()) {
|
|
||||||
if (file->isCursorOn(declId)) {
|
|
||||||
if (FunctionDefinitionAST *candidate = path.at(idx - 2)->asFunctionDefinition()) {
|
|
||||||
funDef = candidate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node->asClassSpecifier())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!funDef || !funDef->symbol)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Function *fun = funDef->symbol;
|
|
||||||
if (Class *matchingClass = isMemberFunction(interface.context(), fun)) {
|
|
||||||
const QualifiedNameId *qName = fun->name()->asQualifiedNameId();
|
|
||||||
for (Symbol *symbol = matchingClass->find(qName->identifier());
|
|
||||||
symbol; symbol = symbol->next()) {
|
|
||||||
Symbol *s = symbol;
|
|
||||||
if (fun->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())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (s->type().match(fun->type())) {
|
|
||||||
// Declaration exists.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const FilePath fileName = matchingClass->filePath();
|
|
||||||
const QString decl = InsertDeclOperation::generateDeclaration(fun);
|
|
||||||
|
|
||||||
// Add several possible insertion locations for declaration
|
|
||||||
DeclOperationFactory operation(interface, fileName, matchingClass, decl);
|
|
||||||
|
|
||||||
result << operation(InsertionPointLocator::Public, 5)
|
|
||||||
<< operation(InsertionPointLocator::PublicSlot, 4)
|
|
||||||
<< operation(InsertionPointLocator::Protected, 3)
|
|
||||||
<< operation(InsertionPointLocator::ProtectedSlot, 2)
|
|
||||||
<< operation(InsertionPointLocator::Private, 1)
|
|
||||||
<< operation(InsertionPointLocator::PrivateSlot, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InsertDeclOperation::generateDeclaration(const Function *function)
|
|
||||||
{
|
|
||||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
oo.showFunctionSignatures = true;
|
|
||||||
oo.showReturnTypes = true;
|
|
||||||
oo.showArgumentNames = true;
|
|
||||||
oo.showEnclosingTemplate = true;
|
|
||||||
|
|
||||||
QString decl;
|
|
||||||
decl += oo.prettyType(function->type(), function->unqualifiedName());
|
|
||||||
decl += QLatin1String(";\n");
|
|
||||||
|
|
||||||
return decl;
|
|
||||||
}
|
|
||||||
|
|
||||||
class InsertMemberFromInitializationOp : public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InsertMemberFromInitializationOp(
|
|
||||||
const CppQuickFixInterface &interface,
|
|
||||||
const Class *theClass,
|
|
||||||
const NameAST *memberName,
|
|
||||||
const TypeOrExpr &typeOrExpr,
|
|
||||||
const CallAST *call,
|
|
||||||
InsertionPointLocator::AccessSpec accessSpec,
|
|
||||||
bool makeStatic,
|
|
||||||
bool makeConst)
|
|
||||||
: CppQuickFixOperation(interface),
|
|
||||||
m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call),
|
|
||||||
m_accessSpec(accessSpec), m_makeStatic(makeStatic), m_makeConst(makeConst)
|
|
||||||
{
|
|
||||||
if (call)
|
|
||||||
setDescription(Tr::tr("Add Member Function \"%1\"").arg(nameString(memberName)));
|
|
||||||
else
|
|
||||||
setDescription(Tr::tr("Add Class Member \"%1\"").arg(nameString(memberName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
QString decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(),
|
|
||||||
currentFile(), m_makeConst);
|
|
||||||
if (decl.isEmpty())
|
|
||||||
return;
|
|
||||||
if (m_makeStatic)
|
|
||||||
decl.prepend("static ");
|
|
||||||
|
|
||||||
const CppRefactoringChanges refactoring(snapshot());
|
|
||||||
const InsertionPointLocator locator(refactoring);
|
|
||||||
const FilePath filePath = FilePath::fromUtf8(m_class->fileName());
|
|
||||||
const InsertionLocation loc = locator.methodDeclarationInClass(
|
|
||||||
filePath, m_class, m_accessSpec);
|
|
||||||
QTC_ASSERT(loc.isValid(), return);
|
|
||||||
|
|
||||||
CppRefactoringFilePtr targetFile = refactoring.cppFile(filePath);
|
|
||||||
const int targetPosition = targetFile->position(loc.line(), loc.column());
|
|
||||||
ChangeSet target;
|
|
||||||
target.insert(targetPosition, loc.prefix() + decl + ";\n");
|
|
||||||
targetFile->setChangeSet(target);
|
|
||||||
targetFile->apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Class * const m_class;
|
|
||||||
const NameAST * const m_memberName;
|
|
||||||
const TypeOrExpr m_typeOrExpr;
|
|
||||||
const CallAST * m_call;
|
|
||||||
const InsertionPointLocator::AccessSpec m_accessSpec;
|
|
||||||
const bool m_makeStatic;
|
|
||||||
const bool m_makeConst;
|
|
||||||
};
|
|
||||||
|
|
||||||
void AddDeclarationForUndeclaredIdentifier::doMatch(const CppQuickFixInterface &interface,
|
|
||||||
QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
// Are we on a name?
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
if (!path.last()->asSimpleName())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Special case: Member initializer.
|
|
||||||
if (!checkForMemberInitializer(interface, result))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Are we inside a function?
|
|
||||||
const FunctionDefinitionAST *func = nullptr;
|
|
||||||
for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
|
|
||||||
func = (*it)->asFunctionDefinition();
|
|
||||||
if (!func)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Is this name declared somewhere already?
|
|
||||||
const CursorInEditor cursorInEditor(interface.cursor(), interface.filePath(),
|
|
||||||
interface.editor(), interface.editor()->textDocument());
|
|
||||||
const auto followSymbolFallback = [&](const Link &link) {
|
|
||||||
if (!link.hasValidTarget())
|
|
||||||
collectOperations(interface, result);
|
|
||||||
};
|
|
||||||
CppModelManager::followSymbol(cursorInEditor, followSymbolFallback, false, false,
|
|
||||||
FollowSymbolMode::Exact,
|
|
||||||
CppModelManager::Backend::Builtin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDeclarationForUndeclaredIdentifier::collectOperations(
|
|
||||||
const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
const CppRefactoringFilePtr &file = interface.currentFile();
|
|
||||||
for (int index = path.size() - 1; index != -1; --index) {
|
|
||||||
if (const auto call = path.at(index)->asCall())
|
|
||||||
return handleCall(call, interface, result);
|
|
||||||
|
|
||||||
// We only trigger if the identifier appears on the left-hand side of an
|
|
||||||
// assignment expression.
|
|
||||||
const auto binExpr = path.at(index)->asBinaryExpression();
|
|
||||||
if (!binExpr)
|
|
||||||
continue;
|
|
||||||
if (!binExpr->left_expression || !binExpr->right_expression
|
|
||||||
|| file->tokenAt(binExpr->binary_op_token).kind() != T_EQUAL
|
|
||||||
|| !interface.isCursorOn(binExpr->left_expression)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the case of "a.|b = c", find out the type of a, locate the class declaration
|
|
||||||
// and add a member b there.
|
|
||||||
if (const auto memberAccess = binExpr->left_expression->asMemberAccess()) {
|
|
||||||
if (interface.isCursorOn(memberAccess->member_name)
|
|
||||||
&& memberAccess->member_name == path.last()) {
|
|
||||||
maybeAddMember(interface, file->scopeAt(memberAccess->firstToken()),
|
|
||||||
file->textOf(memberAccess->base_expression).toUtf8(),
|
|
||||||
binExpr->right_expression, nullptr, result);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto idExpr = binExpr->left_expression->asIdExpression();
|
|
||||||
if (!idExpr || !idExpr->name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// In the case of "A::|b = c", add a static member b to A.
|
|
||||||
if (const auto qualName = idExpr->name->asQualifiedName()) {
|
|
||||||
return maybeAddStaticMember(interface, qualName, binExpr->right_expression, nullptr,
|
|
||||||
result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For an unqualified access, offer a local declaration and, if we are
|
|
||||||
// in a member function, a member declaration.
|
|
||||||
if (const auto simpleName = idExpr->name->asSimpleName()) {
|
|
||||||
if (!m_membersOnly)
|
|
||||||
result << new AddLocalDeclarationOp(interface, index, binExpr, simpleName);
|
|
||||||
maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
|
|
||||||
binExpr->right_expression, nullptr, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDeclarationForUndeclaredIdentifier::handleCall(
|
|
||||||
const CallAST *call, const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
if (!call->base_expression)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// In order to find out the return type, we need to check the context of the call.
|
|
||||||
// If it is a statement expression, the type is void, if it's a binary expression,
|
|
||||||
// we assume the type of the other side of the expression, if it's a return statement,
|
|
||||||
// we use the return type of the surrounding function, and if it's a declaration,
|
|
||||||
// we use the type of the variable. Other cases are not supported.
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
const CppRefactoringFilePtr &file = interface.currentFile();
|
|
||||||
TypeOrExpr returnTypeOrExpr;
|
|
||||||
for (auto it = path.rbegin(); it != path.rend(); ++it) {
|
|
||||||
if ((*it)->asCompoundStatement())
|
|
||||||
return;
|
|
||||||
if ((*it)->asExpressionStatement()) {
|
|
||||||
returnTypeOrExpr = FullySpecifiedType(new VoidType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (const auto binExpr = (*it)->asBinaryExpression()) {
|
|
||||||
returnTypeOrExpr = interface.isCursorOn(binExpr->left_expression)
|
|
||||||
? binExpr->right_expression : binExpr->left_expression;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (const auto returnExpr = (*it)->asReturnStatement()) {
|
|
||||||
for (auto it2 = std::next(it); it2 != path.rend(); ++it2) {
|
|
||||||
if (const auto func = (*it2)->asFunctionDefinition()) {
|
|
||||||
if (!func->symbol)
|
|
||||||
return;
|
|
||||||
returnTypeOrExpr = func->symbol->returnType();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (const auto declarator = (*it)->asDeclarator()) {
|
|
||||||
if (!interface.isCursorOn(declarator->initializer))
|
|
||||||
return;
|
|
||||||
const auto decl = (*std::next(it))->asSimpleDeclaration();
|
|
||||||
if (!decl || !decl->symbols)
|
|
||||||
return;
|
|
||||||
if (!decl->symbols->value->type().isValid())
|
|
||||||
return;
|
|
||||||
returnTypeOrExpr = decl->symbols->value->type();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::holds_alternative<const ExpressionAST *>(returnTypeOrExpr)
|
|
||||||
&& !std::get<const ExpressionAST *>(returnTypeOrExpr)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// a.f()
|
|
||||||
if (const auto memberAccess = call->base_expression->asMemberAccess()) {
|
|
||||||
if (!interface.isCursorOn(memberAccess->member_name))
|
|
||||||
return;
|
|
||||||
maybeAddMember(
|
|
||||||
interface, file->scopeAt(call->firstToken()),
|
|
||||||
file->textOf(memberAccess->base_expression).toUtf8(), returnTypeOrExpr, call, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto idExpr = call->base_expression->asIdExpression();
|
|
||||||
if (!idExpr || !idExpr->name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// A::f()
|
|
||||||
if (const auto qualName = idExpr->name->asQualifiedName())
|
|
||||||
return maybeAddStaticMember(interface, qualName, returnTypeOrExpr, call, result);
|
|
||||||
|
|
||||||
// f()
|
|
||||||
if (const auto simpleName = idExpr->name->asSimpleName()) {
|
|
||||||
maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
|
|
||||||
returnTypeOrExpr, call, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddDeclarationForUndeclaredIdentifier::checkForMemberInitializer(
|
|
||||||
const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
const int size = path.size();
|
|
||||||
if (size < 4)
|
|
||||||
return true;
|
|
||||||
const MemInitializerAST * const memInitializer = path.at(size - 2)->asMemInitializer();
|
|
||||||
if (!memInitializer)
|
|
||||||
return true;
|
|
||||||
if (!path.at(size - 3)->asCtorInitializer())
|
|
||||||
return true;
|
|
||||||
const FunctionDefinitionAST * ctor = path.at(size - 4)->asFunctionDefinition();
|
|
||||||
if (!ctor)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Now find the class.
|
|
||||||
const Class *theClass = nullptr;
|
|
||||||
if (size > 4) {
|
|
||||||
const ClassSpecifierAST * const classSpec = path.at(size - 5)->asClassSpecifier();
|
|
||||||
if (classSpec) // Inline constructor. We get the class directly.
|
|
||||||
theClass = classSpec->symbol;
|
|
||||||
}
|
|
||||||
if (!theClass) {
|
|
||||||
// Out-of-line constructor. We need to find the class.
|
|
||||||
SymbolFinder finder;
|
|
||||||
const QList<Declaration *> matches = finder.findMatchingDeclaration(
|
|
||||||
LookupContext(interface.currentFile()->cppDocument(), interface.snapshot()),
|
|
||||||
ctor->symbol);
|
|
||||||
if (!matches.isEmpty())
|
|
||||||
theClass = matches.first()->enclosingClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!theClass)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const SimpleNameAST * const name = path.at(size - 1)->asSimpleName();
|
|
||||||
QTC_ASSERT(name, return false);
|
|
||||||
|
|
||||||
// Check whether the member exists already.
|
|
||||||
if (theClass->find(interface.currentFile()->cppDocument()->translationUnit()->identifier(
|
|
||||||
name->identifier_token))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result << new InsertMemberFromInitializationOp(
|
|
||||||
interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression,
|
|
||||||
nullptr, InsertionPointLocator::Private, false, false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDeclarationForUndeclaredIdentifier::maybeAddMember(
|
|
||||||
const CppQuickFixInterface &interface, Scope *scope, const QByteArray &classTypeExpr,
|
|
||||||
const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
|
|
||||||
TypeOfExpression typeOfExpression;
|
|
||||||
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
|
|
||||||
interface.context().bindings());
|
|
||||||
const QList<LookupItem> lhsTypes = typeOfExpression(
|
|
||||||
classTypeExpr, scope,
|
|
||||||
TypeOfExpression::Preprocess);
|
|
||||||
if (lhsTypes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Type *type = lhsTypes.first().type().type();
|
|
||||||
if (!type)
|
|
||||||
return;
|
|
||||||
if (type->asPointerType()) {
|
|
||||||
type = type->asPointerType()->elementType().type();
|
|
||||||
if (!type)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto namedType = type->asNamedType();
|
|
||||||
if (!namedType)
|
|
||||||
return;
|
|
||||||
const ClassOrNamespace * const classOrNamespace
|
|
||||||
= interface.context().lookupType(namedType->name(), scope);
|
|
||||||
if (!classOrNamespace || !classOrNamespace->rootClass())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Class * const theClass = classOrNamespace->rootClass();
|
|
||||||
bool needsStatic = lhsTypes.first().type().isStatic();
|
|
||||||
|
|
||||||
// If the base expression refers to the same class that the member function is in,
|
|
||||||
// then we want to insert a private member, otherwise a public one.
|
|
||||||
const FunctionDefinitionAST *func = nullptr;
|
|
||||||
for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
|
|
||||||
func = (*it)->asFunctionDefinition();
|
|
||||||
QTC_ASSERT(func, return);
|
|
||||||
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
|
|
||||||
for (int i = 0; i < theClass->memberCount(); ++i) {
|
|
||||||
if (theClass->memberAt(i) == func->symbol) {
|
|
||||||
accessSpec = InsertionPointLocator::Private;
|
|
||||||
needsStatic = func->symbol->isStatic();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (accessSpec == InsertionPointLocator::Public) {
|
|
||||||
QList<Declaration *> decls;
|
|
||||||
QList<Declaration *> dummy;
|
|
||||||
SymbolFinder().findMatchingDeclaration(interface.context(), func->symbol, &decls,
|
|
||||||
&dummy, &dummy);
|
|
||||||
for (const Declaration * const decl : std::as_const(decls)) {
|
|
||||||
for (int i = 0; i < theClass->memberCount(); ++i) {
|
|
||||||
if (theClass->memberAt(i) == decl) {
|
|
||||||
accessSpec = InsertionPointLocator::Private;
|
|
||||||
needsStatic = decl->isStatic();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (accessSpec == InsertionPointLocator::Private)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result << new InsertMemberFromInitializationOp(interface, theClass, path.last()->asName(),
|
|
||||||
typeOrExpr, call, accessSpec, needsStatic,
|
|
||||||
func->symbol->isConst());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember(
|
|
||||||
const CppQuickFixInterface &interface, const QualifiedNameAST *qualName,
|
|
||||||
const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
|
|
||||||
if (!interface.isCursorOn(qualName->unqualified_name))
|
|
||||||
return;
|
|
||||||
if (qualName->unqualified_name != path.last())
|
|
||||||
return;
|
|
||||||
if (!qualName->nested_name_specifier_list)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const NameAST * const topLevelName
|
|
||||||
= qualName->nested_name_specifier_list->value->class_or_namespace_name;
|
|
||||||
if (!topLevelName)
|
|
||||||
return;
|
|
||||||
ClassOrNamespace * const classOrNamespace = interface.context().lookupType(
|
|
||||||
topLevelName->name, interface.currentFile()->scopeAt(qualName->firstToken()));
|
|
||||||
if (!classOrNamespace)
|
|
||||||
return;
|
|
||||||
QList<const Name *> otherNames;
|
|
||||||
for (auto it = qualName->nested_name_specifier_list->next; it; it = it->next) {
|
|
||||||
if (!it->value || !it->value->class_or_namespace_name)
|
|
||||||
return;
|
|
||||||
otherNames << it->value->class_or_namespace_name->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Class *theClass = nullptr;
|
|
||||||
if (!otherNames.isEmpty()) {
|
|
||||||
const Symbol * const symbol = classOrNamespace->lookupInScope(otherNames);
|
|
||||||
if (!symbol)
|
|
||||||
return;
|
|
||||||
theClass = symbol->asClass();
|
|
||||||
} else {
|
|
||||||
theClass = classOrNamespace->rootClass();
|
|
||||||
}
|
|
||||||
if (theClass) {
|
|
||||||
result << new InsertMemberFromInitializationOp(
|
|
||||||
interface, theClass, path.last()->asName(), typeOrExpr, call,
|
|
||||||
InsertionPointLocator::Public, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -4516,8 +3858,6 @@ void createCppQuickFixes()
|
|||||||
new ConvertFromAndToPointer;
|
new ConvertFromAndToPointer;
|
||||||
new ExtractFunction;
|
new ExtractFunction;
|
||||||
new ExtractLiteralAsParameter;
|
new ExtractLiteralAsParameter;
|
||||||
new InsertDeclFromDef;
|
|
||||||
new AddDeclarationForUndeclaredIdentifier;
|
|
||||||
|
|
||||||
new AssignToLocalVariable;
|
new AssignToLocalVariable;
|
||||||
|
|
||||||
@@ -4530,6 +3870,7 @@ void createCppQuickFixes()
|
|||||||
registerInsertFunctionDefinitionQuickfixes();
|
registerInsertFunctionDefinitionQuickfixes();
|
||||||
registerBringIdentifierIntoScopeQuickfixes();
|
registerBringIdentifierIntoScopeQuickfixes();
|
||||||
registerConvertStringLiteralQuickfixes();
|
registerConvertStringLiteralQuickfixes();
|
||||||
|
registerCreateDeclarationFromUseQuickfixes();
|
||||||
|
|
||||||
new OptimizeForLoop;
|
new OptimizeForLoop;
|
||||||
|
|
||||||
|
@@ -250,47 +250,6 @@ private:
|
|||||||
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
|
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
Adds a declarations to a definition
|
|
||||||
*/
|
|
||||||
class InsertDeclFromDef: public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result) override;
|
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
|
||||||
void setMembersOnly() { m_membersOnly = true; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
void collectOperations(const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result);
|
|
||||||
void handleCall(const CPlusPlus::CallAST *call, const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result);
|
|
||||||
|
|
||||||
// Returns whether to still do other checks.
|
|
||||||
bool checkForMemberInitializer(const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result);
|
|
||||||
|
|
||||||
void maybeAddMember(const CppQuickFixInterface &interface, CPlusPlus::Scope *scope,
|
|
||||||
const QByteArray &classTypeExpr, const TypeOrExpr &typeOrExpr,
|
|
||||||
const CPlusPlus::CallAST *call, TextEditor::QuickFixOperations &result);
|
|
||||||
|
|
||||||
void maybeAddStaticMember(
|
|
||||||
const CppQuickFixInterface &interface, const CPlusPlus::QualifiedNameAST *qualName,
|
|
||||||
const TypeOrExpr &typeOrExpr, const CPlusPlus::CallAST *call,
|
|
||||||
TextEditor::QuickFixOperations &result);
|
|
||||||
|
|
||||||
bool m_membersOnly = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Extracts the selected code and puts it to a function
|
Extracts the selected code and puts it to a function
|
||||||
*/
|
*/
|
||||||
|
@@ -3,10 +3,15 @@
|
|||||||
|
|
||||||
#include "cppquickfixhelpers.h"
|
#include "cppquickfixhelpers.h"
|
||||||
|
|
||||||
|
#include "../cppcodestylesettings.h"
|
||||||
#include "../cppprojectfile.h"
|
#include "../cppprojectfile.h"
|
||||||
#include "../includeutils.h"
|
#include "../includeutils.h"
|
||||||
#include "cppquickfixassistant.h"
|
#include "cppquickfixassistant.h"
|
||||||
|
|
||||||
|
#include <cplusplus/CppRewriter.h>
|
||||||
|
#include <cplusplus/Overview.h>
|
||||||
|
#include <cplusplus/TypeOfExpression.h>
|
||||||
|
|
||||||
using namespace CPlusPlus;
|
using namespace CPlusPlus;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -153,4 +158,36 @@ CPlusPlus::Namespace *isNamespaceFunction(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString nameString(const CPlusPlus::NameAST *name)
|
||||||
|
{
|
||||||
|
return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlusPlus::FullySpecifiedType typeOfExpr(
|
||||||
|
const ExpressionAST *expr,
|
||||||
|
const CppRefactoringFilePtr &file,
|
||||||
|
const Snapshot &snapshot,
|
||||||
|
const LookupContext &context)
|
||||||
|
{
|
||||||
|
TypeOfExpression typeOfExpression;
|
||||||
|
typeOfExpression.init(file->cppDocument(), snapshot, context.bindings());
|
||||||
|
Scope *scope = file->scopeAt(expr->firstToken());
|
||||||
|
const QList<LookupItem> result
|
||||||
|
= typeOfExpression(file->textOf(expr).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();
|
||||||
|
return rewriteType(result.first().type(), &env, control);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CppEditor::Internal
|
} // namespace CppEditor::Internal
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../cpprefactoringchanges.h"
|
#include "../cpprefactoringchanges.h"
|
||||||
|
#include "cppquickfixes.h"
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
@@ -36,4 +37,12 @@ CPlusPlus::Class *isMemberFunction(
|
|||||||
CPlusPlus::Namespace *isNamespaceFunction(
|
CPlusPlus::Namespace *isNamespaceFunction(
|
||||||
const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
|
const CPlusPlus::LookupContext &context, CPlusPlus::Function *function);
|
||||||
|
|
||||||
|
QString nameString(const CPlusPlus::NameAST *name);
|
||||||
|
|
||||||
|
CPlusPlus::FullySpecifiedType typeOfExpr(
|
||||||
|
const CPlusPlus::ExpressionAST *expr,
|
||||||
|
const CppRefactoringFilePtr &file,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::LookupContext &context);
|
||||||
|
|
||||||
} // namespace CppEditor::Internal
|
} // namespace CppEditor::Internal
|
||||||
|
1213
src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp
Normal file
1213
src/plugins/cppeditor/quickfixes/createdeclarationfromuse.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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 registerCreateDeclarationFromUseQuickfixes();
|
||||||
|
} // namespace CppEditor::Internal
|
Reference in New Issue
Block a user