From c25f0585d1f063e82e5eafdda40ab6a87bf68443 Mon Sep 17 00:00:00 2001
From: Leandro Melo <leandro.melo@nokia.com>
Date: Wed, 21 Dec 2011 18:24:14 +0100
Subject: [PATCH] C++: Extract function quickfix

Task-number: QTCREATORBUG-5485

Change-Id: Ib6aaeaadad4b421480d22399392768d4bad85bda
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
---
 src/plugins/cppeditor/cppinsertdecldef.cpp | 612 +++++++++++++++++++--
 src/plugins/cppeditor/cppinsertdecldef.h   |  18 +-
 src/plugins/cppeditor/cppquickfixes.cpp    |   2 +
 src/plugins/cpptools/cpptoolsreuse.cpp     |  16 +
 src/plugins/cpptools/cpptoolsreuse.h       |   2 +
 5 files changed, 589 insertions(+), 61 deletions(-)

diff --git a/src/plugins/cppeditor/cppinsertdecldef.cpp b/src/plugins/cppeditor/cppinsertdecldef.cpp
index 72bda81f481..5d1733274e4 100644
--- a/src/plugins/cppeditor/cppinsertdecldef.cpp
+++ b/src/plugins/cppeditor/cppinsertdecldef.cpp
@@ -38,11 +38,21 @@
 #include <cplusplus/CppRewriter.h>
 #include <cplusplus/LookupContext.h>
 #include <cplusplus/Overview.h>
+#include <cplusplus/ASTVisitor.h>
 #include <cpptools/insertionpointlocator.h>
 #include <cpptools/cpprefactoringchanges.h>
+#include <cpptools/cpptoolsreuse.h>
+
+#include <utils/qtcassert.h>
 
 #include <QtCore/QCoreApplication>
 #include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QStringBuilder>
+#include <QtGui/QTextDocument>
+#include <QtGui/QTextBlock>
+#include <QtGui/QInputDialog>
+#include <QtGui/QMessageBox>
 
 using namespace CPlusPlus;
 using namespace CppEditor;
@@ -99,6 +109,8 @@ public:
         targetFile->apply();
     }
 
+    static QString generateDeclaration(Function *function);
+
 private:
     QString m_targetFileName;
     const Class *m_targetSymbol;
@@ -106,6 +118,36 @@ private:
     QString m_decl;
 };
 
