aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-10-23 10:55:35 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-10-28 13:38:34 +0000
commit8d2aa4264ce2eef7b465c1917ca027eea294747e (patch)
tree93e9b72309172a93b6483cd636f01a4c4c06480f
parenta26b04fae0f055d7590c9e7380afe7a5fb67c58e (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>
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp31
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp3
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp49
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h2
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