cppvirtualfunctionassistprovider.cpp 17.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/


#include "cppvirtualfunctionassistprovider.h"

#include "cppeditorconstants.h"
#include "cppelementevaluator.h"
35
#include "cppvirtualfunctionproposalitem.h"
36
37
38
39
40
41
42

#include <cplusplus/Icons.h>
#include <cplusplus/Overview.h>

#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>

43
44
#include <cpptools/symbolfinder.h>

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/genericproposalwidget.h>
#include <texteditor/codeassist/iassistinterface.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistproposal.h>

#include <utils/qtcassert.h>

using namespace CPlusPlus;
using namespace CppEditor::Internal;
using namespace TextEditor;

/// Activate current item with the same shortcut that is configured for Follow Symbol Under Cursor.
/// This is limited to single-key shortcuts without modifiers.
class VirtualFunctionProposalWidget : public GenericProposalWidget
{
public:
    VirtualFunctionProposalWidget(bool openInSplit)
    {
        const char *id = openInSplit
            ? TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT
            : TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR;
        if (Core::Command *command = Core::ActionManager::command(id))
            m_sequence = command->keySequence();
    }

protected:
    bool eventFilter(QObject *o, QEvent *e)
    {
        if (e->type() == QEvent::ShortcutOverride && m_sequence.count() == 1) {
            QKeyEvent *ke = static_cast<QKeyEvent *>(e);
            const QKeySequence seq(ke->key());
            if (seq == m_sequence) {
                activateCurrentProposalItem();
                e->accept();
                return true;
            }
        }
        return GenericProposalWidget::eventFilter(o, e);
    }

private:
    QKeySequence m_sequence;
};

class VirtualFunctionProposal : public GenericProposal
{
public:
    VirtualFunctionProposal(int cursorPos, IGenericProposalModel *model, bool openInSplit)
        : GenericProposal(cursorPos, model)
        , m_openInSplit(openInSplit)
    {}

    bool isFragile() const
    { return true; }

    IAssistProposalWidget *createWidget() const
    { return new VirtualFunctionProposalWidget(m_openInSplit); }

private:
    bool m_openInSplit;
};

class VirtualFunctionsAssistProcessor : public IAssistProcessor
{
public:
112
113
    VirtualFunctionsAssistProcessor(const VirtualFunctionAssistProvider::Parameters &params)
        : m_params(params)
114
115
    {}

116
    IAssistProposal *immediateProposal(const TextEditor::IAssistInterface *)
117
    {
118
        QTC_ASSERT(m_params.function, return 0);
119
120
121
122
123
124
125

        BasicProposalItem *hintItem = new VirtualFunctionProposalItem(CPPEditorWidget::Link());
        hintItem->setText(QCoreApplication::translate("VirtualFunctionsAssistProcessor",
                                                      "...searching overrides"));
        hintItem->setOrder(-1000);

        QList<BasicProposalItem *> items;
126
        items << itemFromSymbol(maybeDefinitionFor(m_params.function));
127
        items << hintItem;
128
        return new VirtualFunctionProposal(m_params.cursorPosition,
129
                                           new BasicProposalItemListModel(items),
130
                                           m_params.openInNextSplit);
131
132
    }

133
    IAssistProposal *perform(const IAssistInterface *)
134
    {
135
        QTC_ASSERT(m_params.function, return 0);
136
        QTC_ASSERT(m_params.staticClass, return 0);
137
        QTC_ASSERT(!m_params.snapshot.isEmpty(), return 0);
138

139
140
141
142
143
        Class *functionsClass = m_finder.findMatchingClassDeclaration(m_params.function,
                                                                      m_params.snapshot);
        if (!functionsClass)
            return 0;

144
145
        const QList<Symbol *> overrides = FunctionHelper::overrides(
            m_params.function, functionsClass, m_params.staticClass, m_params.snapshot);
146
147
148
        if (overrides.isEmpty())
            return 0;

149
150
        QList<BasicProposalItem *> items;
        foreach (Symbol *symbol, overrides)
151
152
            items << itemFromSymbol(maybeDefinitionFor(symbol));
        items.first()->setOrder(1000); // Ensure top position for function of static type
153

154
        return new VirtualFunctionProposal(m_params.cursorPosition,
155
                                           new BasicProposalItemListModel(items),
156
                                           m_params.openInNextSplit);
157
158
    }

159
160
161
private:
    Symbol *maybeDefinitionFor(Symbol *symbol)
    {
162
        if (Function *definition = m_finder.findMatchingDefinition(symbol, m_params.snapshot))
163
164
165
166
167
            return definition;
        return symbol;
    }