+Class *isMemberFunction(const LookupContext &context, Function *function)
+{
+    Q_ASSERT(function);
+
+    Scope *enclosingScope = function->enclosingScope();
+    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
+        enclosingScope = enclosingScope->enclosingScope();
+    Q_ASSERT(enclosingScope != 0);
+
+    const Name *functionName = function->name();
+    if (! functionName)
+        return 0; // anonymous function names are not valid c++
+
+    if (! functionName->isQualifiedNameId())
+        return 0; // trying to add a declaration for a global function
+
+    const QualifiedNameId *q = functionName->asQualifiedNameId();
+    if (!q->base())
+        return 0;
+
+    if (ClassOrNamespace *binding = context.lookupType(q->base(), enclosingScope)) {
+        foreach (Symbol *s, binding->symbols()) {
+            if (Class *matchingClass = s->asClass())
+                return matchingClass;
+        }
+    }
+
+    return 0;
+}
+
 } // anonymous namespace
 
 QList<CppQuickFixOperation::Ptr> DeclFromDef::match(
@@ -141,70 +183,39 @@ QList<CppQuickFixOperation::Ptr> DeclFromDef::match(
     if (!funDef || !funDef->symbol)
         return noResult();
 
-    Function *method = funDef->symbol;
-
-    Scope *enclosingScope = method->enclosingScope();
-    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
-        enclosingScope = enclosingScope->enclosingScope();
-    Q_ASSERT(enclosingScope != 0);
-
-    const Name *functionName = method->name();
-    if (! functionName)
-        return noResult(); // warn, anonymous function names are not valid c++
-
-    if (! functionName->isQualifiedNameId())
-        return noResult(); // warn, trying to add a declaration for a global function
-
-    const QualifiedNameId *q = functionName->asQualifiedNameId();
-    if (!q->base())
-        return noResult();
-
-    if (ClassOrNamespace *binding = interface->context().lookupType(q->base(), enclosingScope)) {
-        foreach (Symbol *s, binding->symbols()) {
-            if (Class *matchingClass = s->asClass()) {
-                for (Symbol *s = matchingClass->find(q->identifier()); s; s = s->next()) {
-                    if (! s->name())
-                        continue;
-                    else if (! q->identifier()->isEqualTo(s->identifier()))
-                        continue;
-                    else if (! s->type()->isFunctionType())
-                        continue;
-
-                    if (s->type().isEqualTo(method->type()))
-                        return noResult();
-                }
-
-                // a good candidate
-
-                const QString fn = QString::fromUtf8(matchingClass->fileName(),
-                                                     matchingClass->fileNameLength());
-                const QString decl = generateDeclaration(interface,
-                                                         method,
-                                                         binding);
-                return singleResult(
-                            new InsertDeclOperation(interface, fn, matchingClass,
-                                                    InsertionPointLocator::Public,
-                                                    decl));
+    Function *fun = funDef->symbol;
+    if (Class *matchingClass = isMemberFunction(interface->context(), fun)) {
+        const QualifiedNameId *qName = fun->name()->asQualifiedNameId();
+        for (Symbol *s = matchingClass->find(qName->base()->identifier()); s; s = s->next()) {
+            if (!s->name()
+                    || !qName->identifier()->isEqualTo(s->identifier())
+                    || !s->type()->isFunctionType())
+                continue;
+
+            if (s->type().isEqualTo(fun->type())) {
+                // Declaration exists.
+                return noResult();
             }
         }
+        QString fileName = QString::fromUtf8(matchingClass->fileName(),
+                                             matchingClass->fileNameLength());
+        const QString decl = InsertDeclOperation::generateDeclaration(fun);
+        return singleResult(new InsertDeclOperation(interface, fileName, matchingClass,
+                                                    InsertionPointLocator::Public, decl));
     }
 
     return noResult();
 }
 
-QString DeclFromDef::generateDeclaration(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &,
-                                         Function *method,
-                                         ClassOrNamespace *targetBinding)
+QString InsertDeclOperation::generateDeclaration(Function *function)
 {
-    Q_UNUSED(targetBinding);
-
     Overview oo;
     oo.setShowFunctionSignatures(true);
     oo.setShowReturnTypes(true);
     oo.setShowArgumentNames(true);
 
     QString decl;
-    decl += oo(method->type(), method->unqualifiedName());
+    decl += oo(function->type(), function->unqualifiedName());
     decl += QLatin1String(";\n");
 
     return decl;
@@ -212,6 +223,9 @@ QString DeclFromDef::generateDeclaration(const QSharedPointer<const Internal::Cp
 
 namespace {
 
+
+
+
 class InsertDefOperation: public CppQuickFixOperation
 {
 public:
@@ -317,3 +331,501 @@ QList<CppQuickFixOperation::Ptr> DefFromDecl::match(
 
     return noResult();
 }
+
+namespace {
+
+class ExtractFunctionOperation : public CppQuickFixOperation
+{
+public:
+    ExtractFunctionOperation(const QSharedPointer<const CppQuickFixAssistInterface> &interface,
+                    int extractionStart,
+                    int extractionEnd,
+                    FunctionDefinitionAST *refFuncDef,
+                    Symbol *funcReturn,
+                    QList<QPair<QString, QString> > relevantDecls)
+        : CppQuickFixOperation(interface)
+        , m_extractionStart(extractionStart)
+        , m_extractionEnd(extractionEnd)
+        , m_refFuncDef(refFuncDef)
+        , m_funcReturn(funcReturn)
+        , m_relevantDecls(relevantDecls)
+    {
+        setDescription(QCoreApplication::translate("QuickFix::ExtractFunction", "Extract Function"));
+    }
+
+    void performChanges(const CppTools::CppRefactoringFilePtr &currentFile,
+                        const CppTools::CppRefactoringChanges &refactoring)
+    {
+        QTC_ASSERT(!m_funcReturn || !m_relevantDecls.isEmpty(), return);
+
+        const QString &funcName = getFunctionName();
+        if (funcName.isEmpty())
+            return;
+
+        Function *refFunc = m_refFuncDef->symbol;
+
+        // We don't need to rewrite the type for declarations made inside the reference function,
+        // since their scope will remain the same. Then we preserve the original spelling style.
+        // However, we must do so for the return type in the definition.
+        SubstitutionEnvironment env;
+        env.setContext(assistInterface()->context());
+        env.switchScope(refFunc);
+        ClassOrNamespace *targetCoN =
+                assistInterface()->context().lookupType(refFunc->enclosingScope());
+        if (!targetCoN)
+            targetCoN = assistInterface()->context().globalNamespace();
+        UseMinimalNames subs(targetCoN);
+        env.enter(&subs);
+
+        Overview printer;
+        Control *control = assistInterface()->context().control().data();
+        QString funcDef;
+        QString funcDecl; // We generate a declaration only in the case of a member function.
+        QString funcCall;
+
+        Class *matchingClass = isMemberFunction(assistInterface()->context(), refFunc);
+
+        // Write return type.
+        if (!m_funcReturn) {
+            funcDef.append(QLatin1String("void "));
+            if (matchingClass)
+                funcDecl.append(QLatin1String("void "));
+        } else {
+            const FullySpecifiedType &fullType = rewriteType(m_funcReturn->type(), &env, control);
+            funcDef.append(printer.prettyType(fullType) + QLatin1Char(' '));
+            funcDecl.append(printer.prettyType(m_funcReturn->type()) + QLatin1Char(' '));
+        }
+
+        // Write class qualification, if any.
+        if (matchingClass) {
+            const Name *name = rewriteName(matchingClass->name(), &env, control);
+            funcDef.append(printer.prettyName(name));
+            funcDef.append(QLatin1String("::"));
+        }
+
+        // Write the extracted function itself and its call.
+        funcDef.append(funcName);
+        if (matchingClass)
+            funcDecl.append(funcName);
+        funcCall.append(funcName);
+        funcDef.append(QLatin1Char('('));
+        if (matchingClass)
+            funcDecl.append(QLatin1Char('('));
+        funcCall.append(QLatin1Char('('));
+        for (int i = m_funcReturn ? 1 : 0; i < m_relevantDecls.length(); ++i) {
+            QPair<QString, QString> p = m_relevantDecls.at(i);
+            funcCall.append(p.first);
+            funcDef.append(p.second);
+            if (matchingClass)
+                funcDecl.append(p.second);
+            if (i < m_relevantDecls.length() - 1) {
+                funcCall.append(QLatin1String(", "));
+                funcDef.append(QLatin1String(", "));
+                if (matchingClass)
+                    funcDecl.append(QLatin1String(", "));
+            }
+        }
+        funcDef.append(QLatin1Char(')'));
+        if (matchingClass)
+            funcDecl.append(QLatin1Char(')'));
+        funcCall.append(QLatin1Char(')'));
+        if (refFunc->isConst()) {
+            funcDef.append(QLatin1String(" const"));
+            funcDecl.append(QLatin1String(" const"));
+        }
+        funcDef.append(QLatin1String("\n{\n")
+                       % currentFile->textOf(m_extractionStart, m_extractionEnd)
+                       % QLatin1Char('\n'));
+        if (matchingClass)
+            funcDecl.append(QLatin1String(";\n"));
+        if (m_funcReturn) {
+            funcDef.append(QLatin1String("return ")
+                        % m_relevantDecls.at(0).first
+                        % QLatin1String(";\n"));
+            funcCall.prepend(m_relevantDecls.at(0).second % QLatin1String(" = "));
+        }
+        funcDef.append(QLatin1String("}\n\n"));
+        funcDef.replace(QChar::ParagraphSeparator, QLatin1String("\n"));
+        funcCall.append(QLatin1Char(';'));
+
+        Utils::ChangeSet change;
+        int position = currentFile->startOf(m_refFuncDef);
+        change.insert(position, funcDef);
+        change.replace(m_extractionStart, m_extractionEnd, funcCall);
+        currentFile->setChangeSet(change);
+        currentFile->appendIndentRange(Utils::ChangeSet::Range(position, position + funcDef.length()));
+        currentFile->apply();
+
+        // Write declaration, if necessary.
+        if (matchingClass) {
+            InsertionPointLocator locator(refactoring);
+            const InsertionLocation &location =
+                    locator.methodDeclarationInClass(matchingClass->fileName(),
+                                                     matchingClass,
+                                                     InsertionPointLocator::Public);
+            CppTools::CppRefactoringFilePtr declFile = refactoring.file(matchingClass->fileName());
+            change.clear();
+            position = declFile->position(location.line(), location.column());
+            change.insert(position, funcDecl);
+            declFile->setChangeSet(change);
+            declFile->appendIndentRange(Utils::ChangeSet::Range(position,
+                                                                position + funcDecl.length()));
+            declFile->apply();
+        }
+    }
+
+    QString getFunctionName() const
+    {
+        bool ok;
+        QString name =
+                QInputDialog::getText(0,
+                                      QCoreApplication::translate("QuickFix::ExtractFunction",
+                                                                  "Extract Function Refactoring"),
+                                      QCoreApplication::translate("QuickFix::ExtractFunction",
+                                                                  "Enter function name"),
+                                      QLineEdit::Normal,
+                                      QString(),
+                                      &ok);
+        name = name.trimmed();
+        if (!ok || name.isEmpty())
+            return QString();
+
+        if (!isValidIdentifier(name)) {
+            QMessageBox::critical(0,
+                                  QCoreApplication::translate("QuickFix::ExtractFunction",
+                                                              "Extract Function Refactoring"),
+                                  QCoreApplication::translate("QuickFix::ExtractFunction",
+                                                              "Invalid function name"));
+            return QString();
+        }
+
+        return name;
+    }
+
+    int m_extractionStart;
+    int m_extractionEnd;
+    FunctionDefinitionAST *m_refFuncDef;
+    Symbol *m_funcReturn;
+    QList<QPair<QString, QString> > m_relevantDecls;
+};
+
+QPair<QString, QString> assembleDeclarationData(const QString &specifiers,
+                                                DeclaratorAST *decltr,
+                                                const CppRefactoringFilePtr &file,
+                                                const Overview &printer)
+{
+    if (decltr->core_declarator
+            && decltr->core_declarator->asDeclaratorId()
+            && decltr->core_declarator->asDeclaratorId()->name) {
+        QString decltrText = file->textOf(file->startOf(decltr),
+                                          file->endOf(decltr->core_declarator));
+        if (!decltrText.isEmpty()) {
+            const QString &name = printer.prettyName(
+                    decltr->core_declarator->asDeclaratorId()->name->name);
+            QString completeDecl = specifiers;
+            if (!decltrText.contains(QLatin1Char(' ')))
+                completeDecl.append(QLatin1Char(' ') + decltrText);
+            else
+                completeDecl.append(decltrText);
+            return qMakePair(name, completeDecl);
+        }
+    }
+    return QPair<QString, QString>();
+}
+
+
+class FunctionExtractionAnalyser : public ASTVisitor
+{
+public:
+    FunctionExtractionAnalyser(TranslationUnit *unit,
+                               const int selStart,
+                               const int selEnd,
+                               const CppRefactoringFilePtr &file,
+                               const Overview &printer)
+        : ASTVisitor(unit)
+        , m_done(false)
+        , m_failed(false)
+        , m_selStart(selStart)
+        , m_selEnd(selEnd)
+        , m_extractionStart(0)
+        , m_extractionEnd(0)
+        , m_file(file)
+        , m_printer(printer)
+    {}
+
+    bool operator()(FunctionDefinitionAST *refFunDef)
+    {
+        accept(refFunDef);
+
+        if (!m_failed && m_extractionStart == m_extractionEnd)
+            m_failed = true;
+
+        return !m_failed;
+    }
+
+    bool preVisit(AST *)
+    {
+        if (m_done)
+            return false;
+        return true;
+    }
+
+    void statement(StatementAST *stmt)
+    {
+        if (!stmt)
+            return;
+
+        const int stmtStart = m_file->startOf(stmt);
+        const int stmtEnd = m_file->endOf(stmt);
+
+        if (stmtStart >= m_selEnd
+                || m_extractionStart && stmtEnd > m_selEnd) {
+            m_done = true;
+            return;
+        }
+
+        if (stmtStart >= m_selStart && !m_extractionStart)
+            m_extractionStart = stmtStart;
+        if (stmtEnd > m_extractionEnd && m_extractionStart)
+            m_extractionEnd = stmtEnd;
+
+        accept(stmt);
+    }
+
+    bool visit(CaseStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        return false;
+    }
+
+    bool visit(CompoundStatementAST *stmt)
+    {
+        for (StatementListAST *it = stmt->statement_list; it; it = it->next) {
+            statement(it->value);
+            if (m_done)
+                break;
+        }
+        return false;
+    }
+
+    bool visit(DoStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        return false;
+    }
+
+    bool visit(ForeachStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        return false;
+    }
+
+    bool visit(ForStatementAST *stmt)
+    {
+        statement(stmt->initializer);
+        if (!m_done)
+            statement(stmt->statement);
+        return false;
+    }
+
+    bool visit(IfStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        if (!m_done)
+            statement(stmt->else_statement);
+        return false;
+    }
+
+    bool visit(TryBlockStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        for (CatchClauseListAST *it = stmt->catch_clause_list; it; it = it->next) {
+            statement(it->value);
+            if (m_done)
+                break;
+        }
+        return false;
+    }
+
+    bool visit(WhileStatementAST *stmt)
+    {
+        statement(stmt->statement);
+        return false;
+    }
+
+    bool visit(DeclarationStatementAST *declStmt)
+    {
+        // We need to collect the declarations we see before the extraction or even inside it.
+        // They might need to be used as either a parameter or return value. Actually, we could
+        // still obtain their types from the local uses, but it's good to preserve the original
+        // typing style.
+        if (declStmt
+                && declStmt->declaration
+                && declStmt->declaration->asSimpleDeclaration()) {
+            SimpleDeclarationAST *simpleDecl = declStmt->declaration->asSimpleDeclaration();
+            if (simpleDecl->decl_specifier_list
+                    && simpleDecl->declarator_list) {
+                const QString &specifiers =
+                        m_file->textOf(m_file->startOf(simpleDecl),
+                                     m_file->endOf(simpleDecl->decl_specifier_list->lastValue()));
+                for (DeclaratorListAST *decltrList = simpleDecl->declarator_list;
+                     decltrList;
+                     decltrList = decltrList->next) {
+                    const QPair<QString, QString> p =
+                        assembleDeclarationData(specifiers, decltrList->value, m_file, m_printer);
+                    if (!p.first.isEmpty())
+                        m_knownDecls.insert(p.first, p.second);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    bool visit(ReturnStatementAST *)
+    {
+        if (m_extractionStart) {
+            m_done = true;
+            m_failed = true;
+        }
+
+        return false;
+    }
+
+    bool m_done;
+    bool m_failed;
+    const int m_selStart;
+    const int m_selEnd;
+    int m_extractionStart;
+    int m_extractionEnd;
+    QHash<QString, QString> m_knownDecls;
+    CppRefactoringFilePtr m_file;
+    const Overview &m_printer;
+};
+
+} // anonymous namespace
+
+QList<CppQuickFixOperation::Ptr> ExtractFunction::match(
+        const QSharedPointer<const CppQuickFixAssistInterface> &interface)
+{
+    CppRefactoringFilePtr file = interface->currentFile();
+
+    QTextCursor cursor = file->cursor();
+    if (!cursor.hasSelection())
+        return noResult();
+
+    const QList<AST *> &path = interface->path();
+    FunctionDefinitionAST *refFuncDef = 0; // The "reference" function, which we will extract from.
+    for (int i = path.size() - 1; i >= 0; --i) {
+        refFuncDef = path.at(i)->asFunctionDefinition();
+        if (refFuncDef)
+            break;
+    }
+
+    if (!refFuncDef
+            || !refFuncDef->function_body
+            || !refFuncDef->function_body->asCompoundStatement()
+            || !refFuncDef->function_body->asCompoundStatement()->statement_list
+            || !refFuncDef->symbol
+            || !refFuncDef->symbol->name()
+            || refFuncDef->symbol->enclosingScope()->isTemplate() /* TODO: Templates... */) {
+        return noResult();
+    }
+
+    // Adjust selection ends.
+    int selStart = cursor.selectionStart();
+    int selEnd = cursor.selectionEnd();
+    if (selStart > selEnd)
+        qSwap(selStart, selEnd);
+
+    Overview printer;
+
+    // Analyse the content to be extracted, which consists of determining the statements
+    // which are complete and collecting the declarations seen.
+    FunctionExtractionAnalyser analyser(interface->semanticInfo().doc->translationUnit(),
+                                        selStart, selEnd,
+                                        file,
+                                        printer);
+    if (!analyser(refFuncDef))
+        return noResult();
+
+    // We also need to collect the declarations of the parameters from the reference function.
+    QSet<QString> refFuncParams;
+    if (refFuncDef->declarator->postfix_declarator_list
+            && refFuncDef->declarator->postfix_declarator_list->value
+            && refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator()) {
+        FunctionDeclaratorAST *funcDecltr =
+            refFuncDef->declarator->postfix_declarator_list->value->asFunctionDeclarator();
+        if (funcDecltr->parameter_declaration_clause
+                && funcDecltr->parameter_declaration_clause->parameter_declaration_list) {
+            for (ParameterDeclarationListAST *it =
+                    funcDecltr->parameter_declaration_clause->parameter_declaration_list;
+                 it;
+                 it = it->next) {
+                ParameterDeclarationAST *paramDecl = it->value->asParameterDeclaration();
+                const QString &specifiers =
+                       file->textOf(file->startOf(paramDecl),
+                                    file->endOf(paramDecl->type_specifier_list->lastValue()));
+                const QPair<QString, QString> &p =
+                        assembleDeclarationData(specifiers, paramDecl->declarator, file, printer);
+                if (!p.first.isEmpty()) {
+                    analyser.m_knownDecls.insert(p.first, p.second);
+                    refFuncParams.insert(p.first);
+                }
+            }
+        }
+    }
+
+    // Identify what would be parameters for the new function and its return value, if any.
+    Symbol *funcReturn = 0;
+    QList<QPair<QString, QString> > relevantDecls;
+    SemanticInfo::LocalUseIterator it(interface->semanticInfo().localUses);
+    while (it.hasNext()) {
+        it.next();
+
+        bool usedBeforeExtraction = false;
+        bool usedAfterExtraction = false;
+        bool usedInsideExtraction = false;
+        const QList<SemanticInfo::Use> &uses = it.value();
+        foreach (const SemanticInfo::Use &use, uses) {
+            const int position = file->position(use.line, use.column);
+            if (position < analyser.m_extractionStart)
+                usedBeforeExtraction = true;
+            else if (position >= analyser.m_extractionEnd)
+                usedAfterExtraction = true;
+            else
+                usedInsideExtraction = true;
+        }
+
+        const QString &name = printer.prettyName(it.key()->name());
+
+        if (usedBeforeExtraction && usedInsideExtraction
+                || usedInsideExtraction && refFuncParams.contains(name)) {
+            QTC_ASSERT(analyser.m_knownDecls.contains(name), return noResult());
+            relevantDecls.append(qMakePair(name, analyser.m_knownDecls.value(name)));
+        }
+
+        // We assume that the first use of a local corresponds to its declaration.
+        if (usedInsideExtraction && usedAfterExtraction && !usedBeforeExtraction) {
+            if (!funcReturn) {
+                QTC_ASSERT(analyser.m_knownDecls.contains(name), return noResult());
+                // The return, if any, is stored as the first item in the list.
+                relevantDecls.prepend(qMakePair(name, analyser.m_knownDecls.value(name)));
+                funcReturn = it.key();
+            } else {
+                // Would require multiple returns. (Unless we do fancy things, as pointed below.)
+                return noResult();
+            }
+        }
+    }
+
+    // The current implementation doesn't try to be too smart since it preserves the original form
+    // of the declarations. This might be or not the desired effect. An improvement would be to
+    // let the user somehow customize the function interface.
+    return singleResult(new ExtractFunctionOperation(interface,
+                                                     analyser.m_extractionStart,
+                                                     analyser.m_extractionEnd,
+                                                     refFuncDef,
+                                                     funcReturn,
+                                                     relevantDecls));
+}
diff --git a/src/plugins/cppeditor/cppinsertdecldef.h b/src/plugins/cppeditor/cppinsertdecldef.h
index 8507f87186b..4f2f97e1687 100644
--- a/src/plugins/cppeditor/cppinsertdecldef.h
+++ b/src/plugins/cppeditor/cppinsertdecldef.h
@@ -35,12 +35,6 @@
 
 #include "cppquickfix.h"
 
-#include <CPlusPlusForwardDeclarations.h>
-
-namespace CPlusPlus {
-class ClassOrNamespace;
-} // namespace CPlusPlus
-
 namespace CppEditor {
 namespace Internal {
 
@@ -49,11 +43,6 @@ class DeclFromDef: public CppQuickFixFactory
 public:
     virtual QList<CppQuickFixOperation::Ptr>
         match(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
-
-protected:
-    static QString generateDeclaration(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
-                                       CPlusPlus::Function *method,
-                                       CPlusPlus::ClassOrNamespace *targetBinding);
 };
 
 class DefFromDecl: public CppQuickFixFactory
@@ -63,6 +52,13 @@ public:
         match(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface);
 };
 
+class ExtractFunction : public CppQuickFixFactory
+{
+    virtual QList<CppQuickFixOperation::Ptr>
+        match(const QSharedPointer<const CppQuickFixAssistInterface> &interface);
+};
+
+
 } // namespace Internal
 } // namespace CppEditor
 
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 29bb1750ae0..0c683a3b479 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -1948,6 +1948,7 @@ private:
     };
 };
 
+
 } // end of anonymous namespace
 
 void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
@@ -1973,4 +1974,5 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
     plugIn->addAutoReleasedObject(new DefFromDecl);
     plugIn->addAutoReleasedObject(new ApplyDeclDefLinkChanges);
     plugIn->addAutoReleasedObject(new IncludeAdder);
+    plugIn->addAutoReleasedObject(new ExtractFunction);
 }
diff --git a/src/plugins/cpptools/cpptoolsreuse.cpp b/src/plugins/cpptools/cpptoolsreuse.cpp
index 27b09656e73..f9744ff14ed 100644
--- a/src/plugins/cpptools/cpptoolsreuse.cpp
+++ b/src/plugins/cpptools/cpptoolsreuse.cpp
@@ -102,5 +102,21 @@ bool isOwnershipRAIIType(CPlusPlus::Symbol *symbol, const LookupContext &context
     return false;
 }
 
+bool isValidIdentifier(const QString &s)
+{
+    const int length = s.length();
+    for (int i = 0; i < length; ++i) {
+        const QChar &c = s.at(i);
+        if (i == 0) {
+            if (!c.isLetter() && c != QLatin1Char('_'))
+                return false;
+        } else {
+            if (!c.isLetterOrNumber() && c != QLatin1Char('_'))
+                return false;
+        }
+    }
+    return true;
+}
+
 
 } // CppTools
diff --git a/src/plugins/cpptools/cpptoolsreuse.h b/src/plugins/cpptools/cpptoolsreuse.h
index a290905bda3..f6b4d42a265 100644
--- a/src/plugins/cpptools/cpptoolsreuse.h
+++ b/src/plugins/cpptools/cpptoolsreuse.h
@@ -49,6 +49,8 @@ void CPPTOOLS_EXPORT moveCursorToEndOfIdentifier(QTextCursor *tc);
 bool CPPTOOLS_EXPORT isOwnershipRAIIType(CPlusPlus::Symbol *symbol,
                                          const CPlusPlus::LookupContext &context);
 
+bool CPPTOOLS_EXPORT isValidIdentifier(const QString &s);
+
 } // CppTools
 
 #endif // CPPTOOLSREUSE_H
-- 
GitLab