cppinsertdecldef.cpp 12.2 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
**
**************************************************************************/

33
#include "cppinsertdecldef.h"
Leandro Melo's avatar
Leandro Melo committed
34
#include "cppquickfixassistant.h"
35

36
#include <CPlusPlus.h>
37
#include <cplusplus/ASTPath.h>
38
#include <cplusplus/CppRewriter.h>
39
40
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
41
#include <cpptools/insertionpointlocator.h>
42
#include <cpptools/cpprefactoringchanges.h>
43
44

#include <QtCore/QCoreApplication>
45
#include <QtCore/QDir>
46
47

using namespace CPlusPlus;
48
using namespace CppEditor;
49
50
51
52
53
using namespace CppEditor::Internal;
using namespace CppTools;

namespace {

54
class InsertDeclOperation: public CppQuickFixOperation
55
56
{
public:
Leandro Melo's avatar
Leandro Melo committed
57
58
    InsertDeclOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
                        int priority,
59
60
61
                        const QString &targetFileName, const Class *targetSymbol,
                        InsertionPointLocator::AccessSpec xsSpec,
                        const QString &decl)
Leandro Melo's avatar
Leandro Melo committed
62
        : CppQuickFixOperation(interface, priority)
63
        , m_targetFileName(targetFileName)
64
        , m_targetSymbol(targetSymbol)
65
        , m_xsSpec(xsSpec)
66
67
        , m_decl(decl)
    {
68
69
70
71
72
73
74
75
76
77
78
        QString type;
        switch (xsSpec) {
        case InsertionPointLocator::Public: type = QLatin1String("public"); break;
        case InsertionPointLocator::Protected: type = QLatin1String("protected"); break;
        case InsertionPointLocator::Private: type = QLatin1String("private"); break;
        case InsertionPointLocator::PublicSlot: type = QLatin1String("public slot"); break;
        case InsertionPointLocator::ProtectedSlot: type = QLatin1String("protected slot"); break;
        case InsertionPointLocator::PrivateSlot: type = QLatin1String("private slot"); break;
        default: break;
        }

Friedemann Kleint's avatar
Friedemann Kleint committed
79
        setDescription(QCoreApplication::translate("CppEditor::InsertDeclOperation",
80
                                                   "Add %1 Declaration").arg(type));
81
    }
82

83
84
    void performChanges(const CppRefactoringFilePtr &,
                        const CppRefactoringChanges &refactoring)
85
    {
86
        InsertionPointLocator locator(refactoring);
87
88
        const InsertionLocation loc = locator.methodDeclarationInClass(
                    m_targetFileName, m_targetSymbol, m_xsSpec);
Roberto Raggi's avatar
Roberto Raggi committed
89
        Q_ASSERT(loc.isValid());
90

91
92
93
        CppRefactoringFilePtr targetFile = refactoring.file(m_targetFileName);
        int targetPosition1 = targetFile->position(loc.line(), loc.column());
        int targetPosition2 = qMax(0, targetFile->position(loc.line(), 1) - 1);
94
95

        Utils::ChangeSet target;
96
        target.insert(targetPosition1, loc.prefix() + m_decl);
97
98
99
100
        targetFile->setChangeSet(target);
        targetFile->appendIndentRange(Utils::ChangeSet::Range(targetPosition2, targetPosition1));
        targetFile->setOpenEditor(true, targetPosition1);
        targetFile->apply();
101
102
103
104
    }

private:
    QString m_targetFileName;
105
    const Class *m_targetSymbol;
106
    InsertionPointLocator::AccessSpec m_xsSpec;
107
108
109
110
    QString m_decl;
};

} // anonymous namespace
111

Leandro Melo's avatar
Leandro Melo committed
112
113
QList<CppQuickFixOperation::Ptr> DeclFromDef::match(
    const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
114
{
Leandro Melo's avatar
Leandro Melo committed
115
    const QList<AST *> &path = interface->path();
116
    CppRefactoringFilePtr file = interface->currentFile();
117
118
119
120
121

    FunctionDefinitionAST *funDef = 0;
    int idx = 0;
    for (; idx < path.size(); ++idx) {
        AST *node = path.at(idx);
122
123
        if (idx > 1) {
            if (DeclaratorIdAST *declId = node->asDeclaratorId()) {
124
                if (file->isCursorOn(declId)) {
125
126
127
128
129
130
131
132
133
134
135
136
137
                    if (FunctionDefinitionAST *candidate = path.at(idx - 2)->asFunctionDefinition()) {
                        if (funDef) {
                            return noResult();
                        } else {
                            funDef = candidate;
                            break;
                        }
                    }
                }
            }
        }

        if (node->asClassSpecifier()) {
138
            return noResult();
mae's avatar
mae committed
139
        }
140
141
142
    }

    if (!funDef || !funDef->symbol)
143
        return noResult();
144
145
146

    Function *method = funDef->symbol;

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    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();

Leandro Melo's avatar
Leandro Melo committed
163
    if (ClassOrNamespace *binding = interface->context().lookupType(q->base(), enclosingScope)) {
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
        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());
Leandro Melo's avatar
Leandro Melo committed
182
                const QString decl = generateDeclaration(interface,
183
                                                         method,
184
185
                                                         binding);
                return singleResult(
Leandro Melo's avatar
Leandro Melo committed
186
                            new InsertDeclOperation(interface, idx, fn, matchingClass,
187
188
189
                                                    InsertionPointLocator::Public,
                                                    decl));
            }
190
191
192
        }
    }

193
    return noResult();
194
195
}

Leandro Melo's avatar
Leandro Melo committed
196
QString DeclFromDef::generateDeclaration(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &,
197
198
                                         Function *method,
                                         ClassOrNamespace *targetBinding)
199
{
200
201
    Q_UNUSED(targetBinding);

202
    Overview oo;
203
204
205
    oo.setShowFunctionSignatures(true);
    oo.setShowReturnTypes(true);
    oo.setShowArgumentNames(true);
206

207
    QString decl;
208
    decl += oo(method->type(), method->unqualifiedName());
209
    decl += QLatin1String(";\n");
210
211
212

    return decl;
}
213
214
215

namespace {

216
class InsertDefOperation: public CppQuickFixOperation
217
{
218
public:
Leandro Melo's avatar
Leandro Melo committed
219
    InsertDefOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface, int priority,
220
                       Declaration *decl, const InsertionLocation &loc)
Leandro Melo's avatar
Leandro Melo committed
221
        : CppQuickFixOperation(interface, priority)
222
223
224
        , m_decl(decl)
        , m_loc(loc)
    {
225
226
        const QString declFile = QString::fromUtf8(decl->fileName(), decl->fileNameLength());
        const QDir dir = QFileInfo(declFile).dir();
Friedemann Kleint's avatar
Friedemann Kleint committed
227
        setDescription(QCoreApplication::translate("CppEditor::InsertDefOperation",
228
                                                   "Add Definition in %1")
229
                       .arg(dir.relativeFilePath(m_loc.fileName())));
230
    }
231

232
233
    void performChanges(const CppRefactoringFilePtr &,
                        const CppRefactoringChanges &refactoring)
234
235
    {
        Q_ASSERT(m_loc.isValid());
236

237
        CppRefactoringFilePtr targetFile = refactoring.file(m_loc.fileName());
238

239
240
241
242
243
        Overview oo;
        oo.setShowFunctionSignatures(true);
        oo.setShowReturnTypes(true);
        oo.setShowArgumentNames(true);

244
        // make target lookup context
245
        Document::Ptr targetDoc = targetFile->cppDocument();
246
247
248
249
250
251
252
        Scope *targetScope = targetDoc->scopeAt(m_loc.line(), m_loc.column());
        LookupContext targetContext(targetDoc, assistInterface()->snapshot());
        ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
        if (!targetCoN)
            targetCoN = targetContext.globalNamespace();

        // setup rewriting to get minimally qualified names
253
        SubstitutionEnvironment env;
Leandro Melo's avatar
Leandro Melo committed
254
        env.setContext(assistInterface()->context());
255
        env.switchScope(m_decl->enclosingScope());
256
        UseMinimalNames q(targetCoN);
257
        env.enter(&q);
Leandro Melo's avatar
Leandro Melo committed
258
        Control *control = assistInterface()->context().control().data();
259
260

        // rewrite the function type
261
        FullySpecifiedType tn = rewriteType(m_decl->type(), &env, control);
262
263
264
265
266
267
268
269
270

        // rewrite the function name
        QString name;
        const FullySpecifiedType nametype = rewriteType(control->namedType(m_decl->name()), &env, control);
        if (NamedType *nt = nametype.type()->asNamedType()) {
            name = oo(nt->name());
        } else {
            name = oo(LookupContext::fullyQualifiedName(m_decl));
        }
271
272
273

        QString defText = oo.prettyType(tn, name) + "\n{\n}";

274
275
        int targetPos = targetFile->position(m_loc.line(), m_loc.column());
        int targetPos2 = qMax(0, targetFile->position(m_loc.line(), 1) - 1);
276
277
278

        Utils::ChangeSet target;
        target.insert(targetPos,  m_loc.prefix() + defText + m_loc.suffix());
279
280
281
282
        targetFile->setChangeSet(target);
        targetFile->appendIndentRange(Utils::ChangeSet::Range(targetPos2, targetPos));
        targetFile->setOpenEditor(true, targetPos);
        targetFile->apply();
283
284
    }

285
286
287
288
private:
    Declaration *m_decl;
    InsertionLocation m_loc;
};
289
290
291

} // anonymous namespace

Leandro Melo's avatar
Leandro Melo committed
292
293
QList<CppQuickFixOperation::Ptr> DefFromDecl::match(
    const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface)
294
{
Leandro Melo's avatar
Leandro Melo committed
295
    const QList<AST *> &path = interface->path();
296
297
298
299

    int idx = path.size() - 1;
    for (; idx >= 0; --idx) {
        AST *node = path.at(idx);
300
301
302
303
        if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
            if (simpleDecl->symbols && ! simpleDecl->symbols->next) {
                if (Symbol *symbol = simpleDecl->symbols->value) {
                    if (Declaration *decl = symbol->asDeclaration()) {
304
305
306
307
                        if (decl->type()->isFunctionType()
                                && !decl->type()->asFunctionType()->isPureVirtual()
                                && decl->enclosingScope()
                                && decl->enclosingScope()->isClass()) {
308
309
310
311
312
313
                            CppRefactoringChanges refactoring(interface->snapshot());
                            InsertionPointLocator locator(refactoring);
                            QList<CppQuickFixOperation::Ptr> results;
                            foreach (const InsertionLocation &loc, locator.methodDefinition(decl)) {
                                if (loc.isValid())
                                    results.append(CppQuickFixOperation::Ptr(new InsertDefOperation(interface, idx, decl, loc)));
314
                            }
315
                            return results;
316
317
318
319
                        }
                    }
                }
            }
320

321
322
            break;
        }
323
324
325
326
    }

    return noResult();
}