aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp')
-rw-r--r--src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp2123
1 files changed, 2123 insertions, 0 deletions
diff --git a/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
new file mode 100644
index 0000000000..693b2c4a1d
--- /dev/null
+++ b/src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
@@ -0,0 +1,2123 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "insertfunctiondefinition.h"
+
+#include "../cppcodestylepreferences.h"
+#include "../cppcodestylesettings.h"
+#include "../cppeditortr.h"
+#include "../cppeditorwidget.h"
+#include "../cpprefactoringchanges.h"
+#include "../cpptoolssettings.h"
+#include "../insertionpointlocator.h"
+#include "../symbolfinder.h"
+#include "cppquickfix.h"
+#include "cppquickfixhelpers.h"
+
+#include <coreplugin/icore.h>
+#include <cplusplus/CppRewriter.h>
+#include <cplusplus/Overview.h>
+#include <utils/layoutbuilder.h>
+
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QHBoxLayout>
+
+#ifdef WITH_TESTS
+#include "cppquickfix_test.h"
+#include <QtTest>
+#endif
+
+using namespace CPlusPlus;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace CppEditor::Internal {
+namespace {
+
+enum DefPos {
+ DefPosInsideClass,
+ DefPosOutsideClass,
+ DefPosImplementationFile
+};
+
+enum class InsertDefsFromDeclsMode {
+ Off, // Testing: simulates user canceling the dialog
+ Impl, // Testing: simulates user choosing cpp file for every function
+ Alternating, // Testing: simulates user choosing a different DefPos for every function
+ User // Normal interactive mode
+};
+
+class InsertDefOperation: public CppQuickFixOperation
+{
+public:
+ // Make sure that either loc is valid or targetFileName is not empty.
+ InsertDefOperation(const CppQuickFixInterface &interface,
+ Declaration *decl, DeclaratorAST *declAST, const InsertionLocation &loc,
+ const DefPos defpos, const FilePath &targetFileName = {},
+ bool freeFunction = false)
+ : CppQuickFixOperation(interface, 0)
+ , m_decl(decl)
+ , m_declAST(declAST)
+ , m_loc(loc)
+ , m_defpos(defpos)
+ , m_targetFilePath(targetFileName)
+ {
+ if (m_defpos == DefPosImplementationFile) {
+ const FilePath declFile = decl->filePath();
+ const FilePath targetFile = m_loc.isValid() ? m_loc.filePath() : m_targetFilePath;
+ const FilePath resolved = targetFile.relativePathFrom(declFile.parentDir());
+ setPriority(2);
+ setDescription(Tr::tr("Add Definition in %1").arg(resolved.displayName()));
+ } else if (freeFunction) {
+ setDescription(Tr::tr("Add Definition Here"));
+ } else if (m_defpos == DefPosInsideClass) {
+ setDescription(Tr::tr("Add Definition Inside Class"));
+ } else if (m_defpos == DefPosOutsideClass) {
+ setPriority(1);
+ setDescription(Tr::tr("Add Definition Outside Class"));
+ }
+ }
+
+ static void insertDefinition(
+ const CppQuickFixOperation *op,
+ InsertionLocation loc,
+ DefPos defPos,
+ DeclaratorAST *declAST,
+ Declaration *decl,
+ const FilePath &targetFilePath,
+ ChangeSet *changeSet = nullptr)
+ {
+ CppRefactoringChanges refactoring(op->snapshot());
+ if (!loc.isValid())
+ loc = insertLocationForMethodDefinition(decl, true, NamespaceHandling::Ignore,
+ refactoring, targetFilePath);
+ QTC_ASSERT(loc.isValid(), return);
+
+ CppRefactoringFilePtr targetFile = refactoring.cppFile(loc.filePath());
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ oo.showArgumentNames = true;
+ oo.showEnclosingTemplate = true;
+
+ // What we really want is to show template parameters for the class, but not for the
+ // function, but we cannot express that. This is an approximation that will work
+ // as long as either the surrounding class or the function is not a template.
+ oo.showTemplateParameters = decl->enclosingClass()
+ && decl->enclosingClass()->enclosingTemplate();
+
+ if (defPos == DefPosInsideClass) {
+ const int targetPos = targetFile->position(loc.line(), loc.column());
+ ChangeSet localChangeSet;
+ ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
+ target->replace(targetPos - 1, targetPos, QLatin1String("\n {\n\n}")); // replace ';'
+
+ if (!changeSet) {
+ targetFile->setOpenEditor(true, targetPos);
+ targetFile->apply(*target);
+
+ // Move cursor inside definition
+ QTextCursor c = targetFile->cursor();
+ c.setPosition(targetPos);
+ c.movePosition(QTextCursor::Down);
+ c.movePosition(QTextCursor::EndOfLine);
+ op->editor()->setTextCursor(c);
+ }
+ } else {
+ // make target lookup context
+ Document::Ptr targetDoc = targetFile->cppDocument();
+ Scope *targetScope = targetDoc->scopeAt(loc.line(), loc.column());
+
+ // Correct scope in case of a function try-block. See QTCREATORBUG-14661.
+ if (targetScope && targetScope->asBlock()) {
+ if (Class * const enclosingClass = targetScope->enclosingClass())
+ targetScope = enclosingClass;
+ else
+ targetScope = targetScope->enclosingNamespace();
+ }
+
+ LookupContext targetContext(targetDoc, op->snapshot());
+ ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
+ if (!targetCoN)
+ targetCoN = targetContext.globalNamespace();
+
+ // setup rewriting to get minimally qualified names
+ SubstitutionEnvironment env;
+ env.setContext(op->context());
+ env.switchScope(decl->enclosingScope());
+ UseMinimalNames q(targetCoN);
+ env.enter(&q);
+ Control *control = op->context().bindings()->control().get();
+
+ // rewrite the function type
+ const FullySpecifiedType tn = rewriteType(decl->type(), &env, control);
+
+ // rewrite the function name
+ if (nameIncludesOperatorName(decl->name())) {
+ const QString operatorNameText = op->currentFile()->textOf(declAST->core_declarator);
+ oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
+ }
+ const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN,
+ control));
+
+ const QString inlinePref = inlinePrefix(targetFilePath, [defPos] {
+ return defPos == DefPosOutsideClass;
+ });
+
+ const QString prettyType = oo.prettyType(tn, name);
+
+ QString input = prettyType;
+ int index = 0;
+ while (input.startsWith("template")) {
+ QRegularExpression templateRegex("template\\s*<[^>]*>");
+ QRegularExpressionMatch match = templateRegex.match(input);
+ if (match.hasMatch()) {
+ index += match.captured().size() + 1;
+ input = input.mid(match.captured().size() + 1);
+ }
+ }
+
+ QString defText = prettyType;
+ defText.insert(index, inlinePref);
+ defText += QLatin1String("\n{\n\n}");
+
+ ChangeSet localChangeSet;
+ ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
+ const int targetPos = targetFile->position(loc.line(), loc.column());
+ target->insert(targetPos, loc.prefix() + defText + loc.suffix());
+
+ if (!changeSet) {
+ targetFile->setOpenEditor(true, targetPos);
+ targetFile->apply(*target);
+
+ // Move cursor inside definition
+ QTextCursor c = targetFile->cursor();
+ c.setPosition(targetPos);
+ c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,
+ loc.prefix().count(QLatin1String("\n")) + 2);
+ c.movePosition(QTextCursor::EndOfLine);
+ if (defPos == DefPosImplementationFile) {
+ if (targetFile->editor())
+ targetFile->editor()->setTextCursor(c);
+ } else {
+ op->editor()->setTextCursor(c);
+ }
+ }
+ }
+ }
+
+private:
+ void perform() override
+ {
+ insertDefinition(this, m_loc, m_defpos, m_declAST, m_decl, m_targetFilePath);
+ }
+
+ Declaration *m_decl;
+ DeclaratorAST *m_declAST;
+ InsertionLocation m_loc;
+ const DefPos m_defpos;
+ const FilePath m_targetFilePath;
+};
+
+class MemberFunctionImplSetting
+{
+public:
+ Symbol *func = nullptr;
+ DefPos defPos = DefPosImplementationFile;
+};
+using MemberFunctionImplSettings = QList<MemberFunctionImplSetting>;
+
+class AddImplementationsDialog : public QDialog
+{
+public:
+ AddImplementationsDialog(const QList<Symbol *> &candidates, const FilePath &implFile)
+ : QDialog(Core::ICore::dialogParent()), m_candidates(candidates)
+ {
+ setWindowTitle(Tr::tr("Member Function Implementations"));
+
+ const auto defaultImplTargetComboBox = new QComboBox;
+ QStringList implTargetStrings{Tr::tr("None"), Tr::tr("Inline"), Tr::tr("Outside Class")};
+ if (!implFile.isEmpty())
+ implTargetStrings.append(implFile.fileName());
+ defaultImplTargetComboBox->insertItems(0, implTargetStrings);
+ connect(defaultImplTargetComboBox, &QComboBox::currentIndexChanged, this,
+ [this](int index) {
+ for (int i = 0; i < m_implTargetBoxes.size(); ++i) {
+ if (!m_candidates.at(i)->type()->asFunctionType()->isPureVirtual())
+ static_cast<QComboBox *>(m_implTargetBoxes.at(i))->setCurrentIndex(index);
+ }
+ });
+ const auto defaultImplTargetLayout = new QHBoxLayout;
+ defaultImplTargetLayout->addWidget(new QLabel(Tr::tr("Default implementation location:")));
+ defaultImplTargetLayout->addWidget(defaultImplTargetComboBox);
+
+ const auto candidatesLayout = new QGridLayout;
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ oo.showReturnTypes = true;
+ for (int i = 0; i < m_candidates.size(); ++i) {
+ const Function * const func = m_candidates.at(i)->type()->asFunctionType();
+ QTC_ASSERT(func, continue);
+ const auto implTargetComboBox = new QComboBox;
+ m_implTargetBoxes.append(implTargetComboBox);
+ implTargetComboBox->insertItems(0, implTargetStrings);
+ if (func->isPureVirtual())
+ implTargetComboBox->setCurrentIndex(0);
+ candidatesLayout->addWidget(new QLabel(oo.prettyType(func->type(), func->name())),
+ i, 0);
+ candidatesLayout->addWidget(implTargetComboBox, i, 1);
+ }
+
+ const auto buttonBox
+ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ defaultImplTargetComboBox->setCurrentIndex(implTargetStrings.size() - 1);
+ const auto mainLayout = new QVBoxLayout(this);
+ mainLayout->addLayout(defaultImplTargetLayout);
+ mainLayout->addWidget(Layouting::createHr(this));
+ mainLayout->addLayout(candidatesLayout);
+ mainLayout->addWidget(buttonBox);
+ }
+
+ MemberFunctionImplSettings settings() const
+ {
+ QTC_ASSERT(m_candidates.size() == m_implTargetBoxes.size(), return {});
+ MemberFunctionImplSettings settings;
+ for (int i = 0; i < m_candidates.size(); ++i) {
+ MemberFunctionImplSetting setting;
+ const int index = m_implTargetBoxes.at(i)->currentIndex();
+ const bool addImplementation = index != 0;
+ if (!addImplementation)
+ continue;
+ setting.func = m_candidates.at(i);
+ setting.defPos = static_cast<DefPos>(index - 1);
+ settings << setting;
+ }
+ return settings;
+ }
+
+private:
+ const QList<Symbol *> m_candidates;
+ QList<QComboBox *> m_implTargetBoxes;
+};
+
+class InsertDefsOperation: public CppQuickFixOperation
+{
+public:
+ InsertDefsOperation(const CppQuickFixInterface &interface) : CppQuickFixOperation(interface)
+ {
+ setDescription(Tr::tr("Create Implementations for Member Functions"));
+
+ m_classAST = astForClassOperations(interface);
+ if (!m_classAST)
+ return;
+ const Class * const theClass = m_classAST->symbol;
+ if (!theClass)
+ return;
+
+ // Collect all member functions.
+ for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
+ Symbol * const s = *it;
+ if (!s->identifier() || !s->type() || !s->asDeclaration() || s->asFunction())
+ continue;
+ Function * const func = s->type()->asFunctionType();
+ if (!func || func->isSignal() || func->isFriend())
+ continue;
+ Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ oo.showFunctionSignatures = true;
+ if (magicQObjectFunctions().contains(oo.prettyName(func->name())))
+ continue;
+ m_declarations << s;
+ }
+ }
+
+ bool isApplicable() const { return !m_declarations.isEmpty(); }
+ void setMode(InsertDefsFromDeclsMode mode) { m_mode = mode; }
+
+private:
+ void perform() override
+ {
+ QList<Symbol *> unimplemented;
+ SymbolFinder symbolFinder;
+ for (Symbol * const s : std::as_const(m_declarations)) {
+ if (!symbolFinder.findMatchingDefinition(s, snapshot()))
+ unimplemented << s;
+ }
+ if (unimplemented.isEmpty())
+ return;
+
+ CppRefactoringChanges refactoring(snapshot());
+ const bool isHeaderFile = ProjectFile::isHeader(ProjectFile::classify(filePath().toString()));
+ FilePath cppFile; // Only set if the class is defined in a header file.
+ if (isHeaderFile) {
+ InsertionPointLocator locator(refactoring);
+ for (const InsertionLocation &location
+ : locator.methodDefinition(unimplemented.first(), false, {})) {
+ if (!location.isValid())
+ continue;
+ const FilePath filePath = location.filePath();
+ if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
+ const FilePath source = correspondingHeaderOrSource(filePath);
+ if (!source.isEmpty())
+ cppFile = source;
+ } else {
+ cppFile = filePath;
+ }
+ break;
+ }
+ }
+
+ MemberFunctionImplSettings settings;
+ switch (m_mode) {
+ case InsertDefsFromDeclsMode::User: {
+ AddImplementationsDialog dlg(unimplemented, cppFile);
+ if (dlg.exec() == QDialog::Accepted)
+ settings = dlg.settings();
+ break;
+ }
+ case InsertDefsFromDeclsMode::Impl: {
+ for (Symbol * const func : std::as_const(unimplemented)) {
+ MemberFunctionImplSetting setting;
+ setting.func = func;
+ setting.defPos = DefPosImplementationFile;
+ settings << setting;
+ }
+ break;
+ }
+ case InsertDefsFromDeclsMode::Alternating: {
+ int defPos = DefPosImplementationFile;
+ const auto incDefPos = [&defPos] {
+ defPos = (defPos + 1) % (DefPosImplementationFile + 2);
+ };
+ for (Symbol * const func : std::as_const(unimplemented)) {
+ incDefPos();
+ if (defPos > DefPosImplementationFile)
+ continue;
+ MemberFunctionImplSetting setting;
+ setting.func = func;
+ setting.defPos = static_cast<DefPos>(defPos);
+ settings << setting;
+ }
+ break;
+ }
+ case InsertDefsFromDeclsMode::Off:
+ break;
+ }
+
+ if (settings.isEmpty())
+ return;
+
+ class DeclFinder : public ASTVisitor
+ {
+ public:
+ DeclFinder(const CppRefactoringFile *file, const Symbol *func)
+ : ASTVisitor(file->cppDocument()->translationUnit()), m_func(func) {}
+
+ SimpleDeclarationAST *decl() const { return m_decl; }
+
+ private:
+ bool visit(SimpleDeclarationAST *decl) override
+ {
+ if (m_decl)
+ return false;
+ if (decl->symbols && decl->symbols->value == m_func)
+ m_decl = decl;
+ return !m_decl;
+ }
+
+ const Symbol * const m_func;
+ SimpleDeclarationAST *m_decl = nullptr;
+ };
+
+ QHash<FilePath, ChangeSet> changeSets;
+ for (const MemberFunctionImplSetting &setting : std::as_const(settings)) {
+ DeclFinder finder(currentFile().data(), setting.func);
+ finder.accept(m_classAST);
+ QTC_ASSERT(finder.decl(), continue);
+ InsertionLocation loc;
+ const FilePath targetFilePath = setting.defPos == DefPosImplementationFile
+ ? cppFile : filePath();
+ QTC_ASSERT(!targetFilePath.isEmpty(), continue);
+ if (setting.defPos == DefPosInsideClass) {
+ int line, column;
+ currentFile()->lineAndColumn(currentFile()->endOf(finder.decl()), &line, &column);
+ loc = InsertionLocation(filePath(), QString(), QString(), line, column);
+ }
+ ChangeSet &changeSet = changeSets[targetFilePath];
+ InsertDefOperation::insertDefinition(
+ this, loc, setting.defPos, finder.decl()->declarator_list->value,
+ setting.func->asDeclaration(),targetFilePath, &changeSet);
+ }
+ for (auto it = changeSets.cbegin(); it != changeSets.cend(); ++it)
+ refactoring.cppFile(it.key())->apply(it.value());
+ }
+
+ ClassSpecifierAST *m_classAST = nullptr;
+ InsertDefsFromDeclsMode m_mode = InsertDefsFromDeclsMode::User;
+ QList<Symbol *> m_declarations;
+};
+
+class InsertDefFromDecl: public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+ void setOutside() { m_defPosOutsideClass = true; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const QList<AST *> &path = interface.path();
+
+ int idx = path.size() - 1;
+ for (; idx >= 0; --idx) {
+ AST *node = path.at(idx);
+ if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
+ if (idx > 0 && path.at(idx - 1)->asStatement())
+ return;
+ if (simpleDecl->symbols && !simpleDecl->symbols->next) {
+ if (Symbol *symbol = simpleDecl->symbols->value) {
+ if (Declaration *decl = symbol->asDeclaration()) {
+ if (Function *func = decl->type()->asFunctionType()) {
+ if (func->isSignal() || func->isPureVirtual() || func->isFriend())
+ return;
+
+ // Check if there is already a definition
+ SymbolFinder symbolFinder;
+ if (symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
+ true)) {
+ return;
+ }
+
+ // Insert Position: Implementation File
+ DeclaratorAST *declAST = simpleDecl->declarator_list->value;
+ InsertDefOperation *op = nullptr;
+ ProjectFile::Kind kind = ProjectFile::classify(interface.filePath().toString());
+ const bool isHeaderFile = ProjectFile::isHeader(kind);
+ if (isHeaderFile) {
+ CppRefactoringChanges refactoring(interface.snapshot());
+ InsertionPointLocator locator(refactoring);
+ // find appropriate implementation file, but do not use this
+ // location, because insertLocationForMethodDefinition() should
+ // be used in perform() to get consistent insert positions.
+ for (const InsertionLocation &location :
+ locator.methodDefinition(decl, false, {})) {
+ if (!location.isValid())
+ continue;
+
+ const FilePath filePath = location.filePath();
+ if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
+ const FilePath source = correspondingHeaderOrSource(filePath);
+ if (!source.isEmpty()) {
+ op = new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosImplementationFile,
+ source);
+ }
+ } else {
+ op = new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosImplementationFile,
+ filePath);
+ }
+
+ if (op)
+ result << op;
+ break;
+ }
+ }
+
+ // Determine if we are dealing with a free function
+ const bool isFreeFunction = func->enclosingClass() == nullptr;
+
+ // Insert Position: Outside Class
+ if (!isFreeFunction || m_defPosOutsideClass) {
+ result << new InsertDefOperation(interface, decl, declAST,
+ InsertionLocation(),
+ DefPosOutsideClass,
+ interface.filePath());
+ }
+
+ // Insert Position: Inside Class
+ // Determine insert location direct after the declaration.
+ int line, column;
+ const CppRefactoringFilePtr file = interface.currentFile();
+ file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
+ const InsertionLocation loc
+ = InsertionLocation(interface.filePath(), QString(),
+ QString(), line, column);
+ result << new InsertDefOperation(interface, decl, declAST, loc,
+ DefPosInsideClass, FilePath(),
+ isFreeFunction);
+
+ return;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ bool m_defPosOutsideClass = false;
+};
+
+//! Adds a definition for any number of member function declarations.
+class InsertDefsFromDecls : public CppQuickFixFactory
+{
+public:
+#ifdef WITH_TESTS
+ static QObject *createTest();
+#endif
+
+ void setMode(InsertDefsFromDeclsMode mode) { m_mode = mode; }
+
+private:
+ void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
+ {
+ const auto op = QSharedPointer<InsertDefsOperation>::create(interface);
+ op->setMode(m_mode);
+ if (op->isApplicable())
+ result << op;
+ }
+
+private:
+ InsertDefsFromDeclsMode m_mode = InsertDefsFromDeclsMode::User;
+};
+
+#ifdef WITH_TESTS
+using namespace Tests;
+
+static QList<TestDocumentPtr> singleHeader(const QByteArray &original, const QByteArray &expected)
+{
+ return {CppTestDocument::create("file.h", original, expected)};
+}
+
+class InsertDefFromDeclTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ /// Check if definition is inserted right after class for insert definition outside
+ void testAfterClass()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a@();\n"
+ "};\n"
+ "\n"
+ "class Bar {};\n";
+ expected =
+ "class Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a();\n"
+ "};\n"
+ "\n"
+ "inline void Foo::a()\n"
+ "{\n\n}\n"
+ "\n"
+ "class Bar {};\n";
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Check from header file: If there is a source file, insert the definition in the source file.
+ /// Case: Source file is empty.
+ void testHeaderSourceBasic1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If there is a source file, insert the definition in the source file.
+ /// Case: Source file is not empty.
+ void testHeaderSourceBasic2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f(const std::vector<int> &v)@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "int x;\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "int x;\n"
+ "\n"
+ "void f(const std::vector<int> &v)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from source file: Insert in source file, not header file.
+ void testHeaderSourceBasic3()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Empty Header File
+ testDocuments << CppTestDocument::create("file.h", "", "");
+
+ // Source File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n";
+ expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If the class is in a namespace, the added function definition
+ /// name must be qualified accordingly.
+ void testHeaderSourceNamespace1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace N {\n"
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original.resize(0);
+ expected =
+ "\n"
+ "N::Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check from header file: If the class is in namespace N and the source file has a
+ /// "using namespace N" line, the function definition name must be qualified accordingly.
+ void testHeaderSourceNamespace2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "namespace N {\n"
+ "struct Foo\n"
+ "{\n"
+ " Foo()@;\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "using namespace N;\n"
+ ;
+ expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check definition insert inside class
+ void testInsideClass()
+ {
+ const QByteArray original =
+ "class Foo {\n"
+ " void b@ar();\n"
+ "};";
+ const QByteArray expected =
+ "class Foo {\n"
+ " void bar()\n"
+ " {\n\n"
+ " }\n"
+ "};";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory, ProjectExplorer::HeaderPaths(),
+ 1);
+ }
+
+ /// Check not triggering when definition exists
+ void testNotTriggeringWhenDefinitionExists()
+ {
+ const QByteArray original =
+ "class Foo {\n"
+ " void b@ar();\n"
+ "};\n"
+ "void Foo::bar() {}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, ""), &factory, ProjectExplorer::HeaderPaths(), 1);
+ }
+
+ /// Find right implementation file.
+ void testFindRightImplementationFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "struct Foo\n"
+ "{\n"
+ " Foo();\n"
+ " void a();\n"
+ " void b@();\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File #1
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+
+ // Source File #2
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n";
+ expected = original +
+ "\n"
+ "void Foo::b()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file2.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Ignore generated functions declarations when looking at the surrounding
+ /// functions declarations in order to find the right implementation file.
+ void testIgnoreSurroundingGeneratedDeclarations()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "#define DECLARE_HIDDEN_FUNCTION void hidden();\n"
+ "struct Foo\n"
+ "{\n"
+ " void a();\n"
+ " DECLARE_HIDDEN_FUNCTION\n"
+ " void b@();\n"
+ "};\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File #1
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n";
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::a()\n"
+ "{\n\n"
+ "}\n"
+ "\n"
+ "void Foo::b()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ // Source File #2
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::hidden()\n"
+ "{\n\n"
+ "}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file2.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames1()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor =();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator =();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator =()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check if whitespace is respected for operator functions
+ void testRespectWsInOperatorNames2()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " Foo &opera@tor=();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " Foo &operator=();\n"
+ "};\n"
+ "\n"
+ "Foo &Foo::operator=()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check that the noexcept exception specifier is transferred
+ void testNoexceptSpecifier()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " void @foo() noexcept(false);\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " void foo() noexcept(false);\n"
+ "};\n"
+ "\n"
+ "void Foo::foo() noexcept(false)\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ /// Check if a function like macro use is not separated by the function to insert
+ /// Case: Macro preceded by preproceesor directives and declaration.
+ void testMacroUsesAtEndOfFile1()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "int lala;\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "int lala;\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if a function like macro use is not separated by the function to insert
+ /// Case: Marco preceded only by preprocessor directives.
+ void testMacroUsesAtEndOfFile2()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "#define MACRO(X) X x;\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MACRO(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check if insertion happens before syntactically erroneous statements at end of file.
+ void testErroneousStatementAtEndOfFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f()@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ "\n"
+ "MissingSemicolon(int)\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "\n"
+ "\n"
+ "void f()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "MissingSemicolon(int)\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Check: Respect rvalue references
+ void testRvalueReference()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = "void f(Foo &&)@;\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = "";
+ expected =
+ "\n"
+ "void f(Foo &&)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testFunctionTryBlock()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+struct Foo {
+ void tryCatchFunc();
+ void @otherFunc();
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+
+void Foo::tryCatchFunc() try {} catch (...) {}
+)";
+ expected = R"(
+#include "file.h"
+
+void Foo::tryCatchFunc() try {} catch (...) {}
+
+void Foo::otherFunc()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testUsingDecl()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+namespace N { struct S; }
+using N::S;
+
+void @func(const S &s);
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+void func(const S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ original = R"(
+namespace N1 {
+namespace N2 { struct S; }
+using N2::S;
+}
+
+void @func(const N1::S &s);
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+void func(const N1::S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+
+ // No using declarations here, but the code model has one. No idea why.
+ testDocuments.clear();
+ original = R"(
+class B {};
+class D : public B {
+ @D();
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+D::D()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ original = R"(
+namespace ns1 { template<typename T> class span {}; }
+
+namespace ns {
+using ns1::span;
+class foo
+{
+ void @bar(ns::span<int>);
+};
+}
+)";
+ expected = R"(
+namespace ns1 { template<typename T> class span {}; }
+
+namespace ns {
+using ns1::span;
+class foo
+{
+ void bar(ns::span<int>);
+};
+
+void foo::bar(ns::span<int>)
+{
+
+}
+
+}
+)";
+ // TODO: Unneeded namespace gets inserted in RewriteName::visit(const QualifiedNameId *)
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ /// Find right implementation file. (QTCREATORBUG-10728)
+ void testFindImplementationFile()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original =
+ "class Foo {\n"
+ " void bar();\n"
+ " void ba@z();\n"
+ "};\n"
+ "\n"
+ "void Foo::bar()\n"
+ "{}\n";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original =
+ "#include \"file.h\"\n"
+ ;
+ expected =
+ "#include \"file.h\"\n"
+ "\n"
+ "void Foo::baz()\n"
+ "{\n"
+ "\n"
+ "}\n"
+ ;
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testUnicodeIdentifier()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+//
+// The following "non-latin1" code points are used in the tests:
+//
+// U+00FC - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
+// U+4E8C - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
+// U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
+//
+
+#define UNICODE_U00FC "\xc3\xbc"
+#define UNICODE_U4E8C "\xe4\xba\x8c"
+#define UNICODE_U10302 "\xf0\x90\x8c\x82"
+#define TEST_UNICODE_IDENTIFIER UNICODE_U00FC UNICODE_U4E8C UNICODE_U10302
+
+ original =
+ "class Foo {\n"
+ " void @" TEST_UNICODE_IDENTIFIER "();\n"
+ "};\n";
+ ;
+ expected = original;
+ expected +=
+ "\n"
+ "void Foo::" TEST_UNICODE_IDENTIFIER "()\n"
+ "{\n"
+ "\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+#undef UNICODE_U00FC
+#undef UNICODE_U4E8C
+#undef UNICODE_U10302
+#undef TEST_UNICODE_IDENTIFIER
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testTemplateClass()
+ {
+ QByteArray original =
+ "template<class T>\n"
+ "class Foo\n"
+ "{\n"
+ " void fun@c1();\n"
+ " void func2();\n"
+ "};\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func2() {}\n";
+ QByteArray expected =
+ "template<class T>\n"
+ "class Foo\n"
+ "{\n"
+ " void func1();\n"
+ " void func2();\n"
+ "};\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func1()\n"
+ "{\n"
+ "\n"
+ "}\n\n"
+ "template<class T>\n"
+ "void Foo<T>::func2() {}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassWithValueParam()
+ {
+ QList<TestDocumentPtr> testDocuments;
+ QByteArray original =
+ "template<typename T, int size> struct MyArray {};\n"
+ "MyArray<int, 1> @foo();";
+ QByteArray expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ original = "#include \"file.h\"\n";
+ expected =
+ "#include \"file.h\"\n\n"
+ "MyArray<int, 1> foo()\n"
+ "{\n\n"
+ "}\n";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testTemplateFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " template<class T>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassAndTemplateFunction()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "T Foo<T>::func(U u)\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testTemplateClassAndFunctionInsideNamespace()
+ {
+ QByteArray original =
+ "namespace N {\n"
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "}\n";
+ QByteArray expected =
+ "namespace N {\n"
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " T fun@c(U u);\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "T Foo<T>::func(U u)\n"
+ "{\n"
+ "\n"
+ "}\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testFunctionWithSignedUnsignedArgument()
+ {
+ QByteArray original;
+ QByteArray expected;
+ InsertDefFromDecl factory;
+
+ original =R"--(
+class myclass
+{
+ myc@lass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+
+myclass::myclass(QVector<signed int> g)
+{
+
+}
+)--";
+
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myc@lass(QVector<unsigned> g);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ myclass(QVector<signed> g);
+ myclass(QVector<unsigned> g);
+}
+
+myclass::myclass(QVector<unsigned int> g)
+{
+
+}
+)--";
+
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ unsigned f@oo(unsigned);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ unsigned foo(unsigned);
+}
+
+unsigned int myclass::foo(unsigned int)
+{
+
+}
+)--";
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+
+ original =R"--(
+class myclass
+{
+ signed f@oo(signed);
+}
+)--";
+ expected =R"--(
+class myclass
+{
+ signed foo(signed);
+}
+
+signed int myclass::foo(signed int)
+{
+
+}
+)--";
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testNotTriggeredForFriendFunc()
+ {
+ const QByteArray contents =
+ "class Foo\n"
+ "{\n"
+ " friend void f@unc();\n"
+ "};\n"
+ "\n";
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(contents, ""), &factory);
+ }
+
+ void testMinimalFunctionParameterType()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+class C {
+ typedef int A;
+ A @foo(A);
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+C::A C::foo(A)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+
+ testDocuments.clear();
+ // Header File
+ original = R"(
+namespace N {
+ struct S;
+ S @foo(const S &s);
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+N::S N::foo(const S &s)
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testAliasTemplateAsReturnType()
+ {
+ QList<TestDocumentPtr> testDocuments;
+
+ QByteArray original;
+ QByteArray expected;
+
+ // Header File
+ original = R"(
+struct foo {
+ struct foo2 {
+ template <typename T> using MyType = T;
+ MyType<int> @bar();
+ };
+};
+)";
+ expected = original;
+ testDocuments << CppTestDocument::create("file.h", original, expected);
+
+ // Source File
+ original = R"(
+#include "file.h"
+)";
+ expected = R"(
+#include "file.h"
+
+foo::foo2::MyType<int> foo::foo2::bar()
+{
+
+}
+)";
+ testDocuments << CppTestDocument::create("file.cpp", original, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArray>("original");
+ QTest::addColumn<QByteArray>("expected");
+
+ // Check from source file: If there is no header file, insert the definition after the class.
+ QByteArray original =
+ "struct Foo\n"
+ "{\n"
+ " Foo();@\n"
+ "};\n";
+ QByteArray expected = original +
+ "\n"
+ "Foo::Foo()\n"
+ "{\n\n"
+ "}\n";
+ QTest::newRow("basic") << original << expected;
+
+ original = "void free()@;\n";
+ expected = "void free()\n{\n\n}\n";
+ QTest::newRow("freeFunction") << original << expected;
+
+ original = "class Foo {\n"
+ "public:\n"
+ " Foo() {}\n"
+ "};\n"
+ "void freeFunc() {\n"
+ " Foo @f();"
+ "}\n";
+
+ // Check not triggering when it is a statement
+ QTest::newRow("notTriggeringStatement") << original << QByteArray();
+ }
+
+ void test()
+ {
+ QFETCH(QByteArray, original);
+ QFETCH(QByteArray, expected);
+
+ InsertDefFromDecl factory;
+ QuickFixOperationTest(singleDocument(original, expected), &factory);
+ }
+
+ void testOutsideTemplateClassAndTemplateFunction()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "template<class U>\n"
+ "inline void Foo<T>::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideTemplateClass()
+ {
+ QByteArray original =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "template<class T>"
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class T>\n"
+ "inline void Foo<T>::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideTemplateFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " template<class U>\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "template<class U>\n"
+ "inline void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+ }
+
+ void testOutsideFunction()
+ {
+ QByteArray original =
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n";
+ QByteArray expected =
+ "class Foo\n"
+ "{\n"
+ " void fun@c();\n"
+ "};\n"
+ "\n"
+ "inline void Foo::func()\n"
+ "{\n"
+ "\n"
+ "}\n";
+
+ InsertDefFromDecl factory;
+ factory.setOutside();
+ QuickFixOperationTest(singleHeader(original, expected), &factory);
+
+ }
+
+};
+
+class InsertDefsFromDeclsTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+
+ void test_data()
+ {
+ QTest::addColumn<QByteArrayList>("headers");
+ QTest::addColumn<QByteArrayList>("sources");
+ QTest::addColumn<int>("mode");
+
+ QByteArray origHeader = R"(
+namespace N {
+class @C
+{
+public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+ void funcNotSelected();
+ void funcInline();
+ void funcBelow();
+ void funcCppFile();
+
+signals:
+ void ignoredSignal();
+};
+
+inline void C::ignoredImplemented2() {}
+
+} // namespace N)";
+ QByteArray origSource = R"(
+#include "file.h"
+
+namespace N {
+
+void C::ignoredImplemented3() {}
+
+} // namespace N)";
+
+ QByteArray expectedHeader = R"(
+namespace N {
+class C
+{
+public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+ void funcNotSelected();
+ void funcInline()
+ {
+
+ }
+ void funcBelow();
+ void funcCppFile();
+
+signals:
+ void ignoredSignal();
+};
+
+inline void C::ignoredImplemented2() {}
+
+inline void C::funcBelow()
+{
+
+}
+
+} // namespace N)";
+ QByteArray expectedSource = R"(
+#include "file.h"
+
+namespace N {
+
+void C::ignoredImplemented3() {}
+
+void C::funcCppFile()
+{
+
+}
+
+} // namespace N)";
+ QTest::addRow("normal case")
+ << QByteArrayList{origHeader, expectedHeader}
+ << QByteArrayList{origSource, expectedSource}
+ << int(InsertDefsFromDeclsMode::Alternating);
+ QTest::addRow("aborted dialog")
+ << QByteArrayList{origHeader, origHeader}
+ << QByteArrayList{origSource, origSource}
+ << int(InsertDefsFromDeclsMode::Off);
+
+ origHeader = R"(
+ namespace N {
+ class @C
+ {
+ public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+ void ignoredImplemented2(); // Below
+ void ignoredImplemented3(); // In cpp file
+
+ signals:
+ void ignoredSignal();
+ };
+
+ inline void C::ignoredImplemented2() {}
+
+ } // namespace N)";
+ QTest::addRow("no candidates")
+ << QByteArrayList{origHeader, origHeader}
+ << QByteArrayList{origSource, origSource}
+ << int(InsertDefsFromDeclsMode::Alternating);
+
+ origHeader = R"(
+ namespace N {
+ class @C
+ {
+ public:
+ friend void ignoredFriend();
+ void ignoredImplemented() {};
+
+ signals:
+ void ignoredSignal();
+ };
+ } // namespace N)";
+ QTest::addRow("no member functions")
+ << QByteArrayList{origHeader, ""}
+ << QByteArrayList{origSource, ""}
+ << int(InsertDefsFromDeclsMode::Alternating);
+
+
+ }
+
+ void test()
+ {
+ QFETCH(QByteArrayList, headers);
+ QFETCH(QByteArrayList, sources);
+ QFETCH(int, mode);
+
+ QList<TestDocumentPtr> testDocuments(
+ {CppTestDocument::create("file.h", headers.at(0), headers.at(1)),
+ CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))});
+ InsertDefsFromDecls factory;
+ factory.setMode(static_cast<InsertDefsFromDeclsMode>(mode));
+ QuickFixOperationTest(testDocuments, &factory);
+ }
+
+ void testInsertAndFormat()
+ {
+ if (!isClangFormatPresent())
+ QSKIP("This test reqires ClangFormat");
+
+ const QByteArray origHeader = R"(
+class @C
+{
+public:
+ void func1 (int const &i);
+ void func2 (double const d);
+};
+)";
+ const QByteArray origSource = R"(
+#include "file.h"
+)";
+
+ const QByteArray expectedSource = R"(
+#include "file.h"
+
+void C::func1 (int const &i)
+{
+
+}
+
+void C::func2 (double const d)
+{
+
+}
+)";
+
+ const QByteArray clangFormatSettings = R"(
+BreakBeforeBraces: Allman
+QualifierAlignment: Right
+SpaceBeforeParens: Always
+)";
+
+ const QList<TestDocumentPtr> testDocuments({
+ CppTestDocument::create("file.h", origHeader, origHeader),
+ CppTestDocument::create("file.cpp", origSource, expectedSource)});
+ InsertDefsFromDecls factory;
+ factory.setMode(InsertDefsFromDeclsMode::Impl);
+ CppCodeStylePreferences * const prefs = CppToolsSettings::cppCodeStyle();
+ const CppCodeStyleSettings settings = prefs->codeStyleSettings();
+ CppCodeStyleSettings tempSettings = settings;
+ tempSettings.forceFormatting = true;
+ prefs->setCodeStyleSettings(tempSettings);
+ QuickFixOperationTest(testDocuments, &factory, {}, {}, {}, clangFormatSettings);
+ prefs->setCodeStyleSettings(settings);
+ }
+};
+
+QObject *InsertDefFromDecl::createTest()
+{
+ return new InsertDefFromDeclTest;
+}
+
+QObject *InsertDefsFromDecls::createTest()
+{
+ return new InsertDefsFromDeclsTest;
+}
+
+#endif // WITH_TESTS
+} // namespace
+
+void registerInsertFunctionDefinitionQuickfixes()
+{
+ CppQuickFixFactory::registerFactory<InsertDefFromDecl>();
+ CppQuickFixFactory::registerFactory<InsertDefsFromDecls>();
+}
+
+} // namespace CppEditor::Internal
+
+#ifdef WITH_TESTS
+#include <insertfunctiondefinition.moc>
+#endif