File indexing completed on 2024-06-16 04:23:16

0001 /*
0002     SPDX-FileCopyrightText: 2016 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "test_stringhelpers.h"
0008 
0009 #include <QTest>
0010 #include <QStandardPaths>
0011 
0012 #include <language/duchain/stringhelpers.h>
0013 
0014 #include <cstring>
0015 
0016 QTEST_MAIN(TestStringHelpers)
0017 
0018 using namespace KDevelop;
0019 
0020 void TestStringHelpers::initTestCase()
0021 {
0022     QStandardPaths::setTestModeEnabled(true);
0023 }
0024 
0025 void TestStringHelpers::testFormatComment_data()
0026 {
0027     QTest::addColumn<QByteArray>("input");
0028     QTest::addColumn<QByteArray>("output");
0029 
0030     QTest::newRow("cpp-style") << QByteArrayLiteral(
0031         "// foo\n"
0032         "// bar"
0033     ) << QByteArrayLiteral("foo\n bar");
0034 
0035     QTest::newRow("doxy-cpp-style") << QByteArrayLiteral(
0036         "/// foo\n"
0037         "/// bar"
0038     ) << QByteArrayLiteral("foo\n bar");
0039 
0040     QTest::newRow("doxy-cpp-excl-style") << QByteArrayLiteral(
0041         "//! foo\n"
0042         "//! bar"
0043     ) << QByteArrayLiteral("foo\n bar");
0044 
0045     QTest::newRow("doxy-cpp-singleline-style") << QByteArrayLiteral("///< foo") << QByteArrayLiteral("foo");
0046     QTest::newRow("doxy-cpp-excl-singleline-style") << QByteArrayLiteral("//!< foo") << QByteArrayLiteral("foo");
0047 
0048     QTest::newRow("c-style") << QByteArrayLiteral(
0049         "/*\n"
0050         " foo\n"
0051         " bar\n*/"
0052     ) << QByteArrayLiteral("foo\nbar");
0053 
0054     QTest::newRow("doxy-c-style") << QByteArrayLiteral(
0055         "/**\n"
0056         " * foo\n"
0057         " * bar\n */"
0058     ) << QByteArrayLiteral("foo\n bar");
0059 
0060     QTest::newRow("doxy-c-style2") << QByteArrayLiteral(
0061         "/**\n"
0062         " * foo\n"
0063         " * bar\n **/"
0064     ) << QByteArrayLiteral("foo\n bar");
0065 
0066     QTest::newRow("real multiline") << QByteArrayLiteral(
0067                           "/**\n"
0068                           " * This is a real comment of some imaginary code.\n"
0069                           " *\n"
0070                           " * @param foo bar\n"
0071                           " * @return meh\n"
0072                           " */\n"
0073                       )
0074                     << QByteArrayLiteral("This is a real comment of some imaginary code.\n\n @param foo bar\n @return meh");
0075     QTest::newRow("doxy-qt-style-after-member") << QByteArrayLiteral(
0076         "/*!< line1\n"
0077         "line2 */"
0078     ) << QByteArrayLiteral("line1\nline2");
0079 
0080     QTest::newRow("doxy-c-style-after-member") << QByteArrayLiteral(
0081         "/**< line1\n"
0082         "line2 */"
0083     ) << QByteArrayLiteral("line1\nline2");
0084 
0085     QTest::newRow("doxy-cpp-style-after-member") << QByteArrayLiteral(
0086         "//!< line1\n"
0087         "//!< line2"
0088     ) << QByteArrayLiteral("line1\n line2");
0089 
0090     QTest::newRow("doxy-cpp-style-after-member2") << QByteArrayLiteral(
0091         "/// line1\n"
0092         "/// < line2"
0093     ) << QByteArrayLiteral("line1\n < line2");
0094 
0095     QTest::newRow("doxy-qt-style-before-member") << QByteArrayLiteral(
0096         "/*! line1\n"
0097         "line2 */"
0098     ) << QByteArrayLiteral("line1\nline2");
0099 
0100     QTest::newRow("doxy-qt-style-before-member2") << QByteArrayLiteral(
0101         "/*! line1\n"
0102         " * *line2* */"
0103     ) << QByteArrayLiteral("line1\n *line2*");
0104 
0105     QTest::newRow("doxy-cpp-style-before-member") << QByteArrayLiteral(
0106         "//! line1\n"
0107         "//! line2"
0108     ) << QByteArrayLiteral("line1\n line2");
0109 }
0110 
0111 void TestStringHelpers::testFormatComment()
0112 {
0113     QFETCH(QByteArray, input);
0114     QFETCH(QByteArray, output);
0115 
0116     QCOMPARE(formatComment(input), output);
0117 }
0118 
0119 void TestStringHelpers::benchFormatComment()
0120 {
0121     QBENCHMARK {
0122         formatComment(QByteArrayLiteral(
0123                           "/**\n"
0124                           " * This is a real comment of some imaginary code.\n"
0125                           " *\n"
0126                           " * @param foo bar\n"
0127                           " * @return meh\n"
0128                           " */\n"
0129                       ));
0130     }
0131 }
0132 
0133 void TestStringHelpers::testParamIterator_data()
0134 {
0135     QTest::addColumn<QString>("parens");
0136     QTest::addColumn<QString>("source");
0137     QTest::addColumn<QStringList>("params");
0138     QTest::addColumn<int>("endPosition");
0139 
0140     auto addGeneralTest = [](const QString& parens, const QString& source, const QStringList& params,
0141                              int endPosition = -1) {
0142         QTest::addRow("%s", qPrintable(source))
0143             << parens << source << params << (endPosition == -1 ? source.size() : endPosition);
0144     };
0145     auto addTest = [addGeneralTest](const QString& source, const QStringList& params, int endPosition = -1) {
0146         addGeneralTest("<>:", source, params, endPosition);
0147     };
0148     auto addMacroTest = [addGeneralTest](const QString& source, const QStringList& params, int endPosition = -1) {
0149         addGeneralTest("()", source, params, endPosition);
0150     };
0151 
0152     addTest("Empty", {});
0153     addTest("Foo<T1, T2>", {"T1", "T2"});
0154     addTest("operator<", {});
0155 
0156     // Such strings are passed to ParamIterator() when an Identifier is constructed from a template argument inside the
0157     // call to Visitor::makeType() in Visitor::createClassTemplateSpecializationType(). For example, the following valid
0158     // C++ code `K< &operator<< <S> > k;` passes the string "&operator<<<S>" to ParamIterator().
0159     addTest("operator< <QString>", {"QString"});
0160     addTest("operator<<<KDevVarLengthArray, Path >", {"KDevVarLengthArray", "Path"});
0161     addTest("operator<=>< QRegularExpression,IndexedString*,char\t>", {"QRegularExpression", "IndexedString*", "char"});
0162     // The valid C++ code `C<decltype(A::f<int>)> c;` passes the string "decltype(A::f<int>)" to ParamIterator().
0163     addTest("decltype(f<int>)", {});
0164     addTest("decltype(A::f)", {});
0165     addTest("decltype(A::f<int>)", {});
0166     addTest("decltype(N::operator<  <N::String>)", {});
0167 
0168     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>>",
0169             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>"});
0170     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>>",
0171             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>"});
0172     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>>",
0173             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>"});
0174     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>>",
0175             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>"});
0176     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator>>(std::declval<_Up>()))>>",
0177             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator>>(std::declval<_Up>()))>"});
0178     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<<(std::declval<_Up>()))>>",
0179             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<<(std::declval<_Up>()))>"});
0180     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator<=>(std::declval<_Up>()))>>",
0181             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator<=>(std::declval<_Up>()))>"});
0182     addTest("__not_overloaded2<_Tp, foo<bar>, __void_t<decltype(std::declval<_Tp>().operator->(std::declval<_Up>()))>>",
0183             {"_Tp", "foo<bar>", "__void_t<decltype(std::declval<_Tp>().operator->(std::declval<_Up>()))>"});
0184     addTest("A<\">\\\">\">", {"\">\\\">\""});
0185     addTest("A<'>'>", {"'>'"});
0186     addTest("myoperator<anoperator<anotheroperator>, my_operator>", {"anoperator<anotheroperator>", "my_operator"});
0187     // c++17 operator<=
0188     addTest("Y<decltype(&X::operator<=), &X::operator<=>", {"decltype(&X::operator<=)", "&X::operator<="});
0189     // c++20 operator<=>
0190     addTest("Y<decltype(&X::operator<=>), &X::operator<=>>", {"decltype(&X::operator<=>)", "&X::operator<=>"});
0191     addTest("Y<decltype(&X::operator->), &X::operator->>", {"decltype(&X::operator->)", "&X::operator->"});
0192     addTest("Y<decltype(&X::operator->), Z<&X::operator->>>", {"decltype(&X::operator->)", "Z<&X::operator->>"});
0193     addTest("Y<decltype(&X::operator--), &X::operator-->", {"decltype(&X::operator--)", "&X::operator--"});
0194     addTest("Y<decltype(&X::operator--), Z<&X::operator-->>", {"decltype(&X::operator--)", "Z<&X::operator-->"});
0195     // c++17 operator<=
0196     addTest("Y<decltype(&X::operator<=), Z<&X::operator<=>>", {"decltype(&X::operator<=)", "Z<&X::operator<=>"});
0197     // c++20 operator<=>
0198     addTest("Y<decltype(&X::operator<=>), Z<&X::operator<=>>>", {"decltype(&X::operator<=>)", "Z<&X::operator<=>>"});
0199     // NOTE: the "bogus" identifiers below are invalid but shouldn't trigger UB, so the tests verify that we get
0200     // _something_ (even if it's wrong).
0201     addTest("bogus<_Tp, _Up, invalid<decltype(<=(std::declval<_Tp>(), std::declval<_Up>()))>>",
0202             {"_Tp", "_Up", "invalid<decltype(<=(std::declval<_Tp>(), std::declval<_Up>()))>"});
0203     addTest("bogus<_Tp, _Up, invalid<<=(std::declval<_Tp>(), std::declval<_Up>())>>",
0204             {"_Tp", "_Up", "invalid<<=(std::declval<_Tp>(), std::declval<_Up>())>>"});
0205     addTest("hardToParse<A<B>", {"A<B"});
0206     addTest("hardToParse<(A<B)>", {"(A<B)"});
0207     addTest("hardToParse<(A>B)>", {"(A>B)"});
0208     addTest("hardToParse<Foo<(A<B)>>", {"Foo<(A<B)>"});
0209     addTest("hardToParse<Foo<(A>B)>>", {"Foo<(A>B)>"});
0210 
0211     // Such zero/empty tparam strings are actually passed to ParamIterator() while libstdc++ headers are parsed.
0212     addTest("_Index_tuple<>", {});
0213     addTest("const __shared_count<  \t>", {});
0214     addTest("_Rb_tree_impl<_Key_compare, >", {"_Key_compare", ""});
0215     addTest("__hash_enum<_Tp,\t  \t>", {"_Tp", ""});
0216     addTest("__uniq_ptr_data<_Tp, _Dp, , >", {"_Tp", "_Dp", "", ""});
0217 
0218     const auto addSpacedOperatorTest = [addTest](const QString& op) {
0219         const auto tArg1 = QStringLiteral("x %1 y").arg(op);
0220         const auto tArg2 = QStringLiteral("Foo<%1>").arg(tArg1);
0221         for (const auto& templateArgument : {tArg1, tArg2}) {
0222             addTest(QStringLiteral("spaced<%1>").arg(templateArgument), {templateArgument});
0223         }
0224     };
0225 
0226     addSpacedOperatorTest("<");
0227     addSpacedOperatorTest(">");
0228     addSpacedOperatorTest("<=");
0229     addSpacedOperatorTest(">=");
0230     addSpacedOperatorTest("<<");
0231     addSpacedOperatorTest(">>");
0232     addSpacedOperatorTest("<<=");
0233     addSpacedOperatorTest(">>=");
0234     addSpacedOperatorTest("<=>");
0235 
0236     addSpacedOperatorTest("->");
0237     // -> must be recognized even when not surrounded with spaces
0238     addTest("arrow<u->v>", {"u->v"});
0239     addTest("arrow<Foo<u->v>>", {"Foo<u->v>"});
0240 
0241     addTest("X::Y<Z>", {}, 1);
0242     addTest("X<Z::C>", {"Z::C"});
0243 
0244     addMacroTest("Q_UNIMPLEMENTED() qWarning(\"Unimplemented code.\")", {}, std::strlen("Q_UNIMPLEMENTED()"));
0245     addMacroTest("Q_FALLTHROUGH( ) [[clang::fallthrough]]", {}, std::strlen("Q_FALLTHROUGH( )"));
0246     addMacroTest("( /*a)b*/ x , /*,*/y,z )", {"/*a)b*/ x", "/*,*/y", "z"});
0247 }
0248 
0249 void TestStringHelpers::testParamIterator()
0250 {
0251     QFETCH(QString, parens);
0252     QFETCH(QString, source);
0253     QFETCH(QStringList, params);
0254     QFETCH(int, endPosition);
0255 
0256     auto it = KDevelop::ParamIterator(parens, source);
0257 
0258     QEXPECT_FAIL("hardToParse<A<B>", "quasi impossible to parse without semantic knowledge of the types", Abort);
0259 
0260     int i = 0;
0261     while (!params.isEmpty()) {
0262         QVERIFY(it);
0263         if (i == 1) {
0264             QEXPECT_FAIL("Y<decltype(&X::operator<=), &X::operator<=>", "clang triggers warning for this C++17 code, due to C++20 spaceship op", Continue);
0265             QEXPECT_FAIL("Y<decltype(&X::operator<=), Z<&X::operator<=>>",
0266                          "clang triggers warning for this C++17 code, due to C++20 spaceship op", Continue);
0267         }
0268         QCOMPARE(*it, params.takeFirst());
0269         ++it;
0270         ++i;
0271     }
0272 
0273     QVERIFY(!it);
0274     QCOMPARE(it.position(), endPosition);
0275 }
0276 
0277 #include "moc_test_stringhelpers.cpp"