Commit 6f3b4121 authored by Lorenz Haas's avatar Lorenz Haas Committed by Nikolai Kosjar
Browse files

CppEditor: Add refactoring operation to move definition of a function



With this new operation it is possible to move a function definition
from a cpp file to the header file or vice versa. One can also move a
function definition outside the class body.

Task-number: QTCREATORBUG-516
Task-number: QTCREATORBUG-5364
Change-Id: Id3daefe79284bd9086282369c9d251e003951c11
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent bf4c2365
......@@ -1862,6 +1862,33 @@
\li Create Getter and Setter Member Functions
\li Creates getter and setter member functions for member variables.
\li Member variable in class definition
\row
\li Move function definition
\li Moves a function definition to the implementation file, outside the class or back to its declaration. For example, rewrites:
\code
class Foo
{
void bar()
{
// do stuff here
}
};
\endcode
as
\code
class Foo
{
void bar();
};
void Foo::bar() {
// do stuff here
}
\endcode
\li Function signature
\endtable
......
......@@ -131,6 +131,22 @@ private slots:
void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01();
void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02();
void test_quickfix_MoveFuncDefOutside_MemberFuncToCpp();
void test_quickfix_MoveFuncDefOutside_MemberFuncOutside();
void test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS();
void test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing();
void test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs();
void test_quickfix_MoveFuncDefOutside_FreeFuncToCpp();
void test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS();
void test_quickfix_MoveFuncDefToDecl_MemberFunc();
void test_quickfix_MoveFuncDefToDecl_MemberFuncOutside();
void test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNS();
void test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNSUsing();
void test_quickfix_MoveFuncDefToDecl_MemberFuncOutsideWithNs();
void test_quickfix_MoveFuncDefToDecl_FreeFuncToCpp();
void test_quickfix_MoveFuncDefToDecl_FreeFuncToCppNS();
// The following tests depend on the projects that are loaded on startup
// and will be skipped in case no projects are loaded.
void test_openEachFile();
......
......@@ -1042,3 +1042,488 @@ void CppPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment0
TestCase data(testFiles);
data.run(&factory);
}
/// Check: Move definition from header to cpp.
void CppPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCpp()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"class Foo {\n"
" inline int numbe@r() const {\n"
" return 5;\n"
" }\n"
"\n"
" void bar();\n"
"};";
expected =
"class Foo {\n"
" inline int number() const;\n"
"\n"
" void bar();\n"
"};\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n";
expected =
"#include \"file.h\"\n"
"\n"
"\n"
"int Foo::number() const {\n"
" return 5;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefOutside factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: Move definition outside class
void CppPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside()
{
QByteArray original =
"class Foo {\n"
" inline int numbe@r() const {\n"
" return 5;\n"
" }\n"
"};";
QByteArray expected =
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"\n"
"int Foo::number() const {\n"
" return 5;\n"
"}"
"\n\n";
MoveFuncDefOutside factory;
TestCase data(original, expected);
data.run(&factory);
}
/// Check: Move definition from header to cpp (with namespace).
void CppPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int numbe@r() const {\n"
" return 5;\n"
" }\n"
"};\n"
"}";
expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"}\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n";
expected =
"#include \"file.h\"\n"
"\n"
"\n"
"int MyNs::Foo::number() const {\n"
" return 5;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefOutside factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: Move definition from header to cpp (with namespace + using).
void CppPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int numbe@r() const {\n"
" return 5;\n"
" }\n"
"};\n"
"}";
expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"}\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"using namespace MyNs;\n"
"\n";
expected =
"#include \"file.h\"\n"
"using namespace MyNs;\n"
"\n"
"\n"
"int Foo::number() const {\n"
" return 5;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefOutside factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: Move definition outside class with Namespace
void CppPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs()
{
QByteArray original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int numbe@r() const {\n"
" return 5;\n"
" }\n"
"};}";
QByteArray expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"\n"
"int Foo::number() const {\n"
" return 5;\n"
"}"
"\n}\n";
MoveFuncDefOutside factory;
TestCase data(original, expected);
data.run(&factory);
}
/// Check: Move free function from header to cpp.
void CppPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCpp()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"int numbe@r() const {\n"
" return 5;\n"
"}\n";
expected =
"int number() const;\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n";
expected =
"#include \"file.h\"\n"
"\n"
"\n"
"int number() const {\n"
" return 5;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefOutside factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: Move free function from header to cpp (with namespace).
void CppPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNamespace {\n"
"int numbe@r() const {\n"
" return 5;\n"
"}\n"
"}\n";
expected =
"namespace MyNamespace {\n"
"int number() const;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n";
expected =
"#include \"file.h\"\n"
"\n"
"\n"
"int MyNamespace::number() const {\n"
" return 5;\n"
"}\n"
"\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefOutside factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCpp()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_MemberFunc()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original = "class Foo {inline int number() const;};\n";
expected = "class Foo {inline int number() const {return 5;}};\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n"
"int Foo::num@ber() const {return 5;}\n";
expected =
"#include \"file.h\"\n"
"\n\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefToDecl factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncOutside()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncOutside()
{
QByteArray original =
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"\n"
"int Foo::num@ber() const {\n"
" return 5;\n"
"}\n";
QByteArray expected =
"class Foo {\n"
" inline int number() const {\n"
" return 5;\n"
" }\n"
"};\n"
"\n\n\n";
MoveFuncDefToDecl factory;
TestCase data(original, expected);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNS()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"}\n";
expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const {\n"
" return 5;\n"
" }\n"
"};\n"
"}\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n"
"int MyNs::Foo::num@ber() const {\n"
" return 5;\n"
"}\n";
expected = "#include \"file.h\"\n\n\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefToDecl factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNSUsing()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"}\n";
expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const {\n"
" return 5;\n"
" }\n"
"};\n"
"}\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"using namespace MyNs;\n"
"\n"
"int Foo::num@ber() const {\n"
" return 5;\n"
"}\n";
expected =
"#include \"file.h\"\n"
"using namespace MyNs;\n"
"\n\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefToDecl factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncOutsideWithNs()
{
QByteArray original =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const;\n"
"};\n"
"\n"
"int Foo::numb@er() const {\n"
" return 5;\n"
"}"
"\n}\n";
QByteArray expected =
"namespace MyNs {\n"
"class Foo {\n"
" inline int number() const {\n"
" return 5;\n"
" }\n"
"};\n\n\n}\n\n";
MoveFuncDefToDecl factory;
TestCase data(original, expected);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_FreeFuncToCpp()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCpp()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original = "int number() const;\n";
expected =
"int number() const {\n"
" return 5;\n"
"}\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n"
"\n"
"int numb@er() const {\n"
" return 5;\n"
"}\n";
expected = "#include \"file.h\"\n\n\n\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefToDecl factory;
TestCase data(testFiles);
data.run(&factory);
}
/// Check: revert test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS()
void CppPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCppNS()
{
QList<TestDocumentPtr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original =
"namespace MyNamespace {\n"
"int number() const;\n"
"}\n";
expected =
"namespace MyNamespace {\n"
"int number() const {\n"
" return 5;\n"
"}\n"
"}\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.h"));
// Source File
original =
"#include \"file.h\"\n"
"\n"
"int MyNamespace::nu@mber() const {\n"
" return 5;\n"
"}\n";
expected =
"#include \"file.h\"\n"
"\n\n\n";
testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp"));
MoveFuncDefToDecl factory;
TestCase data(testFiles);
data.run(&factory);
}
......@@ -41,6 +41,7 @@
#include <cpptools/insertionpointlocator.h>
#include <cpptools/symbolfinder.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/CPlusPlusForwardDeclarations.h>
#include <cplusplus/CppRewriter.h>
#include <cplusplus/DependencyTable.h>
......@@ -104,6 +105,9 @@ void CppEditor::Internal::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
plugIn->addAutoReleasedObject(new GenerateGetterSetter);
plugIn->addAutoReleasedObject(new InsertDeclFromDef);
plugIn->addAutoReleasedObject(new InsertDefFromDecl);
plugIn->addAutoReleasedObject(new MoveFuncDefOutside);
plugIn->addAutoReleasedObject(new MoveFuncDefToDecl);
}
// In the following anonymous namespace all functions are collected, which could be of interest for
......@@ -3544,3 +3548,389 @@ void ApplyDeclDefLinkChanges::match(const CppQuickFixInterface &interface,
op->setDescription(FunctionDeclDefLink::tr("Apply Function Signature Changes"));
result += op;
}
namespace {
QString getDefinitionSignature(const CppQuickFixAssistInterface *assist, Function *func,
CppRefactoringFilePtr &file, Scope *scope)
{
QTC_ASSERT(assist, return QString());
QTC_ASSERT(func, return QString());
QTC_ASSERT(scope, return QString());
LookupContext cppContext(file->cppDocument(), assist->snapshot());
ClassOrNamespace *cppCoN = cppContext.lookupType(scope);
if (!cppCoN)
cppCoN = cppContext.globalNamespace();
SubstitutionEnvironment env;
env.setContext(assist->context());
env.switchScope(func->enclosingScope());
UseMinimalNames q(cppCoN);
env.enter(&q);
Control *control = assist->context().control().data();
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
oo.showFunctionSignatures = true;
oo.showReturnTypes = true;
oo.showArgumentNames = true;
const FullySpecifiedType tn = rewriteType(func->type(), &env, control);
const QString name = oo.prettyName(LookupContext::minimalName(func, cppCoN, control));
return oo.prettyType(tn, name);
}
class MoveFuncDefOutsideOp : public CppQuickFixOperation
{
public:
enum MoveType {
MoveOutside,
MoveToCppFile,