    BasicProposalItem *itemFromSymbol(Symbol *symbol) const
168
169
170
171
    {
        const QString text = m_overview.prettyName(LookupContext::fullyQualifiedName(symbol));
        const CPPEditorWidget::Link link = CPPEditorWidget::linkToSymbol(symbol);

172
        BasicProposalItem *item = new VirtualFunctionProposalItem(link, m_params.openInNextSplit);
173
174
175
176
177
178
        item->setText(text);
        item->setIcon(m_icons.iconForSymbol(symbol));

        return item;
    }

179
    VirtualFunctionAssistProvider::Parameters m_params;
180
181
    Overview m_overview;
    Icons m_icons;
182
    CppTools::SymbolFinder m_finder;
183
184
185
186
187
188
};

VirtualFunctionAssistProvider::VirtualFunctionAssistProvider()
{
}

189
bool VirtualFunctionAssistProvider::configure(const Parameters &parameters)
190
{
191
    m_params = parameters;
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    return true;
}

bool VirtualFunctionAssistProvider::isAsynchronous() const
{
    return true;
}

bool VirtualFunctionAssistProvider::supportsEditor(const Core::Id &editorId) const
{
    return editorId == CppEditor::Constants::CPPEDITOR_ID;
}

IAssistProcessor *VirtualFunctionAssistProvider::createProcessor() const
{
207
    return new VirtualFunctionsAssistProcessor(m_params);
208
209
210
211
}

enum VirtualType { Virtual, PureVirtual };

212
static bool isVirtualFunction_helper(const Function *function,
213
                                     const LookupContext &context,
214
215
                                     VirtualType virtualType,
                                     const Function **firstVirtual)
216
{
217
218
219
220
221
    enum { Unknown, False, True } res = Unknown;

    if (firstVirtual)
        *firstVirtual = 0;

222
223
224
    if (!function)
        return false;

225
    if (virtualType == PureVirtual)
226
227
228
229
230
231
232
233
        res = function->isPureVirtual() ? True : False;

    if (function->isVirtual()) {
        if (firstVirtual)
            *firstVirtual = function;
        if (res == Unknown)
            res = True;
    }
234

235
236
    if (!firstVirtual && res != Unknown)
        return res == True;
237

238
239
240
241
242
243
244
245
246
247
248
    QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope());
    if (!results.isEmpty()) {
        const bool isDestructor = function->name()->isDestructorNameId();
        foreach (const LookupItem &item, results) {
            if (Symbol *symbol = item.declaration()) {
                if (Function *functionType = symbol->type()->asFunctionType()) {
                    if (functionType->name()->isDestructorNameId() != isDestructor)
                        continue;
                    if (functionType == function) // already tested
                        continue;
                    if (functionType->isFinal())
249
250
251
252
253
254
255
256
                        return res == True;
                    if (functionType->isVirtual()) {
                        if (!firstVirtual)
                            return true;
                        if (res == Unknown)
                            res = True;
                        *firstVirtual = functionType;
                    }
257
258
259
260
261
                }
            }
        }
    }

262
    return res == True;
263
264
}

265
266
267
bool FunctionHelper::isVirtualFunction(const Function *function,
                                       const LookupContext &context,
                                       const Function **firstVirtual)
268
{
269
    return isVirtualFunction_helper(function, context, Virtual, firstVirtual);
270
271
}

272
273
274
bool FunctionHelper::isPureVirtualFunction(const Function *function,
                                           const LookupContext &context,
                                           const Function **firstVirtual)
275
{
276
    return isVirtualFunction_helper(function, context, PureVirtual, firstVirtual);
277
278
}

279
QList<Symbol *> FunctionHelper::overrides(Function *function, Class *functionsClass,
280
                                          Class *staticClass, const Snapshot &snapshot)
