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/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
|
||||
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
||||
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
|
||||
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
||||
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
||||
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
|
||||
|
@@ -247,6 +247,8 @@ QtcPlugin {
|
||||
"cppquickfixsettingspage.h",
|
||||
"cppquickfixsettingswidget.cpp",
|
||||
"cppquickfixsettingswidget.h",
|
||||
"createdeclarationfromuse.cpp",
|
||||
"createdeclarationfromuse.h",
|
||||
"insertfunctiondefinition.cpp",
|
||||
"insertfunctiondefinition.h",
|
||||
"moveclasstoownfile.cpp",
|
||||
|
@@ -1566,16 +1566,6 @@ void QuickfixTest::testGeneric_data()
|
||||
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
||||
<< _("void @WhAt_TODO_hErE();\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()
|
||||
@@ -1624,478 +1614,6 @@ CppCodeStyleSettings CppCodeStyleSettingsChanger::currentSettings()
|
||||
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()
|
||||
{
|
||||
|
||||
|
@@ -97,15 +97,6 @@ private slots:
|
||||
void testGeneric_data();
|
||||
void testGeneric();
|
||||
|
||||
void testInsertMemberFromUse_data();
|
||||
void testInsertMemberFromUse();
|
||||
|
||||
void testInsertDeclFromDef();
|
||||
void testInsertDeclFromDefTemplateFuncTypename();
|
||||
void testInsertDeclFromDefTemplateFuncInt();
|
||||
void testInsertDeclFromDefTemplateReturnType();
|
||||
void testInsertDeclFromDefNotTriggeredForTemplateFunc();
|
||||
|
||||
void testAssignToLocalVariableTemplates();
|
||||
|
||||
void testExtractFunction_data();
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "cppquickfixprojectsettings.h"
|
||||
#include "convertqt4connect.h"
|
||||
#include "convertstringliteral.h"
|
||||
#include "createdeclarationfromuse.h"
|
||||
#include "insertfunctiondefinition.h"
|
||||
#include "moveclasstoownfile.h"
|
||||
#include "movefunctiondefinition.h"
|
||||
@@ -128,79 +129,6 @@ const QList<CppQuickFixFactory *> &CppQuickFixFactory::cppQuickFixFactories()
|
||||
|
||||
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 {
|
||||
|
||||
class InverseLogicalComparisonOp: public CppQuickFixOperation
|
||||
@@ -1160,54 +1088,6 @@ void ConvertNumericLiteral::doMatch(const CppQuickFixInterface &interface, Quick
|
||||
|
||||
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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -4516,8 +3858,6 @@ void createCppQuickFixes()
|
||||
new ConvertFromAndToPointer;
|
||||
new ExtractFunction;
|
||||
new ExtractLiteralAsParameter;
|
||||
new InsertDeclFromDef;
|
||||
new AddDeclarationForUndeclaredIdentifier;
|
||||
|
||||
new AssignToLocalVariable;
|
||||
|
||||
@@ -4530,6 +3870,7 @@ void createCppQuickFixes()
|
||||
registerInsertFunctionDefinitionQuickfixes();
|
||||
registerBringIdentifierIntoScopeQuickfixes();
|
||||
registerConvertStringLiteralQuickfixes();
|
||||
registerCreateDeclarationFromUseQuickfixes();
|
||||
|
||||
new OptimizeForLoop;
|
||||
|
||||
|
@@ -250,47 +250,6 @@ private:
|
||||
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
|
||||
*/
|
||||
|
@@ -3,10 +3,15 @@
|
||||
|
||||
#include "cppquickfixhelpers.h"
|
||||
|
||||
#include "../cppcodestylesettings.h"
|
||||
#include "../cppprojectfile.h"
|
||||
#include "../includeutils.h"
|
||||
#include "cppquickfixassistant.h"
|
||||
|
||||
#include <cplusplus/CppRewriter.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <cplusplus/TypeOfExpression.h>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace Utils;
|
||||
|
||||
@@ -153,4 +158,36 @@ CPlusPlus::Namespace *isNamespaceFunction(
|
||||
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
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../cpprefactoringchanges.h"
|
||||
#include "cppquickfixes.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
@@ -36,4 +37,12 @@ CPlusPlus::Class *isMemberFunction(
|
||||
CPlusPlus::Namespace *isNamespaceFunction(
|
||||
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
|
||||
|
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