Commit 2c426653 authored by Milian Wolff's avatar Milian Wolff
Browse files

Handle operators when extracting template parameters

Complex identifiers might include fancy expressions including
operator functions. In such cases we got totally confused by
operators that use angle brackets. This patch fixes that by
detecting such cases and skipping them.

In theory we should only do that for C++, in practice this is somewhat
hard to detect at this level. Furthermore, I believe we shouldn't run
into such issues with other languages anyways, so handling it
generically is hopefully fine.

This patch also fixes runtime warnings of the form:
```
Using QCharRef with an index pointing outside the valid range of a QString.
The corresponding behavior is deprecated, and will be changed in a future version of Qt.
```
parent 953696be
......@@ -74,6 +74,26 @@ int rStrip_impl(const T& str, T& from)
}
return s - from.length();
}
bool isOperator(const QString& str, int pos)
{
const auto c = str[pos];
Q_ASSERT(c == QLatin1Char('<') || c == QLatin1Char('>'));
--pos;
// handle `operator<<` and `operator>>`
if (pos > 0 && str[pos] == c) {
--pos;
}
// skip spaces, e.g. `operator <`
while (pos > 0 && str[pos].isSpace()) {
--pos;
}
return QStringView(str).mid(0, pos + 1).endsWith(QLatin1String("operator"));
}
}
namespace KDevelop {
......@@ -116,6 +136,9 @@ int findClose(const QString& str, int pos)
for (int a = pos; a < str.length(); a++) {
switch (str[a].unicode()) {
case '<':
if (isOperator(str, a))
break;
[[fallthrough]];
case '(':
case '[':
case '{':
......@@ -123,6 +146,8 @@ int findClose(const QString& str, int pos)
depth++;
break;
case '>':
if (isOperator(str, a))
break;
if (last == QLatin1Char('-'))
break;
[[fallthrough]];
......@@ -165,23 +190,28 @@ int findClose(const QString& str, int pos)
int findCommaOrEnd(const QString& str, int pos, QChar validEnd)
{
for (int a = pos; a < str.length(); a++) {
switch (str[a].unicode())
{
switch (str[a].unicode()) {
case '<':
if (isOperator(str, a))
break;
[[fallthrough]];
case '"':
case '(':
case '[':
case '{':
case '<':
a = findClose(str, a);
if (a == -1)
return str.length();
break;
case '>':
if (isOperator(str, a))
break;
[[fallthrough]];
case ')':
case ']':
case '}':
case '>':
if (validEnd != QLatin1Char(' ') && validEnd != str[a])
continue;
break;
[[fallthrough]];
case ',':
return a;
......
......@@ -127,3 +127,50 @@ void TestStringHelpers::benchFormatComment()
));
}
}
void TestStringHelpers::testParamIterator_data()
{
QTest::addColumn<QString>("source");
QTest::addColumn<QStringList>("params");
auto addTest = [](const QString& source, const QStringList& params) {
QTest::addRow("%s", qPrintable(source)) << source << params;
};
addTest("Empty", {});
addTest("Foo<T1, T2>", {"T1", "T2"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>>(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>>(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<<(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<<(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<=>(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<=>(std::declval<_Up>()))>"});
addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator->(std::declval<_Up>()))>>",
{"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator->(std::declval<_Up>()))>"});
}
void TestStringHelpers::testParamIterator()
{
QFETCH(QString, source);
QFETCH(QStringList, params);
const auto parens = QStringLiteral("<>:");
auto it = KDevelop::ParamIterator(parens, source);
while (!params.isEmpty()) {
QVERIFY(it);
QCOMPARE(*it, params.takeFirst());
++it;
}
QVERIFY(!it);
}
......@@ -21,6 +21,9 @@ private Q_SLOTS:
void testFormatComment();
void benchFormatComment();
void testParamIterator_data();
void testParamIterator();
};
#endif // KDEVPLATFORM_TEST_STRINGHELPERS_H
Supports Markdown
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