281
282
{
    QList<Symbol *> result;
283
    QTC_ASSERT(function && functionsClass && staticClass, return result);
284
285
286

    FullySpecifiedType referenceType = function->type();
    const Name *referenceName = function->name();
287
    QTC_ASSERT(referenceName && referenceType.isValid(), return result);
288
289

    // Find overrides
290
    CppEditor::Internal::CppClass cppClass = CppClass(functionsClass);
291
    cppClass.lookupDerived(staticClass, snapshot);
292
293
294
295
296
297
298

    QList<CppClass> l;
    l << cppClass;

    while (!l.isEmpty()) {
        // Add derived
        CppClass clazz = l.takeFirst();
299
300
301
302
303

        QTC_ASSERT(clazz.declaration, continue);
        Class *c = clazz.declaration->asClass();
        QTC_ASSERT(c, continue);

304
305
306
307
308
309
310
311
312
        foreach (const CppClass &d, clazz.derived) {
            if (!l.contains(d))
                l << d;
        }

        // Check member functions
        for (int i = 0, total = c->memberCount(); i < total; ++i) {
            Symbol *candidate = c->memberAt(i);
            const Name *candidateName = candidate->name();
313
314
315
            const FullySpecifiedType candidateType = candidate->type();
            if (!candidateName || !candidateType.isValid())
                continue;
316
317
318
319
320
321
322
            if (candidateName->isEqualTo(referenceName) && candidateType.isEqualTo(referenceType))
                result << candidate;
        }
    }

    return result;
}
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

#ifdef WITH_TESTS
#include "cppeditorplugin.h"

#include <QList>
#include <QTest>

namespace CppEditor {
namespace Internal {

enum Virtuality
{
    NotVirtual,
    Virtual,
    PureVirtual
};
typedef QList<Virtuality> VirtualityList;
340
341
342
343
344
} // Internal namespace
} // CppEditor namespace

Q_DECLARE_METATYPE(CppEditor::Internal::Virtuality)
Q_DECLARE_METATYPE(CppEditor::Internal::VirtualityList)
345
Q_DECLARE_METATYPE(QList<int>)
346
347
348

