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"