diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-11-06 09:20:29 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-11-09 15:49:19 +0000 |
commit | 3428efa5f69d321bcacdd106b0e724ab99bb63ed (patch) | |
tree | 2b53a24f83cefd95e2861e8594a0d37010dfe020 | |
parent | c2a9236fe970fc34fbb90757d4e8b562760cbc15 (diff) |
shiboken6: Add a new parser for AddedFunction parameters
Observe' <' (templates), '{' (initializer lists), '[' (array dimensions)
and '(' (initialization, function pointers) when splitting the
parameter lists of added functions.
Add a test.
Change-Id: I8cdc135a2daceab5587c4b5545ed38f0a022b5f8
Reviewed-by: Christian Tismer <tismer@stackless.com>
4 files changed, 220 insertions, 1 deletions
diff --git a/sources/shiboken6/ApiExtractor/modifications.cpp b/sources/shiboken6/ApiExtractor/modifications.cpp index 54b30f041..a370a2e03 100644 --- a/sources/shiboken6/ApiExtractor/modifications.cpp +++ b/sources/shiboken6/ApiExtractor/modifications.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "modifications.h" +#include "modifications_p.h" #include "typedatabase.h" #include "typesystem.h" @@ -174,6 +175,115 @@ bool FunctionModification::setSignature(const QString &s, QString *errorMessage) return true; } +// Helpers to split a parameter list of <add-function>, <declare-function> +// (@ denoting names), like +// "void foo(QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...)" +namespace AddedFunctionParser { + +bool Argument::equals(const Argument &rhs) const +{ + return type == rhs.type && name == rhs.name && defaultValue == rhs.defaultValue; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const Argument &a) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "Argument(type=\"" << a.type << '"'; + if (!a.name.isEmpty()) + d << ", name=\"" << a.name << '"'; + if (!a.defaultValue.isEmpty()) + d << ", defaultValue=\"" << a.defaultValue << '"'; + d << ')'; + return d; +} +#endif // QT_NO_DEBUG_STREAM + +// Helper for finding the end of a function parameter, observing +// nested template parameters or lists. +static int parameterTokenEnd(int startPos, QStringView paramString) +{ + const int end = paramString.size(); + int nestingLevel = 0; + for (int p = startPos; p < end; ++p) { + switch (paramString.at(p).toLatin1()) { + case ',': + if (nestingLevel == 0) + return p; + break; + case '<': // templates + case '{': // initializer lists of default values + case '(': // initialization, function pointers + case '[': // array dimensions + ++nestingLevel; + break; + case '>': + case '}': + case ')': + case ']': + --nestingLevel; + break; + } + } + return end; +} + +// Split a function parameter list into string tokens containing one +// parameters (including default value, etc). +static QList<QStringView> splitParameterTokens(QStringView paramString) +{ + QList<QStringView> result; + int startPos = 0; + for ( ; startPos < paramString.size(); ) { + int end = parameterTokenEnd(startPos, paramString); + result.append(paramString.mid(startPos, end - startPos).trimmed()); + startPos = end + 1; + } + return result; +} + +// Split a function parameter list +Arguments splitParameters(QStringView paramString, QString *errorMessage) +{ + Arguments result; + const QList<QStringView> tokens = splitParameterTokens(paramString); + + for (const auto &t : tokens) { + Argument argument; + // Check defaultValue, "int @b@=5" + const int equalPos = t.lastIndexOf(QLatin1Char('=')); + if (equalPos != -1) { + const int defaultValuePos = equalPos + 1; + argument.defaultValue = + t.mid(defaultValuePos, t.size() - defaultValuePos).trimmed().toString(); + } + QString typeString = (equalPos != -1 ? t.left(equalPos) : t).trimmed().toString(); + // Check @name@ + const int atPos = typeString.indexOf(QLatin1Char('@')); + if (atPos != -1) { + const int namePos = atPos + 1; + const int nameEndPos = typeString.indexOf(QLatin1Char('@'), namePos); + if (nameEndPos == -1) { + if (errorMessage != nullptr) { + *errorMessage = QLatin1String("Mismatched @ in \"") + + paramString.toString() + QLatin1Char('"'); + } + return {}; + } + argument.name = typeString.mid(namePos, nameEndPos - namePos).trimmed(); + typeString.remove(atPos, nameEndPos - atPos + 1); + } + argument.type = typeString.trimmed(); + result.append(argument); + } + + return result; +} + +} // namespace AddedFunctionParser + // ---------------------- AddedFunction static AddedFunction::TypeInfo parseType(const QString& signature, diff --git a/sources/shiboken6/ApiExtractor/modifications_p.h b/sources/shiboken6/ApiExtractor/modifications_p.h new file mode 100644 index 000000000..c8f18308e --- /dev/null +++ b/sources/shiboken6/ApiExtractor/modifications_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MODIFICATIONS_P_H +#define MODIFICATIONS_P_H + +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QStringView> + +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE + +// Helpers to split a parameter list of <add-function>, <declare-function> +// in a separate header for testing purposes + +namespace AddedFunctionParser { + +struct Argument +{ + bool equals(const Argument &rhs) const; + + QString type; + QString name; + QString defaultValue; +}; + +using Arguments = QList<Argument>; + +inline bool operator==(const Argument &a1, const Argument &a2) { return a1.equals(a2); } +inline bool operator!=(const Argument &a1, const Argument &a2) { return !a1.equals(a2); } + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const Argument &a); +#endif + +Arguments splitParameters(QStringView paramString, QString *errorMessage = nullptr); + +} // namespace AddedFunctionParser + +#endif // MODIFICATIONS_P_H diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp index bacdd4906..ca10cbfc6 100644 --- a/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp +++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp @@ -31,6 +31,7 @@ #include "testutil.h" #include <abstractmetafunction.h> #include <abstractmetalang.h> +#include <modifications_p.h> #include <typesystem.h> void TestAddFunction::testParsingFuncNameAndConstness() @@ -464,5 +465,44 @@ void TestAddFunction::testAddFunctionWithTemplateArg() QCOMPARE(arg.type().instantiations().count(), 1); } -QTEST_APPLESS_MAIN(TestAddFunction) +// Test splitting of <add-function> parameter lists. + +Q_DECLARE_METATYPE(AddedFunctionParser::Argument) + +using Arguments = AddedFunctionParser::Arguments; + +void TestAddFunction::testAddFunctionTypeParser_data() +{ + QTest::addColumn<QString>("parameterList"); + QTest::addColumn<Arguments>("expected"); + + QTest::newRow("empty") + << QString() << Arguments{}; + + QTest::newRow("1-arg") + << QString::fromLatin1("int @a@=42") + << Arguments{{QLatin1String("int"), QLatin1String("a"), QLatin1String("42")}}; + + QTest::newRow("2-args") + << QString::fromLatin1("double @d@, int @a@=42") + << Arguments{{QLatin1String("double"), QLatin1String("d"), {}}, + {QLatin1String("int"), QLatin1String("a"), QLatin1String("42")}}; + + QTest::newRow("template-var_args") + << QString::fromLatin1("const QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...") + << Arguments{{QLatin1String("const QList<X,Y> &"), QLatin1String("list"), QLatin1String("QList<X,Y>{1,2}")}, + {QLatin1String("int"), QLatin1String("b"), QLatin1String("5")}, + {QLatin1String("..."), {}, {}}}; +} +void TestAddFunction::testAddFunctionTypeParser() +{ + + QFETCH(QString, parameterList); + QFETCH(Arguments, expected); + + const auto actual = AddedFunctionParser::splitParameters(parameterList); + QCOMPARE(actual, expected); +} + +QTEST_APPLESS_MAIN(TestAddFunction) diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.h b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h index d95f0ecfe..195633030 100644 --- a/sources/shiboken6/ApiExtractor/tests/testaddfunction.h +++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h @@ -49,6 +49,8 @@ private slots: void testModifyAddedFunction(); void testAddFunctionOnTypedef(); void testAddFunctionWithTemplateArg(); + void testAddFunctionTypeParser_data(); + void testAddFunctionTypeParser(); }; #endif |