namespace CppEditor {
namespace Internal {
349
350
351
352
353
354

void CppEditorPlugin::test_functionhelper_virtualFunctions()
{
    // Create and parse document
    QFETCH(QByteArray, source);
    QFETCH(VirtualityList, virtualityList);
355
    QFETCH(QList<int>, firstVirtualList);
356
357
358
359
360
    Document::Ptr document = Document::create(QLatin1String("virtuals"));
    document->setUtf8Source(source);
    document->check(); // calls parse();
    QCOMPARE(document->diagnosticMessages().size(), 0);
    QVERIFY(document->translationUnit()->ast());
361
362
    QList<const Function *> allFunctions;
    const Function *firstVirtual = 0;
363
364
365
366

    // Iterate through Function symbols
    Snapshot snapshot;
    snapshot.insert(document);
367
    const LookupContext context(document, snapshot);
368
369
370
    Control *control = document->translationUnit()->control();
    Symbol **end = control->lastSymbol();
    for (Symbol **it = control->firstSymbol(); it != end; ++it) {
371
372
        if (const Function *function = (*it)->asFunction()) {
            allFunctions.append(function);
373
374
            QTC_ASSERT(!virtualityList.isEmpty(), return);
            Virtuality virtuality = virtualityList.takeFirst();
375
376
377
378
379
380
381
382
383
384
385
            QTC_ASSERT(!firstVirtualList.isEmpty(), return);
            int firstVirtualIndex = firstVirtualList.takeFirst();
            bool isVirtual = FunctionHelper::isVirtualFunction(function, context, &firstVirtual);
            bool isPureVirtual = FunctionHelper::isPureVirtualFunction(function, context,
                                                                       &firstVirtual);

            // Test for regressions introduced by firstVirtual
            QCOMPARE(FunctionHelper::isVirtualFunction(function, context), isVirtual);
            QCOMPARE(FunctionHelper::isPureVirtualFunction(function, context), isPureVirtual);
            if (isVirtual) {
                if (isPureVirtual)
386
387
388
389
                    QCOMPARE(virtuality, PureVirtual);
                else
                    QCOMPARE(virtuality, Virtual);
            } else {
390
391
392
                QEXPECT_FAIL("virtual-dtor-dtor", "Not implemented", Abort);
                if (allFunctions.size() == 3)
                    QEXPECT_FAIL("dtor-virtual-dtor-dtor", "Not implemented", Abort);
393
394
                QCOMPARE(virtuality, NotVirtual);
            }
395
396
397
398
            if (firstVirtualIndex == -1)
                QVERIFY(!firstVirtual);
            else
                QCOMPARE(firstVirtual, allFunctions.at(firstVirtualIndex));
399
400
401
        }
    }
    QVERIFY(virtualityList.isEmpty());
402
    QVERIFY(firstVirtualList.isEmpty());
403
404
405
406
407
408
409
}

void CppEditorPlugin::test_functionhelper_virtualFunctions_data()
{
    typedef QByteArray _;
    QTest::addColumn<QByteArray>("source");
    QTest::addColumn<VirtualityList>("virtualityList");
410
    QTest::addColumn<QList<int> >("firstVirtualList");
411
412
413

    QTest::newRow("none")
            << _("struct None { void foo() {} };\n")
414
415
            << (VirtualityList() << NotVirtual)
            << (QList<int>() << -1);
416
417
418

    QTest::newRow("single-virtual")
            << _("struct V { virtual void foo() {} };\n")
419
420
            << (VirtualityList() << Virtual)
            << (QList<int>() << 0);
421
422
423

    QTest::newRow("single-pure-virtual")
            << _("struct PV { virtual void foo() = 0; };\n")
424
425
            << (VirtualityList() << PureVirtual)
            << (QList<int>() << 0);
426
427
428
429

    QTest::newRow("virtual-derived-with-specifier")
            << _("struct Base { virtual void foo() {} };\n"
                 "struct Derived : Base { virtual void foo() {} };\n")
430
431
            << (VirtualityList() << Virtual << Virtual)
            << (QList<int>() << 0 << 0);
432
433
434
435

    QTest::newRow("virtual-derived-implicit")
            << _("struct Base { virtual void foo() {} };\n"
                 "struct Derived : Base { void foo() {} };\n")
436
437
            << (VirtualityList() << Virtual << Virtual)
            << (QList<int>() << 0 << 0);
438
439
440
441

    QTest::newRow("not-virtual-then-virtual")
            << _("struct Base { void foo() {} };\n"
                 "struct Derived : Base { virtual void foo() {} };\n")
442
443
            << (VirtualityList() << NotVirtual << Virtual)
            << (QList<int>() << -1 << 1);
444
445
446
447
448

    QTest::newRow("virtual-final-not-virtual")
            << _("struct Base { virtual void foo() {} };\n"
                 "struct Derived : Base { void foo() final {} };\n"
                 "struct Derived2 : Derived { void foo() {} };")
449
450
            << (VirtualityList() << Virtual << Virtual << NotVirtual)
            << (QList<int>() << 0 << 0 << -1);
451
452
453
454
455

    QTest::newRow("virtual-then-pure")
            << _("struct Base { virtual void foo() {} };\n"
                 "struct Derived : Base { virtual void foo() = 0; };\n"
                 "struct Derived2 : Derived { void foo() {} };")
456
457
            << (VirtualityList() << Virtual << PureVirtual << Virtual)
            << (QList<int>() << 0 << 0 << 0);
458
459
460
461
462

    QTest::newRow("virtual-virtual-final-not-virtual")
            << _("struct Base { virtual void foo() {} };\n"
                 "struct Derived : Base { virtual void foo() final {} };\n"
                 "struct Derived2 : Derived { void foo() {} };")
463
464
            << (VirtualityList() << Virtual << Virtual << NotVirtual)
            << (QList<int>() << 0 << 0 << -1);
465
466
467

    QTest::newRow("ctor-virtual-dtor")
            << _("struct Base { Base() {} virtual ~Base() {} };\n")
468
469
            << (VirtualityList() << NotVirtual << Virtual)
            << (QList<int>() << -1 << 1);
470
471
472
473
474
475
476
477
478
479
480
481
482

    QTest::newRow("virtual-dtor-dtor")
            << _("struct Base { virtual ~Base() {} };\n"
                 "struct Derived : Base { ~Derived() {} };\n")
            << (VirtualityList() << Virtual << Virtual)
            << (QList<int>() << 0 << 0);

    QTest::newRow("dtor-virtual-dtor-dtor")
            << _("struct Base { ~Base() {} };\n"
                 "struct Derived : Base { virtual ~Derived() {} };\n"
                 "struct Derived2 : Derived { ~Derived2() {} };\n")
            << (VirtualityList() << NotVirtual << Virtual << Virtual)
            << (QList<int>() << -1 << 1 << 1);
483
484
485
486
487
488
}

} // namespace Internal
} // namespace CppEditor

#endif