Commit e6270788 authored by David Stevens's avatar David Stevens

Added virtual override support to kdev-clang plugin.

REVIEW: 116842
parent cb331592
......@@ -7,6 +7,7 @@ kde4_add_library(kdevclangcodecompletion SHARED
model.cpp
context.cpp
navigationwidget.cpp
overridecompletionhelper.cpp
)
target_link_libraries(kdevclangcodecompletion LINK_PRIVATE
......
......@@ -89,6 +89,30 @@ protected:
QString m_replacement;
};
class OverrideItem : public CompletionItem<CompletionTreeItem>
{
public:
OverrideItem(QString& nameAndParams, QString& returnType)
: CompletionItem<KDevelop::CompletionTreeItem>(
nameAndParams,
i18n("Override %1", returnType),
"virtual " + returnType + ' ' + nameAndParams
)
{
}
QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
{
if (role == Qt::DecorationRole) {
if (index.column() == KTextEditor::CodeCompletionModel::Icon) {
static QIcon icon(KIcon("CTparents"));
return icon;
}
}
return CompletionItem<CompletionTreeItem>::data(index, role, model);
}
};
/**
* Specialized completion item class for items which are represented by a Declaration
*/
......@@ -182,6 +206,7 @@ ClangCodeCompletionContext::ClangCodeCompletionContext(const ParseSession* const
)
: CodeCompletionContext({}, QString(), {}, 0)
, m_results(nullptr, clang_disposeCodeCompleteResults)
, m_overrides(session->unit(), position, ClangString(clang_getFileName(session->file())).c_str())
{
ClangString file(clang_getFileName(session->file()));
......@@ -205,6 +230,7 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(con
const CursorInRevision& position)
{
QList<CompletionTreeItemPointer> items;
QList<CompletionTreeItemPointer> overrides;
QList<CompletionTreeItemPointer> macros;
QList<CompletionTreeItemPointer> builtin;
......@@ -334,6 +360,21 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(con
}
}
auto overrideList = m_overrides.getOverrides();
if (!overrideList.isEmpty()) {
for (int i = 0; i < overrideList.count(); i++) {
FunctionInfo info = overrideList.at(i);
QString nameAndParams = info.name + '(' + info.params.join(", ") + ')';
if(info.isVirtual)
nameAndParams = nameAndParams + " = 0";
overrides << CompletionTreeItemPointer(new OverrideItem(nameAndParams, info.returnType));
}
KDevelop::CompletionCustomGroupNode* node = new KDevelop::CompletionCustomGroupNode(i18n("Virtual Override"), 600);
node->appendChildren(overrides);
m_ungrouped << CompletionTreeElementPointer(node);
}
if (!macros.isEmpty()) {
KDevelop::CompletionCustomGroupNode* node = new KDevelop::CompletionCustomGroupNode(i18n("Macros"), 900);
node->appendChildren(macros);
......
......@@ -28,6 +28,8 @@
#include <memory>
#include "overridecompletionhelper.h"
class ParseSession;
class ClangCodeCompletionContext : public KDevelop::CodeCompletionContext
{
......@@ -50,6 +52,7 @@ public:
private:
std::unique_ptr<CXCodeCompleteResults, void(*)(CXCodeCompleteResults*)> m_results;
QList<KDevelop::CompletionTreeElementPointer> m_ungrouped;
OverrideCompletionHelper m_overrides;
};
#endif // CLANGCODECOMPLETIONCONTEXT_H
/*
* This file is part of KDevelop
* Copyright 2014 David Stevens <dgedstevens@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "overridecompletionhelper.h"
struct OverrideInfo
{
FunctionInfoList* functions;
QStringList templateTypes;
QMap<QString, QString> templateTypeMap;
};
//TODO replace this with clang_Type_getTemplateArgumentAsType when that
//function makes it into the mainstream libclang release.
void getTempalteTypes(CXCursor cursor, QStringList& types)
{
QString tStr = ClangString(clang_getTypeSpelling(clang_getCursorType(cursor))).toString();
int depth = 0;
int start = -1;
int cur = 0;
int length = tStr.size();
while (cur < length) {
char c = tStr.at(cur).toAscii();
if (c == '<') {
depth++;
if (depth == 1) {
start = cur+1;
}
} else if (c == '>') {
depth--;
if (depth == 0) {
types.append(tStr.mid(start, cur - start).trimmed());
}
} else if (c == ',') {
if (depth == 1) {
types.append(tStr.mid(start, cur - start).trimmed());
start = cur + 1;
}
}
cur++;
}
}
CXChildVisitResult baseClassVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data);
void processBaseClass(CXCursor cursor, FunctionInfoList* functionList)
{
QStringList concrete;
CXCursor ref = clang_getCursorReferenced(cursor);
CXCursor isTemplate = clang_getSpecializedCursorTemplate(ref);
if (!clang_Cursor_isNull(isTemplate)) {
getTempalteTypes(ref, concrete);
ref = isTemplate;
}
OverrideInfo info{functionList, concrete, {}};
clang_visitChildren(ref, baseClassVisitor, &info);
}
CXChildVisitResult baseClassVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data)
{
QString templateParam;
OverrideInfo* info = static_cast<OverrideInfo*>(data);
switch(clang_getCursorKind(cursor)) {
case CXCursor_TemplateTypeParameter:
templateParam = ClangString(clang_getCursorSpelling(cursor)).toString();
info->templateTypeMap.insert(templateParam, info->templateTypes.at(info->templateTypeMap.size()));
return CXChildVisit_Continue;
case CXCursor_CXXBaseSpecifier:
processBaseClass(cursor, info->functions);
return CXChildVisit_Continue;
case CXCursor_CXXMethod:
//TODO Add support for const qualifier. The clang-c api currently doesn't provide a good
//method to do this. The USR can be parsed to get the information, but that seems unsafe.
if (clang_CXXMethod_isVirtual(cursor)) {
QStringList params;
int numArgs = clang_Cursor_getNumArguments(cursor);
for (int i = 0; i < numArgs; i++)
{
CXCursor arg = clang_Cursor_getArgument(cursor, i);
QString id = ClangString(clang_getCursorDisplayName(arg)).toString();
QString type = ClangString(clang_getTypeSpelling(clang_getCursorType(arg))).toString();
if (info->templateTypeMap.contains(type)) {
type = info->templateTypeMap.value(type);
}
params << type + ' ' + id;
}
FunctionInfo fp;
QString retType = ClangString(clang_getTypeSpelling(clang_getCursorResultType(cursor))).toString();
if (info->templateTypeMap.contains(retType)) {
retType = info->templateTypeMap.value(retType);
}
fp.returnType = retType;
fp.name = ClangString(clang_getCursorSpelling(cursor)).toString();
fp.params = params;
fp.isVirtual = clang_CXXMethod_isPureVirtual(cursor);
info->functions->append(fp);
}
return CXChildVisit_Continue;
default:
return CXChildVisit_Continue;
}
}
CXChildVisitResult findBaseVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data)
{
if (clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier) {
processBaseClass(cursor, static_cast<FunctionInfoList*>(data));
}
return CXChildVisit_Continue;
}
OverrideCompletionHelper::OverrideCompletionHelper(const CXTranslationUnit& unit, const KDevelop::SimpleCursor& position, const char *file)
{
CXSourceLocation location = clang_getLocation(unit, clang_getFile(unit, file), position.line, position.column);
CXCursor cursor = clang_getCursor(unit, location);
clang_visitChildren(cursor, findBaseVisitor, &m_overrides);
}
FunctionInfoList OverrideCompletionHelper::getOverrides()
{
return m_overrides;
}
/*
* This file is part of KDevelop
* Copyright 2014 David Stevens <dgedstevens@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OVERRIDECOMPLETIONHELPER_H
#define OVERRIDECOMPLETIONHELPER_H
#include <clang-c/Index.h>
#include <language/editor/simplecursor.h>
#include <QStringList>
#include <../duchain/clangtypes.h>
#include "codecompletionexport.h"
struct FunctionInfo
{
QString returnType;
QString name;
QStringList params;
bool isVirtual;
};
Q_DECLARE_TYPEINFO(FunctionInfo, Q_MOVABLE_TYPE);
using FunctionInfoList = QVector<FunctionInfo>;
class KDEVCLANGCODECOMPLETION_EXPORT OverrideCompletionHelper
{
public:
OverrideCompletionHelper(const CXTranslationUnit& unit,
const KDevelop::SimpleCursor& position,
const char *filename);
FunctionInfoList getOverrides();
private:
FunctionInfoList m_overrides;
};
#endif //OVERRIDECOMPLETIONHELPER_H
\ No newline at end of file
......@@ -21,6 +21,18 @@ target_link_libraries(clang-standalone-parser
########### next target ###############
kde4_add_unit_test(codecompletiontest
codecompletiontest.cpp
)
target_link_libraries(codecompletiontest
${KDEVPLATFORM_TESTS_LIBRARIES}
${QT_QTTEST_LIBRARY}
kdevclangcodecompletion
)
########### next target ###############
kde4_add_unit_test(duchaintest
duchaintest.cpp
)
......
/*
* Copyright 2014 David Stevens <dgedstevens@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "codecompletiontest.h"
#include <qtest_kde.h>
#include <tests/testcore.h>
#include <tests/autotestshell.h>
#include <tests/testfile.h>
#include <duchain/parsesession.h>
#include <language/codecompletion/codecompletiontesthelper.h>
#include <codecompletion/overridecompletionhelper.h>
QTEST_KDEMAIN(CodeCompletionTest, NoGUI);
using namespace KDevelop;
void CodeCompletionTest::initTestCase()
{
QVERIFY(qputenv("KDEV_DISABLE_PLUGINS", "kdevcppsupport"));
AutoTestShell::init();
TestCore::initialize(Core::NoUi);
}
void CodeCompletionTest::cleanupTestCase()
{
TestCore::shutdown();
}
QString formatFunctionInfo(const FunctionInfo& info)
{
return QString(info.returnType + ' ' + info.name + '(' + info.params.join(", ") +
')' + (info.isVirtual ? " = 0" : ""));
}
void CodeCompletionTest::testVirtualOverrideNonTemplate() {
TestFile file("class Foo { virtual void foo(); virtual char foo(char c, int i, double d); };\n"
"class Bar : Foo \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),2);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("void foo()"));
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(1)),QString("char foo(char c, int i, double d)"));
}
void CodeCompletionTest::testVirtualOverrideTemplate() {
TestFile file("template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); } ;\n"
"class Bar : Foo<char,double> \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),1);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("double foo(char a, double b, int i)"));
}
void CodeCompletionTest::testVirtualOverrideNestedTemplate() {
TestFile file("template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); } ; "
"template<class T1, class T2> class Baz { };"
"class Bar : Foo<char,Baz<char,double>> \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),1);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("Baz<char, double> foo(char a, Baz<char, double> b, int i)"));
}
void CodeCompletionTest::testVirtualOverrideMulti() {
TestFile file("class Foo { virtual int foo(int i); } ;"
"class Baz { virtual char baz(char c); };"
"class Bar : Foo, Baz \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),2);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("int foo(int i)"));
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(1)),QString("char baz(char c)"));
}
void CodeCompletionTest::testVirtualOverrideDeep() {
TestFile file("class Foo { virtual int foo(int i); } ;"
"class Baz : Foo { };"
"class Bar : Baz \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),1);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("int foo(int i)"));
}
void CodeCompletionTest::testVirtualOverridePure() {
TestFile file("class Foo { virtual void foo() = 0; foo() {} };"
"class Bar : Foo \n{\n}","h");
ClangIndex index;
ParseSession session(IndexedString(file.url()), file.fileContents().toUtf8(), &index);
SimpleCursor position(2,1);
OverrideCompletionHelper overrides(session.unit(), position, file.url().c_str());
QCOMPARE(overrides.getOverrides().count(),1);
QCOMPARE(formatFunctionInfo(overrides.getOverrides().at(0)),QString("void foo() = 0"));
}
#include "codecompletiontest.moc"
/*
* Copyright 2014 David Stevens <dgedstevens@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DUCHAINTEST_H
#define DUCHAINTEST_H
#include <QObject>
class CodeCompletionTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void testVirtualOverrideNonTemplate();
void testVirtualOverrideTemplate();
void testVirtualOverrideNestedTemplate();
void testVirtualOverrideMulti();
void testVirtualOverrideDeep();
void testVirtualOverridePure();
};
#endif // DUCHAINTEST_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment