diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-10-23 10:55:35 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-10-28 13:38:34 +0000 |
commit | 8d2aa4264ce2eef7b465c1917ca027eea294747e (patch) | |
tree | 93e9b72309172a93b6483cd636f01a4c4c06480f | |
parent | a26b04fae0f055d7590c9e7380afe7a5fb67c58e (diff) |
shiboken2: Handle operators written as hidden friends
qtbase/f7f1a71ea41579c1ff86c08c16b82e4c84bc891f changed some operators
to be hidden friends, which causes them to become invisible.
Detecting them requires parsing friend declarations and turning on
parsing of function bodies for clang_isCursorDefinition() being able
to tell a definition.
Fixes a number of tests failing (qlinef_test, qsize_test, repr_test,
unaryoperator_test).
Change-Id: I4d3107181b942efebd785cfae7c3fd1b6f0963ac
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
4 files changed, 76 insertions, 9 deletions
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp index dd0aca407..a90adca17 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp @@ -230,6 +230,7 @@ public: int m_anonymousEnumCount = 0; CodeModel::FunctionType m_currentFunctionType = CodeModel::Normal; + bool m_withinFriendDecl = false; }; bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t) @@ -964,14 +965,14 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) case CXCursor_ClassDecl: case CXCursor_UnionDecl: case CXCursor_StructDecl: - if (clang_isCursorDefinition(cursor) == 0) + if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0) return Skip; if (!d->addClass(cursor, codeModelClassTypeFromCursor(cursor.kind))) return Error; break; case CXCursor_ClassTemplate: case CXCursor_ClassTemplatePartialSpecialization: - if (clang_isCursorDefinition(cursor) == 0) + if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0) return Skip; d->addClass(cursor, CodeModel::Class); d->m_currentClass->setName(d->m_currentClass->name() + templateBrackets()); @@ -1028,16 +1029,18 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) case CXCursor_FieldDecl: d->addField(cursor); break; -#if CINDEX_VERSION_MAJOR > 0 || CINDEX_VERSION_MINOR >= 37 // Clang 4.0 case CXCursor_FriendDecl: + d->m_withinFriendDecl = true; + break; + case CXCursor_CompoundStmt: // Function bodies return Skip; -#endif case CXCursor_Constructor: case CXCursor_Destructor: // Note: Also use clang_CXXConstructor_is..Constructor? case CXCursor_CXXMethod: case CXCursor_ConversionFunction: + // Member functions of other classes can be declared to be friends. // Skip inline member functions outside class, only go by declarations inside class - if (!withinClassDeclaration(cursor)) + if (d->m_withinFriendDecl || !withinClassDeclaration(cursor)) return Skip; d->m_currentFunction = d->createMemberFunction(cursor, false); d->m_scopeStack.back()->addFunction(d->m_currentFunction); @@ -1059,8 +1062,19 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) d->m_scopeStack.back()->addFunction(d->m_currentFunction); break; case CXCursor_FunctionDecl: - d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, false); - d->m_scopeStack.back()->addFunction(d->m_currentFunction); + // Free functions or functions completely defined within "friend" (class + // operators). Note: CXTranslationUnit_SkipFunctionBodies must be off for + // clang_isCursorDefinition() to work here. + if (!d->m_withinFriendDecl || clang_isCursorDefinition(cursor) != 0) { + int scope = d->m_scopeStack.size() - 1; // enclosing class + if (d->m_withinFriendDecl) { + // Friend declaration: go back to namespace or file scope. + for (--scope; d->m_scopeStack.at(scope)->kind() == _CodeModelItem::Kind_Class; --scope) { + } + } + d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, false); + d->m_scopeStack.at(scope)->addFunction(d->m_currentFunction); + } break; case CXCursor_Namespace: { const auto type = namespaceType(cursor); @@ -1212,6 +1226,9 @@ bool Builder::endToken(const CXCursor &cursor) d->m_scopeStack.back()->addEnum(d->m_currentEnum); d->m_currentEnum.clear(); break; + case CXCursor_FriendDecl: + d->m_withinFriendDecl = false; + break; case CXCursor_VarDecl: case CXCursor_FieldDecl: d->m_currentField.clear(); diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp index d27c87828..3646462e8 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp @@ -232,8 +232,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, unsigned flags = 0) { // courtesy qdoc - const unsigned defaultFlags = CXTranslationUnit_SkipFunctionBodies - | CXTranslationUnit_Incomplete; + const unsigned defaultFlags = CXTranslationUnit_Incomplete; static const QByteArrayList defaultArgs = { #ifndef Q_OS_WIN diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp index c33296885..a1b4c708a 100644 --- a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp @@ -619,4 +619,53 @@ class Derived : public BaseAlias2 { QCOMPARE(derived->baseClasses().value(0), base); } +void TestAbstractMetaClass::testFreeOperators_data() +{ + QTest::addColumn<QByteArray>("code"); + + const QByteArray classHeader(R"CPP( +class Value +{ +public: + Value(int v) : m_value(v) {} + int value() const { return m_value; } +)CPP"); + + const QByteArray classFooter(R"CPP( +private: + int m_value; +}; +)CPP"); + + const QByteArray multOperatorSignature("Value operator*(const Value &v1, const Value &v2)"); + const QByteArray multOperatorBody("{ return Value(v1.value() * v2.value()); }"); + const QByteArray multOperator = multOperatorSignature + '\n' + multOperatorBody; + + QTest::newRow("free") + << (classHeader + classFooter + "\ninline " + multOperator); + QTest::newRow("free-friend-declared") + << (classHeader + "\n friend " + multOperatorSignature + ";\n" + classFooter + + "\ninline " + multOperator); + QTest::newRow("hidden friend") + << (classHeader + " friend inline " + multOperator + classFooter); +}; + +void TestAbstractMetaClass::testFreeOperators() +{ + QFETCH(QByteArray, code); + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <primitive-type name="int"/> + <value-type name="Value"/> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(code.constData(), xmlCode)); + QVERIFY(!builder.isNull()); + const auto classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QVERIFY(classes.constFirst()->hasArithmeticOperatorOverload()); + AbstractMetaClass::FunctionQueryOptions opts(AbstractMetaClass::OperatorOverloads); + QCOMPARE(classes.constFirst()->queryFunctions(opts).size(), 1); +} + QTEST_APPLESS_MAIN(TestAbstractMetaClass) diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h index 1d9f8d8f6..67e425128 100644 --- a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h @@ -52,6 +52,8 @@ private slots: void testObjectTypesMustNotHaveCopyConstructors(); void testIsPolymorphic(); void testClassTypedefedBaseClass(); + void testFreeOperators_data(); + void testFreeOperators(); }; #endif // TESTABSTRACTMETACLASS_H |